@abaplint/transpiler-cli 2.14.9 → 2.14.10

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.
Files changed (2) hide show
  1. package/build/bundle.js +1449 -91
  2. package/package.json +3 -3
package/build/bundle.js CHANGED
@@ -50951,7 +50951,7 @@ class SyntaxLogic {
50951
50951
  this.reg.getMSAGReferences().clear(this.object);
50952
50952
  if (this.object instanceof objects_1.Program && this.object.isInclude()) {
50953
50953
  // todo, show some kind of error?
50954
- return { issues: [], spaghetti: this.scope.pop(new position_1.Position(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER)) };
50954
+ return { issues: [], spaghetti: this.scope.pop(new position_1.Position(position_1.MAX_POSITION_VALUE, position_1.MAX_POSITION_VALUE)) };
50955
50955
  }
50956
50956
  this.traverseObject();
50957
50957
  // the FORM definitions are now all traversed, ie. their parameters are typed
@@ -50960,7 +50960,7 @@ class SyntaxLogic {
50960
50960
  }
50961
50961
  this.deferred = [];
50962
50962
  for (;;) {
50963
- const spaghetti = this.scope.pop(new position_1.Position(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER)); // pop built-in scopes
50963
+ const spaghetti = this.scope.pop(new position_1.Position(position_1.MAX_POSITION_VALUE, position_1.MAX_POSITION_VALUE)); // pop built-in scopes
50964
50964
  if (spaghetti.getTop().getIdentifier().stype === _scope_type_1.ScopeType.BuiltIn) {
50965
50965
  const result = { issues: this.issues, spaghetti };
50966
50966
  this.object.syntaxResult = result;
@@ -61667,6 +61667,7 @@ exports.Diagnostics = Diagnostics;
61667
61667
 
61668
61668
  Object.defineProperty(exports, "__esModule", ({ value: true }));
61669
61669
  exports.DumpScope = void 0;
61670
+ const position_1 = __webpack_require__(/*! ../position */ "./node_modules/@abaplint/core/build/src/position.js");
61670
61671
  const _scope_type_1 = __webpack_require__(/*! ../abap/5_syntax/_scope_type */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_scope_type.js");
61671
61672
  class DumpScope {
61672
61673
  static dump(spaghetti) {
@@ -61680,8 +61681,8 @@ class DumpScope {
61680
61681
  const sident = "&nbsp".repeat(indent * 2);
61681
61682
  let ret = sident + "<u>" + identifier.stype + ", <tt>" + identifier.sname + "</tt>";
61682
61683
  ret += ", (" + coverage.start.getRow() + ", " + coverage.start.getCol() + ")";
61683
- if (coverage.end.getRow() === Number.MAX_SAFE_INTEGER
61684
- && coverage.end.getCol() === Number.MAX_SAFE_INTEGER) {
61684
+ if (coverage.end.getRow() === position_1.MAX_POSITION_VALUE
61685
+ && coverage.end.getCol() === position_1.MAX_POSITION_VALUE) {
61685
61686
  ret += ", (max, max)";
61686
61687
  }
61687
61688
  else {
@@ -72051,7 +72052,10 @@ exports.WebReportingTemplate = WebReportingTemplate;
72051
72052
  "use strict";
72052
72053
 
72053
72054
  Object.defineProperty(exports, "__esModule", ({ value: true }));
72054
- exports.Position = void 0;
72055
+ exports.Position = exports.MAX_POSITION_VALUE = void 0;
72056
+ // largest value that is a Smi on all V8 builds, larger values (eg. Number.MAX_SAFE_INTEGER) make V8
72057
+ // store row/col of every Position as boxed heap numbers, costing ~32 bytes per Position
72058
+ exports.MAX_POSITION_VALUE = 2 ** 30 - 1;
72055
72059
  // first position is (1,1)
72056
72060
  class Position {
72057
72061
  constructor(row, col) {
@@ -72602,7 +72606,7 @@ class Registry {
72602
72606
  }
72603
72607
  static abaplintVersion() {
72604
72608
  // magic, see build script "version.js"
72605
- return "2.120.82";
72609
+ return "2.120.83";
72606
72610
  }
72607
72611
  getDDICReferences() {
72608
72612
  return this.ddicReferences;
@@ -73158,14 +73162,18 @@ var __importStar = (this && this.__importStar) || (function () {
73158
73162
  };
73159
73163
  })();
73160
73164
  Object.defineProperty(exports, "__esModule", ({ value: true }));
73161
- exports.SourceKind = exports.PredicateKind = exports.Procedure = exports.ObjectAnalysis = exports.Variable = exports.OccurrenceKind = void 0;
73165
+ exports.Hierarchy = exports.NUMERIC_FUNCTIONS = exports.SourceKind = exports.PredicateKind = exports.InitialDeclaration = exports.Procedure = exports.ObjectAnalysis = exports.Variable = exports.OccurrenceKind = void 0;
73162
73166
  exports.findStructures = findStructures;
73163
73167
  exports.expressionPath = expressionPath;
73164
73168
  exports.plainMove = plainMove;
73169
+ exports.plainCreateObject = plainCreateObject;
73165
73170
  exports.singleField = singleField;
73166
73171
  exports.singleTarget = singleTarget;
73167
73172
  exports.plainClear = plainClear;
73168
73173
  exports.plainData = plainData;
73174
+ exports.initialValue = initialValue;
73175
+ exports.initialDeclaration = initialDeclaration;
73176
+ exports.initialAtFirstReference = initialAtFirstReference;
73169
73177
  exports.isImmutable = isImmutable;
73170
73178
  exports.isReferenceType = isReferenceType;
73171
73179
  exports.identicalTypes = identicalTypes;
@@ -73176,6 +73184,10 @@ exports.isCondition = isCondition;
73176
73184
  exports.dataSource = dataSource;
73177
73185
  exports.targetType = targetType;
73178
73186
  exports.unchangedBetween = unchangedBetween;
73187
+ exports.formalParameter = formalParameter;
73188
+ exports.passedByValue = passedByValue;
73189
+ exports.literalType = literalType;
73190
+ exports.expressionType = expressionType;
73179
73191
  exports.mergeEdits = mergeEdits;
73180
73192
  exports.deleteRegion = deleteRegion;
73181
73193
  exports.deleteStatement = deleteStatement;
@@ -73195,6 +73207,10 @@ const syntax_1 = __webpack_require__(/*! ../abap/5_syntax/syntax */ "./node_modu
73195
73207
  const nodes_1 = __webpack_require__(/*! ../abap/nodes */ "./node_modules/@abaplint/core/build/src/abap/nodes/index.js");
73196
73208
  const _typed_identifier_1 = __webpack_require__(/*! ../abap/types/_typed_identifier */ "./node_modules/@abaplint/core/build/src/abap/types/_typed_identifier.js");
73197
73209
  const basic_1 = __webpack_require__(/*! ../abap/types/basic */ "./node_modules/@abaplint/core/build/src/abap/types/basic/index.js");
73210
+ const class_definition_1 = __webpack_require__(/*! ../abap/types/class_definition */ "./node_modules/@abaplint/core/build/src/abap/types/class_definition.js");
73211
+ const interface_definition_1 = __webpack_require__(/*! ../abap/types/interface_definition */ "./node_modules/@abaplint/core/build/src/abap/types/interface_definition.js");
73212
+ const class_1 = __webpack_require__(/*! ../objects/class */ "./node_modules/@abaplint/core/build/src/objects/class.js");
73213
+ const interface_1 = __webpack_require__(/*! ../objects/interface */ "./node_modules/@abaplint/core/build/src/objects/interface.js");
73198
73214
  const _abap_object_1 = __webpack_require__(/*! ../objects/_abap_object */ "./node_modules/@abaplint/core/build/src/objects/_abap_object.js");
73199
73215
  const edit_helper_1 = __webpack_require__(/*! ../edit_helper */ "./node_modules/@abaplint/core/build/src/edit_helper.js");
73200
73216
  const position_1 = __webpack_require__(/*! ../position */ "./node_modules/@abaplint/core/build/src/position.js");
@@ -73467,6 +73483,23 @@ class Procedure {
73467
73483
  }
73468
73484
  return between;
73469
73485
  }
73486
+ /** the next statement executed after a direct statement of a block, skipping declarations, which have no effect
73487
+ * where they are written; undefined if there is none in the block */
73488
+ nextExecuted(statement) {
73489
+ const block = this.block(statement);
73490
+ for (let index = this.index(statement) + 1; index < this.statements.length; index++) {
73491
+ const next = this.statements[index];
73492
+ const type = next.get();
73493
+ if (this.block(next) !== block) {
73494
+ return undefined;
73495
+ }
73496
+ else if (!(this.isSimple(next) && (type instanceof Statements.Data || type instanceof Statements.Type
73497
+ || type instanceof Statements.Constant || type instanceof Statements.FieldSymbol || type instanceof _statement_1.Comment))) {
73498
+ return next;
73499
+ }
73500
+ }
73501
+ return undefined;
73502
+ }
73470
73503
  /** a comment line or end of line comment between the two positions */
73471
73504
  hasComment(start, end) {
73472
73505
  if (this.comments === undefined) {
@@ -73477,6 +73510,18 @@ class Procedure {
73477
73510
  }
73478
73511
  return this.comments.some(c => !c.isBefore(start) && c.isBefore(end));
73479
73512
  }
73513
+ /** a structure of one of the types contains the statement */
73514
+ enclosedBy(statement, types) {
73515
+ let parent = this.parents.get(statement);
73516
+ while (parent !== undefined && parent !== this.structure) {
73517
+ const type = parent.get();
73518
+ if (types.some(t => type instanceof t)) {
73519
+ return true;
73520
+ }
73521
+ parent = this.parents.get(parent);
73522
+ }
73523
+ return false;
73524
+ }
73480
73525
  /** the IF structure of an IF or ELSEIF statement */
73481
73526
  ifStructure(statement) {
73482
73527
  let parent = this.parents.get(statement);
@@ -73549,6 +73594,23 @@ class Procedure {
73549
73594
  }
73550
73595
  return variable;
73551
73596
  }
73597
+ /** memory that only this procedure can reach: a local variable, or a pass-by-value or RETURNING parameter of
73598
+ * a method or form, with all references understood and its address never taken */
73599
+ private(identifier) {
73600
+ const variable = this.variable(identifier);
73601
+ if (variable === undefined || variable.complete === false || variable.escapes()
73602
+ || variable.identifier.getMeta().includes("static" /* IdentifierMeta.Static */)) {
73603
+ return undefined;
73604
+ }
73605
+ const meta = variable.identifier.getMeta();
73606
+ if (variable.isLocal()) {
73607
+ return variable;
73608
+ }
73609
+ // function module parameters can be global parameters of the function group
73610
+ const parameter = !(this.structure.get() instanceof Structures.FunctionModule)
73611
+ && (meta.includes("pass_by_value" /* IdentifierMeta.PassByValue */) || meta.includes("returning" /* IdentifierMeta.MethodReturning */));
73612
+ return parameter ? variable : undefined;
73613
+ }
73552
73614
  /** all local variables declared in the procedure */
73553
73615
  locals() {
73554
73616
  var _a;
@@ -73640,6 +73702,17 @@ class Procedure {
73640
73702
  }
73641
73703
  return statement.findFirstExpression(Expressions.NewObject) === undefined;
73642
73704
  }
73705
+ /** the method or builtin function called at the method name token */
73706
+ methodAt(token) {
73707
+ for (const reference of this.analysis.referencesAt(this.file.getFilename(), token)) {
73708
+ if ((reference.referenceType === _reference_1.ReferenceType.MethodReference
73709
+ || reference.referenceType === _reference_1.ReferenceType.BuiltinMethodReference)
73710
+ && reference.resolved !== undefined && "getParameters" in reference.resolved) {
73711
+ return reference.resolved;
73712
+ }
73713
+ }
73714
+ return undefined;
73715
+ }
73643
73716
  /** every data write in the statement goes to a local variable, a pass-by-value parameter or the
73644
73717
  * RETURNING parameter, ie. memory that no other data object, field symbol or reference can share */
73645
73718
  writesOnlyPrivate(statement) {
@@ -73810,6 +73883,31 @@ function plainMove(statement) {
73810
73883
  }
73811
73884
  return { target, source, operator };
73812
73885
  }
73886
+ /** CREATE OBJECT target [TYPE class] [EXPORTING parameters]. without chaining, pragmas, EXCEPTIONS or dynamic parts */
73887
+ function plainCreateObject(statement) {
73888
+ if (!(statement.get() instanceof Statements.CreateObject) || statement.getColon() !== undefined
73889
+ || statement.getPragmas().length > 0) {
73890
+ return undefined;
73891
+ }
73892
+ const children = statement.getChildren();
73893
+ const target = children[2];
73894
+ if (!(target instanceof nodes_1.ExpressionNode) || !(target.get() instanceof Expressions.Target)) {
73895
+ return undefined;
73896
+ }
73897
+ let type;
73898
+ // the last child is the period
73899
+ for (let i = 3; i < children.length - 1; i += 2) {
73900
+ const word = children[i] instanceof nodes_1.TokenNode ? children[i].getFirstToken().getStr().toUpperCase() : "";
73901
+ const value = children[i + 1];
73902
+ if (word === "TYPE" && value instanceof nodes_1.ExpressionNode && value.get() instanceof Expressions.ClassName) {
73903
+ type = value;
73904
+ }
73905
+ else if (!(word === "EXPORTING" && (value === null || value === void 0 ? void 0 : value.get()) instanceof Expressions.ParameterListS)) {
73906
+ return undefined;
73907
+ }
73908
+ }
73909
+ return { target, type };
73910
+ }
73813
73911
  /** the source is a single field, ie. one token */
73814
73912
  function singleField(source) {
73815
73913
  var _a;
@@ -73844,6 +73942,49 @@ function plainData(statement) {
73844
73942
  return statement.get() instanceof Statements.Data
73845
73943
  && ((_a = statement.findDirectExpression(Expressions.DataDefinition)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(Expressions.Value)) === undefined;
73846
73944
  }
73945
+ /** VALUE type( ) with an explicit type and an empty body, ie. the initial value of the type */
73946
+ function initialValue(source) {
73947
+ const tokens = source.getTokens();
73948
+ return tokens.length === 4 && tokens[0].getStr().toUpperCase() === "VALUE"
73949
+ && source.findDirectExpression(Expressions.TypeNameOrInfer) !== undefined
73950
+ && tokens[1].getStr() !== "#" && tokens[2].getStr() === "(" && tokens[3].getStr() === ")";
73951
+ }
73952
+ var InitialDeclaration;
73953
+ (function (InitialDeclaration) {
73954
+ /** DATA v TYPE ... without VALUE: v is initial when the procedure starts, the statement is not executed */
73955
+ InitialDeclaration["Data"] = "data";
73956
+ /** DATA(v) = VALUE t( ).: sets v to the initial value of t every time it is executed, also in each loop iteration */
73957
+ InitialDeclaration["Value"] = "value";
73958
+ })(InitialDeclaration || (exports.InitialDeclaration = InitialDeclaration = {}));
73959
+ /** how the declaration of a local variable makes it initial, undefined if it does not */
73960
+ function initialDeclaration(variable) {
73961
+ const declaration = variable.declaration;
73962
+ if (declaration === undefined) {
73963
+ return undefined;
73964
+ }
73965
+ else if (variable.inline === false) {
73966
+ return plainData(declaration) ? InitialDeclaration.Data : undefined;
73967
+ }
73968
+ const move = plainMove(declaration);
73969
+ return move !== undefined && move.operator === "=" && move.target.getChildren().length === 1
73970
+ && move.target.findDirectExpression(Expressions.InlineData) !== undefined && initialValue(move.source)
73971
+ ? InitialDeclaration.Value : undefined;
73972
+ }
73973
+ /** the variable is initial at the statement, which is its first reference after the declaration */
73974
+ function initialAtFirstReference(procedure, variable, statement) {
73975
+ const declaration = variable.declaration;
73976
+ const kind = initialDeclaration(variable);
73977
+ // neither the declaration nor the statement in a loop: nothing can change the variable before the statement
73978
+ const noLoop = procedure.loop(declaration) === undefined && procedure.loop(statement) === undefined;
73979
+ if (kind === InitialDeclaration.Data) {
73980
+ return noLoop;
73981
+ }
73982
+ else if (kind === InitialDeclaration.Value) {
73983
+ // executed before the statement in the same iteration
73984
+ return noLoop || (procedure.dominates(declaration, statement) && procedure.sameLoop(declaration, statement));
73985
+ }
73986
+ return false;
73987
+ }
73847
73988
  /** constants of the class or program, builtin constants like abap_true, and me: never change */
73848
73989
  function isImmutable(identifier) {
73849
73990
  if (!(identifier instanceof _typed_identifier_1.TypedIdentifier)) {
@@ -73971,7 +74112,7 @@ var SourceKind;
73971
74112
  (function (SourceKind) {
73972
74113
  /** constants and me, never change */
73973
74114
  SourceKind["Immutable"] = "immutable";
73974
- /** a local variable, or a component of it, changes only by references to the variable */
74115
+ /** a local variable or a pass-by-value parameter, or a component of it, changes only by references to the variable */
73975
74116
  SourceKind["Local"] = "local";
73976
74117
  /** attributes, parameters, globals, anything reached via a reference: may change by other code */
73977
74118
  SourceKind["Volatile"] = "volatile";
@@ -73980,6 +74121,7 @@ var SourceKind;
73980
74121
  * expressions, field symbols or dereferencing with ->* */
73981
74122
  function dataSource(procedure, source) {
73982
74123
  var _a;
74124
+ var _b;
73983
74125
  if (source.getChildren().length !== 1) {
73984
74126
  return undefined;
73985
74127
  }
@@ -74035,7 +74177,7 @@ function dataSource(procedure, source) {
74035
74177
  if (type === undefined || type.isGeneric() || (type instanceof basic_1.TableType && type.isWithHeader())) {
74036
74178
  return undefined;
74037
74179
  }
74038
- const local = next === 1 ? procedure.local(first) : undefined;
74180
+ const local = next === 1 ? (_b = procedure.local(first)) !== null && _b !== void 0 ? _b : procedure.private(first) : undefined;
74039
74181
  let kind = SourceKind.Volatile;
74040
74182
  if (deref === false && isImmutable(first)) {
74041
74183
  kind = SourceKind.Immutable;
@@ -74116,7 +74258,8 @@ function unchangedBetween(procedure, source, from, to, read) {
74116
74258
  const statement = procedure.statements[index];
74117
74259
  if (index === end && read !== undefined) {
74118
74260
  if (isReceiver(statement, read)
74119
- || (statement.get() instanceof Statements.Move && procedure.isCallFree(statement))) {
74261
+ || (statement.get() instanceof Statements.Move && procedure.isCallFree(statement))
74262
+ || passedByValue(procedure, statement, read)) {
74120
74263
  continue;
74121
74264
  }
74122
74265
  }
@@ -74142,6 +74285,229 @@ function isReceiver(statement, token) {
74142
74285
  && receiver.countTokens() === 1 && receiver.getFirstToken().getStart().equals(token.getStart())
74143
74286
  && ((_b = chain === null || chain === void 0 ? void 0 : chain.getChildren()[1]) === null || _b === void 0 ? void 0 : _b.getFirstToken().getStr()) === "->";
74144
74287
  }
74288
+ /** the token is the complete actual parameter for an importing parameter: m( v ), m( p = v ),
74289
+ * CALL METHOD m EXPORTING p = v, NEW c( p = v ) or CREATE OBJECT o EXPORTING p = v */
74290
+ function formalParameter(procedure, statement, token) {
74291
+ var _a, _b, _c;
74292
+ const path = expressionPath(statement, token);
74293
+ const index = path.findIndex(e => e.get() instanceof Expressions.Source && e.countTokens() === 1);
74294
+ const source = path[index];
74295
+ if (source === undefined || source.findDirectExpression(Expressions.FieldChain) === undefined
74296
+ || !source.getFirstToken().getStart().equals(token.getStart())) {
74297
+ return undefined;
74298
+ }
74299
+ const parent = index > 0 ? path[index - 1] : undefined;
74300
+ let name;
74301
+ let call;
74302
+ if ((parent === null || parent === void 0 ? void 0 : parent.get()) instanceof Expressions.MethodCallParam) {
74303
+ call = path[index - 2];
74304
+ }
74305
+ else if ((parent === null || parent === void 0 ? void 0 : parent.get()) instanceof Expressions.NewObject) {
74306
+ call = parent;
74307
+ }
74308
+ else if ((parent === null || parent === void 0 ? void 0 : parent.get()) instanceof Expressions.ParameterS && ((_a = path[index - 2]) === null || _a === void 0 ? void 0 : _a.get()) instanceof Expressions.ParameterListS) {
74309
+ name = (_b = parent.findDirectExpression(Expressions.ParameterName)) === null || _b === void 0 ? void 0 : _b.getFirstToken().getStr();
74310
+ const list = path[index - 3];
74311
+ const above = path[index - 4];
74312
+ if (list === undefined && statement.get() instanceof Statements.CreateObject) {
74313
+ call = statement;
74314
+ }
74315
+ else if ((list === null || list === void 0 ? void 0 : list.get()) instanceof Expressions.MethodCallParam) {
74316
+ call = above;
74317
+ }
74318
+ else if ((list === null || list === void 0 ? void 0 : list.get()) instanceof Expressions.NewObject) {
74319
+ call = list;
74320
+ }
74321
+ else if ((list === null || list === void 0 ? void 0 : list.get()) instanceof Expressions.MethodParameters && (above === null || above === void 0 ? void 0 : above.get()) instanceof Expressions.MethodCallParam) {
74322
+ call = path[index - 5];
74323
+ }
74324
+ else if ((list === null || list === void 0 ? void 0 : list.get()) instanceof Expressions.MethodParameters && (above === null || above === void 0 ? void 0 : above.get()) instanceof Expressions.MethodCallBody
74325
+ && index === 4 && statement.get() instanceof Statements.Call) {
74326
+ call = statement;
74327
+ }
74328
+ }
74329
+ if (call === undefined) {
74330
+ return undefined;
74331
+ }
74332
+ let method;
74333
+ if (call instanceof nodes_1.ExpressionNode && call.get() instanceof Expressions.MethodCall) {
74334
+ const methodName = (_c = call.findDirectExpression(Expressions.MethodName)) === null || _c === void 0 ? void 0 : _c.getFirstToken();
74335
+ method = methodName === undefined ? undefined : procedure.methodAt(methodName);
74336
+ }
74337
+ else if (call instanceof nodes_1.StatementNode && call.get() instanceof Statements.Call) {
74338
+ const methodSource = call.findDirectExpression(Expressions.MethodSource);
74339
+ method = methodSource === undefined ? undefined : procedure.methodAt(methodSource.getLastToken());
74340
+ }
74341
+ else if (!(call.get() instanceof Expressions.NewObject) && !(call.get() instanceof Statements.CreateObject)) {
74342
+ return undefined;
74343
+ }
74344
+ const parameters = method === null || method === void 0 ? void 0 : method.getParameters();
74345
+ const formal = name !== null && name !== void 0 ? name : parameters === null || parameters === void 0 ? void 0 : parameters.getDefaultImporting();
74346
+ const parameter = formal === undefined ? undefined
74347
+ : parameters === null || parameters === void 0 ? void 0 : parameters.getImporting().find(p => p.getName().toUpperCase() === formal.toUpperCase());
74348
+ return { call, method, parameter };
74349
+ }
74350
+ /** the token is the complete actual parameter for a pass-by-value importing parameter of the only call in the
74351
+ * statement, so the method gets a copy of the value when it is called, and nothing else runs before */
74352
+ function passedByValue(procedure, statement, token) {
74353
+ var _a, _b;
74354
+ if (!(statement.get() instanceof Statements.Call) && plainMove(statement) === undefined) {
74355
+ return false;
74356
+ }
74357
+ const formal = formalParameter(procedure, statement, token);
74358
+ if ((formal === null || formal === void 0 ? void 0 : formal.method) === undefined || ((_a = formal.parameter) === null || _a === void 0 ? void 0 : _a.getMeta().includes("pass_by_value" /* IdentifierMeta.PassByValue */)) !== true
74359
+ || statement.findFirstExpression(Expressions.NewObject) !== undefined) {
74360
+ return false;
74361
+ }
74362
+ for (const call of statement.findAllExpressionsRecursive(Expressions.MethodCall)) {
74363
+ const name = (_b = call.findDirectExpression(Expressions.MethodName)) === null || _b === void 0 ? void 0 : _b.getFirstToken();
74364
+ const builtin = name === undefined ? false : procedure.analysis.referencesAt(procedure.file.getFilename(), name)
74365
+ .some(r => r.referenceType === _reference_1.ReferenceType.BuiltinMethodReference);
74366
+ if (call !== formal.call && builtin === false) {
74367
+ return false;
74368
+ }
74369
+ }
74370
+ return true;
74371
+ }
74372
+ /** builtin functions whose result type follows the type of the argument */
74373
+ exports.NUMERIC_FUNCTIONS = new Set(["abs", "sign", "ceil", "floor", "trunc", "frac", "ipow", "nmax", "nmin",
74374
+ "round", "rescale"]);
74375
+ /** type of a literal: 'abc' is c LENGTH 3, `abc` is string, 12 is i */
74376
+ function literalType(constant) {
74377
+ var _a;
74378
+ var _b;
74379
+ if (constant.countTokens() !== 1) {
74380
+ return undefined;
74381
+ }
74382
+ const text = constant.getFirstToken().getStr();
74383
+ if (constant.findDirectExpression(Expressions.Integer) !== undefined) {
74384
+ const value = Number(text);
74385
+ return Number.isInteger(value) && Math.abs(value) <= 2147483647 ? basic_1.IntegerType.get() : undefined;
74386
+ }
74387
+ else if (text.startsWith("'") && text.endsWith("'") && text.length >= 2) {
74388
+ return new basic_1.CharacterType(Math.max(1, text.length - 2 - ((_b = (_a = text.match(/''/g)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0)));
74389
+ }
74390
+ else if (text.startsWith("`") && text.endsWith("`")) {
74391
+ return basic_1.StringType.get();
74392
+ }
74393
+ return undefined;
74394
+ }
74395
+ /** Static type of an expression whose type does not depend on where it is used: literals, string templates,
74396
+ * concatenations, xsdbool( ), boolc( ), data objects, method calls and attributes after method calls.
74397
+ * Undefined if not known, e.g. for constructor expressions, arithmetic or builtin functions typed by their argument */
74398
+ function expressionType(procedure, source) {
74399
+ var _a, _b, _c;
74400
+ const children = source.getChildren();
74401
+ const first = children[0];
74402
+ const keyword = first instanceof nodes_1.TokenNode ? first.getFirstToken().getStr().toUpperCase() : "";
74403
+ if (children.length === 4 && keyword === "XSDBOOL" && source.findDirectExpression(Expressions.Cond) !== undefined) {
74404
+ return new basic_1.CharacterType(1);
74405
+ }
74406
+ else if (children.length === 4 && keyword === "BOOLC" && source.findDirectExpression(Expressions.Cond) !== undefined) {
74407
+ return basic_1.StringType.get();
74408
+ }
74409
+ else if (children.length === 3 && children[1] instanceof nodes_1.TokenNode && children[1].getFirstToken().getStr() === "&&") {
74410
+ return basic_1.StringType.get();
74411
+ }
74412
+ else if (!(first instanceof nodes_1.ExpressionNode)) {
74413
+ return undefined;
74414
+ }
74415
+ if (children.length === 1 && first.get() instanceof Expressions.Constant) {
74416
+ return literalType(first);
74417
+ }
74418
+ else if (children.length === 1 && first.get() instanceof Expressions.StringTemplate) {
74419
+ return basic_1.StringType.get();
74420
+ }
74421
+ else if (children.length === 1 && first.get() instanceof Expressions.FieldChain) {
74422
+ return (_a = dataSource(procedure, source)) === null || _a === void 0 ? void 0 : _a.type;
74423
+ }
74424
+ else if (!(first.get() instanceof Expressions.MethodCallChain)) {
74425
+ return undefined;
74426
+ }
74427
+ // the last call of the chain, optionally followed by ->attribute
74428
+ let type;
74429
+ const last = first.getChildren()[first.getChildren().length - 1];
74430
+ if (last instanceof nodes_1.ExpressionNode && last.get() instanceof Expressions.MethodCall) {
74431
+ const name = (_b = last.findDirectExpression(Expressions.MethodName)) === null || _b === void 0 ? void 0 : _b.getFirstToken();
74432
+ const method = name === undefined ? undefined : procedure.methodAt(name);
74433
+ if (name === undefined || method === undefined || exports.NUMERIC_FUNCTIONS.has(name.getStr().toLowerCase())) {
74434
+ return undefined;
74435
+ }
74436
+ type = (_c = method.getParameters().getReturning()) === null || _c === void 0 ? void 0 : _c.getType();
74437
+ }
74438
+ else {
74439
+ return undefined;
74440
+ }
74441
+ if (children.length === 1) {
74442
+ return type;
74443
+ }
74444
+ const attribute = children[2];
74445
+ if (children.length === 3 && children[1].get() instanceof Expressions.Arrow && attribute instanceof nodes_1.ExpressionNode
74446
+ && attribute.get() instanceof Expressions.AttributeChain && attribute.getChildren().length === 1) {
74447
+ const identifier = procedure.identifierAt(attribute.getFirstToken());
74448
+ return identifier instanceof _typed_identifier_1.TypedIdentifier ? identifier.getType() : undefined;
74449
+ }
74450
+ return undefined;
74451
+ }
74452
+ ////////////////////////////////////////
74453
+ // classes and interfaces
74454
+ /** classes and interfaces by name: local ones in the scope of the procedure, global ones in the registry */
74455
+ class Hierarchy {
74456
+ constructor(reg, procedure) {
74457
+ this.reg = reg;
74458
+ this.procedure = procedure;
74459
+ }
74460
+ find(name) {
74461
+ var _a;
74462
+ const scope = this.procedure.analysis.spaghetti.lookupPosition(this.procedure.getStart(), this.procedure.file.getFilename());
74463
+ const local = (_a = scope === null || scope === void 0 ? void 0 : scope.findClassDefinition(name)) !== null && _a !== void 0 ? _a : scope === null || scope === void 0 ? void 0 : scope.findInterfaceDefinition(name);
74464
+ if (local !== undefined) {
74465
+ return local;
74466
+ }
74467
+ const clas = this.reg.getObject("CLAS", name);
74468
+ if (clas instanceof class_1.Class) {
74469
+ return clas.getDefinition();
74470
+ }
74471
+ const intf = this.reg.getObject("INTF", name);
74472
+ return intf instanceof interface_1.Interface ? intf.getDefinition() : undefined;
74473
+ }
74474
+ /** the class or interface definition of an object reference type, undefined if it is not known */
74475
+ definition(type) {
74476
+ if (!(type instanceof basic_1.ObjectReferenceType)) {
74477
+ return undefined;
74478
+ }
74479
+ const identifier = type.getIdentifier();
74480
+ const found = identifier instanceof class_definition_1.ClassDefinition || identifier instanceof interface_definition_1.InterfaceDefinition
74481
+ ? identifier : this.find(identifier.getName());
74482
+ return found instanceof class_definition_1.ClassDefinition || found instanceof interface_definition_1.InterfaceDefinition ? found : undefined;
74483
+ }
74484
+ /** sub is sup, or inherits from it or implements it; false if a definition on the way is not known */
74485
+ isSubtype(sub, sup) {
74486
+ const target = sup.toUpperCase();
74487
+ const visited = new Set();
74488
+ const stack = [sub.toUpperCase()];
74489
+ while (stack.length > 0) {
74490
+ const name = stack.pop();
74491
+ if (name === target) {
74492
+ return true;
74493
+ }
74494
+ else if (visited.has(name)) {
74495
+ continue;
74496
+ }
74497
+ visited.add(name);
74498
+ const definition = this.find(name);
74499
+ const superClass = definition === null || definition === void 0 ? void 0 : definition.getSuperClass();
74500
+ if (superClass !== undefined) {
74501
+ stack.push(superClass.toUpperCase());
74502
+ }
74503
+ for (const implementing of (definition === null || definition === void 0 ? void 0 : definition.getImplementing()) || []) {
74504
+ stack.push(implementing.name.toUpperCase());
74505
+ }
74506
+ }
74507
+ return false;
74508
+ }
74509
+ }
74510
+ exports.Hierarchy = Hierarchy;
74145
74511
  ////////////////////////////////////////
74146
74512
  // edits
74147
74513
  const rowCache = new WeakMap();
@@ -76897,14 +77263,7 @@ ENDIF.`,
76897
77263
  return false;
76898
77264
  }
76899
77265
  const constant = (0, _procedure_analysis_1.characterConstant)(procedure, move.source);
76900
- return (constant !== undefined && (0, _procedure_analysis_1.sameCharacters)(constant, "")) || this.initialValue(move.source);
76901
- }
76902
- /** VALUE type( ) */
76903
- initialValue(source) {
76904
- const tokens = source.getTokens();
76905
- return tokens.length === 4 && tokens[0].getStr().toUpperCase() === "VALUE"
76906
- && source.findDirectExpression(Expressions.TypeNameOrInfer) !== undefined
76907
- && tokens[1].getStr() !== "#" && tokens[2].getStr() === "(" && tokens[3].getStr() === ")";
77266
+ return (constant !== undefined && (0, _procedure_analysis_1.sameCharacters)(constant, "")) || (0, _procedure_analysis_1.initialValue)(move.source);
76908
77267
  }
76909
77268
  }
76910
77269
  exports.BooleanFlagViaIf = BooleanFlagViaIf;
@@ -77289,6 +77648,205 @@ exports.CallTransactionAuthorityCheck = CallTransactionAuthorityCheck;
77289
77648
 
77290
77649
  /***/ },
77291
77650
 
77651
+ /***/ "./node_modules/@abaplint/core/build/src/rules/cast_via_generic_reference.js"
77652
+ /*!***********************************************************************************!*\
77653
+ !*** ./node_modules/@abaplint/core/build/src/rules/cast_via_generic_reference.js ***!
77654
+ \***********************************************************************************/
77655
+ (__unused_webpack_module, exports, __webpack_require__) {
77656
+
77657
+ "use strict";
77658
+
77659
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
77660
+ if (k2 === undefined) k2 = k;
77661
+ var desc = Object.getOwnPropertyDescriptor(m, k);
77662
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
77663
+ desc = { enumerable: true, get: function() { return m[k]; } };
77664
+ }
77665
+ Object.defineProperty(o, k2, desc);
77666
+ }) : (function(o, m, k, k2) {
77667
+ if (k2 === undefined) k2 = k;
77668
+ o[k2] = m[k];
77669
+ }));
77670
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
77671
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
77672
+ }) : function(o, v) {
77673
+ o["default"] = v;
77674
+ });
77675
+ var __importStar = (this && this.__importStar) || (function () {
77676
+ var ownKeys = function(o) {
77677
+ ownKeys = Object.getOwnPropertyNames || function (o) {
77678
+ var ar = [];
77679
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
77680
+ return ar;
77681
+ };
77682
+ return ownKeys(o);
77683
+ };
77684
+ return function (mod) {
77685
+ if (mod && mod.__esModule) return mod;
77686
+ var result = {};
77687
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
77688
+ __setModuleDefault(result, mod);
77689
+ return result;
77690
+ };
77691
+ })();
77692
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
77693
+ exports.CastViaGenericReference = exports.CastViaGenericReferenceConf = void 0;
77694
+ const Expressions = __importStar(__webpack_require__(/*! ../abap/2_statements/expressions */ "./node_modules/@abaplint/core/build/src/abap/2_statements/expressions/index.js"));
77695
+ const basic_1 = __webpack_require__(/*! ../abap/types/basic */ "./node_modules/@abaplint/core/build/src/abap/types/basic/index.js");
77696
+ const interface_definition_1 = __webpack_require__(/*! ../abap/types/interface_definition */ "./node_modules/@abaplint/core/build/src/abap/types/interface_definition.js");
77697
+ const edit_helper_1 = __webpack_require__(/*! ../edit_helper */ "./node_modules/@abaplint/core/build/src/edit_helper.js");
77698
+ const issue_1 = __webpack_require__(/*! ../issue */ "./node_modules/@abaplint/core/build/src/issue.js");
77699
+ const _basic_rule_config_1 = __webpack_require__(/*! ./_basic_rule_config */ "./node_modules/@abaplint/core/build/src/rules/_basic_rule_config.js");
77700
+ const _irule_1 = __webpack_require__(/*! ./_irule */ "./node_modules/@abaplint/core/build/src/rules/_irule.js");
77701
+ const _procedure_analysis_1 = __webpack_require__(/*! ./_procedure_analysis */ "./node_modules/@abaplint/core/build/src/rules/_procedure_analysis.js");
77702
+ class CastViaGenericReferenceConf extends _basic_rule_config_1.BasicRuleConfig {
77703
+ }
77704
+ exports.CastViaGenericReferenceConf = CastViaGenericReferenceConf;
77705
+ class CastViaGenericReference {
77706
+ constructor() {
77707
+ this.conf = new CastViaGenericReferenceConf();
77708
+ }
77709
+ getMetadata() {
77710
+ return {
77711
+ key: "cast_via_generic_reference",
77712
+ title: "Cast via generic reference",
77713
+ shortDescription: `Finds a down cast from a local reference variable that only holds a copy of another reference`,
77714
+ extendedInformation: `Reports DATA o TYPE REF TO object. o = r. ... x ?= o. when all of the following hold:
77715
+
77716
+ * o is a local variable of a METHOD, FORM or FUNCTION, declared with DATA without VALUE and not chained,
77717
+ typed REF TO object, or REF TO a class or interface that is the static type of r or a supertype of it
77718
+ * o has exactly one write, o = r., exactly one read, the complete source of x ?= o., and its address is never
77719
+ taken. x is not an inline declaration
77720
+ * r is a plain object reference: a variable, or a chain of attributes and components without calls,
77721
+ offsets, table expressions, field symbols or ->*
77722
+ * r keeps its value from o = r. until the read, as in unnecessary_copy: for a local r, no write to it and its
77723
+ address is never taken; for other r, in addition no method, function or form call and no write except to
77724
+ local variables, by value parameters and RETURNING in between
77725
+ * o = r. is executed before x ?= o. on every path: the read is in the same block or a nested block, and both
77726
+ are in the same loop
77727
+ * x ?= r. is valid: x or r is typed REF TO object or an interface, or the class of x and the class of r
77728
+ are in the same inheritance line
77729
+
77730
+ The object is the same, so the cast succeeds or raises cx_sy_move_cast_error the same way.
77731
+ try_cast_empty_catch can then replace a TRY around the cast.
77732
+ Objects with syntax errors, macros or unknown statements are skipped, as are procedures with dynamic code,
77733
+ INCLUDE, test seams, enhancements or native SQL.
77734
+
77735
+ The quick fix deletes the declaration and o = r., and casts r directly. No release requirement.`,
77736
+ tags: [_irule_1.RuleTag.Quickfix],
77737
+ badExample: `DATA lo_object TYPE REF TO object.
77738
+ lo_object = io_node.
77739
+ TRY.
77740
+ lo_leaf ?= lo_object.
77741
+ CATCH cx_sy_move_cast_error.
77742
+ ENDTRY.`,
77743
+ goodExample: `TRY.
77744
+ lo_leaf ?= io_node.
77745
+ CATCH cx_sy_move_cast_error.
77746
+ ENDTRY.`,
77747
+ };
77748
+ }
77749
+ getConfig() {
77750
+ return this.conf;
77751
+ }
77752
+ setConfig(conf) {
77753
+ this.conf = conf;
77754
+ }
77755
+ initialize(reg) {
77756
+ this.reg = reg;
77757
+ return this;
77758
+ }
77759
+ run(obj) {
77760
+ var _a, _b;
77761
+ const analysis = _procedure_analysis_1.ObjectAnalysis.run(this.reg, obj);
77762
+ if (analysis === undefined) {
77763
+ return [];
77764
+ }
77765
+ const issues = [];
77766
+ for (const procedure of analysis.procedures) {
77767
+ if (procedure.supported === false) {
77768
+ continue;
77769
+ }
77770
+ const hierarchy = new _procedure_analysis_1.Hierarchy(this.reg, procedure);
77771
+ for (const variable of procedure.locals()) {
77772
+ const declaration = variable.declaration;
77773
+ const type = variable.identifier.getType();
77774
+ const writes = variable.writes();
77775
+ const reads = variable.reads();
77776
+ if (variable.inline || !(0, _procedure_analysis_1.plainData)(declaration) || declaration.getColon() !== undefined || variable.escapes()
77777
+ || !this.isObjectReference(type) || writes.length !== 1 || reads.length !== 1) {
77778
+ continue;
77779
+ }
77780
+ const write = writes[0];
77781
+ const read = reads[0];
77782
+ const assign = (0, _procedure_analysis_1.plainMove)(write.statement);
77783
+ const cast = (0, _procedure_analysis_1.plainMove)(read.statement);
77784
+ if (assign === undefined || cast === undefined || assign.operator !== "=" || cast.operator !== "?="
77785
+ || ((_a = (0, _procedure_analysis_1.singleTarget)(assign.target)) === null || _a === void 0 ? void 0 : _a.getStart().equals(write.token.getStart())) !== true
77786
+ || ((_b = (0, _procedure_analysis_1.singleField)(cast.source)) === null || _b === void 0 ? void 0 : _b.getStart().equals(read.token.getStart())) !== true
77787
+ || cast.target.findFirstExpression(Expressions.InlineData) !== undefined
77788
+ || !procedure.dominates(write.statement, read.statement)
77789
+ || !procedure.sameLoop(write.statement, read.statement)) {
77790
+ continue;
77791
+ }
77792
+ const source = (0, _procedure_analysis_1.dataSource)(procedure, assign.source);
77793
+ const target = (0, _procedure_analysis_1.targetType)(procedure, cast.target);
77794
+ if (source === undefined || source.base === variable || target === undefined
77795
+ || !this.isObjectReference(source.type)
77796
+ || !this.isSupertype(hierarchy, type, source.type)
77797
+ || !this.castable(hierarchy, source.type, target)
77798
+ || !(0, _procedure_analysis_1.unchangedBetween)(procedure, source, write.statement, read.statement, read.token)) {
77799
+ continue;
77800
+ }
77801
+ const fix = (0, _procedure_analysis_1.mergeEdits)(procedure.file, [
77802
+ (0, _procedure_analysis_1.deleteStatement)(procedure.file, declaration),
77803
+ (0, _procedure_analysis_1.deleteStatement)(procedure.file, write.statement),
77804
+ edit_helper_1.EditHelper.replaceToken(procedure.file, read.token, source.text)
77805
+ ]);
77806
+ const message = "Cast " + source.text + " directly instead of via " + variable.identifier.getName();
77807
+ issues.push(issue_1.Issue.atStatement(procedure.file, read.statement, message, this.getMetadata().key, this.conf.severity, fix));
77808
+ }
77809
+ }
77810
+ return issues;
77811
+ }
77812
+ isObjectReference(type) {
77813
+ return type instanceof basic_1.GenericObjectReferenceType || type instanceof basic_1.ObjectReferenceType;
77814
+ }
77815
+ /** every reference of type sub can be assigned to type sup without a cast */
77816
+ isSupertype(hierarchy, sup, sub) {
77817
+ if (sup instanceof basic_1.GenericObjectReferenceType) {
77818
+ return true;
77819
+ }
77820
+ else if (!(sup instanceof basic_1.ObjectReferenceType) || !(sub instanceof basic_1.ObjectReferenceType)) {
77821
+ return false;
77822
+ }
77823
+ return hierarchy.isSubtype(sub.getIdentifierName(), sup.getIdentifierName());
77824
+ }
77825
+ /** target ?= source. is statically valid */
77826
+ castable(hierarchy, source, target) {
77827
+ if (source instanceof basic_1.GenericObjectReferenceType || target instanceof basic_1.GenericObjectReferenceType) {
77828
+ return true;
77829
+ }
77830
+ else if (!(source instanceof basic_1.ObjectReferenceType) || !(target instanceof basic_1.ObjectReferenceType)) {
77831
+ return false;
77832
+ }
77833
+ const sourceDefinition = hierarchy.definition(source);
77834
+ const targetDefinition = hierarchy.definition(target);
77835
+ if (sourceDefinition === undefined || targetDefinition === undefined) {
77836
+ return false;
77837
+ }
77838
+ else if (sourceDefinition instanceof interface_definition_1.InterfaceDefinition || targetDefinition instanceof interface_definition_1.InterfaceDefinition) {
77839
+ return true;
77840
+ }
77841
+ return hierarchy.isSubtype(source.getIdentifierName(), target.getIdentifierName())
77842
+ || hierarchy.isSubtype(target.getIdentifierName(), source.getIdentifierName());
77843
+ }
77844
+ }
77845
+ exports.CastViaGenericReference = CastViaGenericReference;
77846
+ //# sourceMappingURL=cast_via_generic_reference.js.map
77847
+
77848
+ /***/ },
77849
+
77292
77850
  /***/ "./node_modules/@abaplint/core/build/src/rules/catch_and_raise.js"
77293
77851
  /*!************************************************************************!*\
77294
77852
  !*** ./node_modules/@abaplint/core/build/src/rules/catch_and_raise.js ***!
@@ -80210,10 +80768,13 @@ class ClearAfterDeclaration {
80210
80768
  shortDescription: `Finds CLEAR of a local variable that is still initial since its declaration`,
80211
80769
  extendedInformation: `Reports CLEAR x. when all of the following hold:
80212
80770
 
80213
- * x is a local variable of a METHOD, FORM or FUNCTION, declared with DATA x TYPE/LIKE ... without VALUE
80771
+ * x is a local variable of a METHOD, FORM or FUNCTION, declared with DATA x TYPE/LIKE ... without VALUE,
80772
+ or with DATA(x) = VALUE t( ). with an explicit type and nothing in the parentheses
80214
80773
  * the CLEAR is the first reference to x after the declaration, it has no WITH addition
80215
- * neither the DATA nor the CLEAR is inside a LOOP, DO, WHILE, SELECT loop, PROVIDE, or a TRY block with
80216
- RETRY, RESUME or CATCH BEFORE UNWIND. DATA is not executed per iteration, so a CLEAR in a loop can be needed
80774
+ * neither the declaration nor the CLEAR is inside a LOOP, DO, WHILE, SELECT loop, PROVIDE, or a TRY block with
80775
+ RETRY, RESUME or CATCH BEFORE UNWIND. DATA is not executed per iteration, so a CLEAR in a loop can be needed.
80776
+ DATA(x) = VALUE t( ). is executed per iteration, it may also be in the same loop as the CLEAR, if the CLEAR is
80777
+ in the block of the declaration or a nested block
80217
80778
 
80218
80779
  Any later use of x does not matter, the CLEAR can only be reached with x still initial.
80219
80780
  Objects with syntax errors, macros or unknown statements are skipped, as are procedures with dynamic code,
@@ -80248,13 +80809,11 @@ CLEAR lv_count.`,
80248
80809
  continue;
80249
80810
  }
80250
80811
  for (const variable of procedure.locals()) {
80251
- const declaration = variable.declaration;
80252
80812
  const first = variable.occurrences[0];
80253
- if (variable.inline || !(0, _procedure_analysis_1.plainData)(declaration) || first === undefined
80813
+ if (first === undefined
80254
80814
  || first.kind !== _procedure_analysis_1.OccurrenceKind.Write
80255
80815
  || ((_a = (0, _procedure_analysis_1.plainClear)(first.statement)) === null || _a === void 0 ? void 0 : _a.getStart().equals(first.token.getStart())) !== true
80256
- || procedure.loop(declaration) !== undefined
80257
- || procedure.loop(first.statement) !== undefined) {
80816
+ || !(0, _procedure_analysis_1.initialAtFirstReference)(procedure, variable, first.statement)) {
80258
80817
  continue;
80259
80818
  }
80260
80819
  const fix = (0, _procedure_analysis_1.deleteStatement)(procedure.file, first.statement);
@@ -80269,6 +80828,195 @@ exports.ClearAfterDeclaration = ClearAfterDeclaration;
80269
80828
 
80270
80829
  /***/ },
80271
80830
 
80831
+ /***/ "./node_modules/@abaplint/core/build/src/rules/clear_before_overwrite.js"
80832
+ /*!*******************************************************************************!*\
80833
+ !*** ./node_modules/@abaplint/core/build/src/rules/clear_before_overwrite.js ***!
80834
+ \*******************************************************************************/
80835
+ (__unused_webpack_module, exports, __webpack_require__) {
80836
+
80837
+ "use strict";
80838
+
80839
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
80840
+ if (k2 === undefined) k2 = k;
80841
+ var desc = Object.getOwnPropertyDescriptor(m, k);
80842
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
80843
+ desc = { enumerable: true, get: function() { return m[k]; } };
80844
+ }
80845
+ Object.defineProperty(o, k2, desc);
80846
+ }) : (function(o, m, k, k2) {
80847
+ if (k2 === undefined) k2 = k;
80848
+ o[k2] = m[k];
80849
+ }));
80850
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
80851
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
80852
+ }) : function(o, v) {
80853
+ o["default"] = v;
80854
+ });
80855
+ var __importStar = (this && this.__importStar) || (function () {
80856
+ var ownKeys = function(o) {
80857
+ ownKeys = Object.getOwnPropertyNames || function (o) {
80858
+ var ar = [];
80859
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
80860
+ return ar;
80861
+ };
80862
+ return ownKeys(o);
80863
+ };
80864
+ return function (mod) {
80865
+ if (mod && mod.__esModule) return mod;
80866
+ var result = {};
80867
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
80868
+ __setModuleDefault(result, mod);
80869
+ return result;
80870
+ };
80871
+ })();
80872
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
80873
+ exports.ClearBeforeOverwrite = exports.ClearBeforeOverwriteConf = void 0;
80874
+ const Expressions = __importStar(__webpack_require__(/*! ../abap/2_statements/expressions */ "./node_modules/@abaplint/core/build/src/abap/2_statements/expressions/index.js"));
80875
+ const Structures = __importStar(__webpack_require__(/*! ../abap/3_structures/structures */ "./node_modules/@abaplint/core/build/src/abap/3_structures/structures/index.js"));
80876
+ const basic_1 = __webpack_require__(/*! ../abap/types/basic */ "./node_modules/@abaplint/core/build/src/abap/types/basic/index.js");
80877
+ const _typed_identifier_1 = __webpack_require__(/*! ../abap/types/_typed_identifier */ "./node_modules/@abaplint/core/build/src/abap/types/_typed_identifier.js");
80878
+ const issue_1 = __webpack_require__(/*! ../issue */ "./node_modules/@abaplint/core/build/src/issue.js");
80879
+ const _basic_rule_config_1 = __webpack_require__(/*! ./_basic_rule_config */ "./node_modules/@abaplint/core/build/src/rules/_basic_rule_config.js");
80880
+ const _irule_1 = __webpack_require__(/*! ./_irule */ "./node_modules/@abaplint/core/build/src/rules/_irule.js");
80881
+ const _procedure_analysis_1 = __webpack_require__(/*! ./_procedure_analysis */ "./node_modules/@abaplint/core/build/src/rules/_procedure_analysis.js");
80882
+ class ClearBeforeOverwriteConf extends _basic_rule_config_1.BasicRuleConfig {
80883
+ }
80884
+ exports.ClearBeforeOverwriteConf = ClearBeforeOverwriteConf;
80885
+ class ClearBeforeOverwrite {
80886
+ constructor() {
80887
+ this.conf = new ClearBeforeOverwriteConf();
80888
+ }
80889
+ getMetadata() {
80890
+ return {
80891
+ key: "clear_before_overwrite",
80892
+ title: "CLEAR before overwrite",
80893
+ shortDescription: `Finds CLEAR of a local variable whose next use overwrites it completely`,
80894
+ extendedInformation: `Reports CLEAR v. when all of the following hold:
80895
+
80896
+ * v is a local variable of a METHOD, FORM or FUNCTION, its address is never taken: no field symbol,
80897
+ REF #( ), GET REFERENCE, and it is not passed to a parameter by reference
80898
+ * the CLEAR is not chained and has no WITH addition
80899
+ * the next reference to v is a full overwrite in the same block: v = <expression>., including v = NEW ...,
80900
+ or CREATE OBJECT v. with optional TYPE and EXPORTING, where nothing else in the statement references v.
80901
+ Not ?=, READ TABLE INTO, offset/length or component writes, CREATE OBJECT with EXCEPTIONS, or other
80902
+ statements that can leave v unchanged
80903
+ * every statement between the CLEAR and the overwrite is a direct statement of the block, not a nested structure,
80904
+ and does not reference v
80905
+ * v is not a table with header line
80906
+ * if a TRY or CATCH SYSTEM-EXCEPTIONS encloses the CLEAR, nothing from the CLEAR to the overwrite can raise
80907
+ an exception, a CATCH could see the old value: the overwrite directly follows the CLEAR and assigns a variable
80908
+ or a literal of identical type, or a reference variable
80909
+
80910
+ Unlike clear_after_declaration, the CLEAR can be in a loop.
80911
+ Objects with syntax errors, macros or unknown statements are skipped, as are procedures with dynamic code,
80912
+ INCLUDE, test seams, enhancements or native SQL.
80913
+
80914
+ The quick fix deletes the CLEAR. No release requirement.`,
80915
+ tags: [_irule_1.RuleTag.Quickfix],
80916
+ badExample: `CLEAR lv_name.
80917
+ lv_name = iv_name.`,
80918
+ goodExample: `lv_name = iv_name.`,
80919
+ };
80920
+ }
80921
+ getConfig() {
80922
+ return this.conf;
80923
+ }
80924
+ setConfig(conf) {
80925
+ this.conf = conf;
80926
+ }
80927
+ initialize(reg) {
80928
+ this.reg = reg;
80929
+ return this;
80930
+ }
80931
+ run(obj) {
80932
+ var _a;
80933
+ const analysis = _procedure_analysis_1.ObjectAnalysis.run(this.reg, obj);
80934
+ if (analysis === undefined) {
80935
+ return [];
80936
+ }
80937
+ const issues = [];
80938
+ for (const procedure of analysis.procedures) {
80939
+ if (procedure.supported === false) {
80940
+ continue;
80941
+ }
80942
+ for (const variable of procedure.locals()) {
80943
+ const type = variable.identifier.getType();
80944
+ if (variable.escapes() || (type instanceof basic_1.TableType && type.isWithHeader())
80945
+ || this.passedByReference(procedure, variable)) {
80946
+ continue;
80947
+ }
80948
+ const occurrences = variable.occurrences;
80949
+ for (let i = 0; i < occurrences.length - 1; i++) {
80950
+ const clear = occurrences[i];
80951
+ const next = occurrences[i + 1];
80952
+ if (((_a = (0, _procedure_analysis_1.plainClear)(clear.statement)) === null || _a === void 0 ? void 0 : _a.getStart().equals(clear.token.getStart())) !== true
80953
+ || clear.statement.getColon() !== undefined
80954
+ || occurrences.some(o => o !== next && o.statement === next.statement)
80955
+ || !this.overwrites(next)) {
80956
+ continue;
80957
+ }
80958
+ const between = procedure.straightBetween(clear.statement, next.statement);
80959
+ if (between === undefined
80960
+ || (procedure.enclosedBy(clear.statement, [Structures.Try, Structures.CatchSystemExceptions])
80961
+ && (between.length > 0 || !this.cannotRaise(procedure, variable, next.statement)))) {
80962
+ continue;
80963
+ }
80964
+ const message = "CLEAR of " + variable.identifier.getName() + ", which is overwritten before it is read";
80965
+ issues.push(issue_1.Issue.atStatement(procedure.file, clear.statement, message, this.getMetadata().key, this.conf.severity, (0, _procedure_analysis_1.deleteStatement)(procedure.file, clear.statement)));
80966
+ }
80967
+ }
80968
+ }
80969
+ return issues;
80970
+ }
80971
+ /** passed to a method, NEW, CREATE OBJECT or RAISE EXCEPTION, other than to a pass-by-value parameter;
80972
+ * the called code could keep a reference to the parameter */
80973
+ passedByReference(procedure, variable) {
80974
+ var _a;
80975
+ for (const read of variable.reads()) {
80976
+ const path = (0, _procedure_analysis_1.expressionPath)(read.statement, read.token);
80977
+ if (!path.some(e => e.get() instanceof Expressions.MethodCallParam || e.get() instanceof Expressions.ParameterListS
80978
+ || e.get() instanceof Expressions.MethodParameters)) {
80979
+ continue;
80980
+ }
80981
+ const formal = (0, _procedure_analysis_1.formalParameter)(procedure, read.statement, read.token);
80982
+ if (((_a = formal === null || formal === void 0 ? void 0 : formal.parameter) === null || _a === void 0 ? void 0 : _a.getMeta().includes("pass_by_value" /* IdentifierMeta.PassByValue */)) !== true) {
80983
+ return true;
80984
+ }
80985
+ }
80986
+ return false;
80987
+ }
80988
+ /** v = <expression>. or CREATE OBJECT v [TYPE c] [EXPORTING ...]. */
80989
+ overwrites(occurrence) {
80990
+ var _a, _b;
80991
+ const statement = occurrence.statement;
80992
+ const move = (0, _procedure_analysis_1.plainMove)(statement);
80993
+ if (move !== undefined) {
80994
+ return move.operator === "=" && ((_a = (0, _procedure_analysis_1.singleTarget)(move.target)) === null || _a === void 0 ? void 0 : _a.getStart().equals(occurrence.token.getStart())) === true;
80995
+ }
80996
+ const create = (0, _procedure_analysis_1.plainCreateObject)(statement);
80997
+ return create !== undefined && ((_b = (0, _procedure_analysis_1.singleTarget)(create.target)) === null || _b === void 0 ? void 0 : _b.getStart().equals(occurrence.token.getStart())) === true;
80998
+ }
80999
+ /** v = w. or v = literal. without conversion */
81000
+ cannotRaise(procedure, variable, statement) {
81001
+ const move = (0, _procedure_analysis_1.plainMove)(statement);
81002
+ if (move === undefined) {
81003
+ return false;
81004
+ }
81005
+ const type = variable.identifier.getType();
81006
+ const field = (0, _procedure_analysis_1.singleField)(move.source);
81007
+ const constant = move.source.getChildren().length === 1 ? move.source.findDirectExpression(Expressions.Constant) : undefined;
81008
+ const identifier = field === undefined ? undefined : procedure.identifierAt(field);
81009
+ const source = identifier instanceof _typed_identifier_1.TypedIdentifier ? identifier.getType()
81010
+ : constant === undefined ? undefined : (0, _procedure_analysis_1.literalType)(constant);
81011
+ return source !== undefined
81012
+ && ((0, _procedure_analysis_1.identicalTypes)(source, type) || ((0, _procedure_analysis_1.isReferenceType)(source) && (0, _procedure_analysis_1.isReferenceType)(type) && field !== undefined));
81013
+ }
81014
+ }
81015
+ exports.ClearBeforeOverwrite = ClearBeforeOverwrite;
81016
+ //# sourceMappingURL=clear_before_overwrite.js.map
81017
+
81018
+ /***/ },
81019
+
80272
81020
  /***/ "./node_modules/@abaplint/core/build/src/rules/clear_exporting_parameters.js"
80273
81021
  /*!***********************************************************************************!*\
80274
81022
  !*** ./node_modules/@abaplint/core/build/src/rules/clear_exporting_parameters.js ***!
@@ -81108,8 +81856,9 @@ c = <constant>, c <> <constant>, EQ, NE, c IS INITIAL, c IS NOT INITIAL, optiona
81108
81856
  The declaration must be executed before the condition on every path: the IF is in the block of the
81109
81857
  declaration or a nested block. The quick fix deletes the declaration.
81110
81858
 
81111
- 2. DATA v TYPE ... without VALUE, where v is never changed, except by CLEAR v., and its address is never taken,
81112
- in a condition v IS [NOT] INITIAL or v IS [NOT] BOUND. The declaration is kept.
81859
+ 2. DATA v TYPE ... without VALUE, or DATA(v) = VALUE t( ). with an explicit type and nothing in the parentheses,
81860
+ where v is never changed, except by CLEAR v., and its address is never taken, in a condition v IS [NOT] INITIAL
81861
+ or v IS [NOT] BOUND. The declaration is kept.
81113
81862
 
81114
81863
  The quick fix removes the IF branch that is never taken, or keeps the branch that is always taken and
81115
81864
  removes the rest, also in ELSEIF chains: a first IF branch that is never taken turns the next ELSEIF into IF,
@@ -81150,12 +81899,12 @@ ENDIF.`,
81150
81899
  }
81151
81900
  const candidates = [];
81152
81901
  for (const variable of procedure.locals()) {
81153
- if (variable.inline) {
81154
- this.constantVariable(procedure, variable, candidates);
81155
- }
81156
- else {
81902
+ if ((0, _procedure_analysis_1.initialDeclaration)(variable) !== undefined) {
81157
81903
  this.initialVariable(procedure, variable, candidates);
81158
81904
  }
81905
+ else if (variable.inline) {
81906
+ this.constantVariable(procedure, variable, candidates);
81907
+ }
81159
81908
  }
81160
81909
  issues.push(...(0, _procedure_analysis_1.foldIssues)(procedure.file, candidates, this.getMetadata().key, this.conf.severity));
81161
81910
  }
@@ -81191,9 +81940,9 @@ ENDIF.`,
81191
81940
  fix: (0, _procedure_analysis_1.mergeEdits)(procedure.file, [fold, (0, _procedure_analysis_1.deleteStatement)(procedure.file, declaration)]),
81192
81941
  });
81193
81942
  }
81194
- /** DATA v TYPE ... never changed: IF v IS INITIAL / IS BOUND */
81943
+ /** DATA v TYPE ... or DATA(v) = VALUE t( ). never changed: IF v IS INITIAL / IS BOUND */
81195
81944
  initialVariable(procedure, variable, candidates) {
81196
- if (!(0, _procedure_analysis_1.plainData)(variable.declaration) || variable.escapes()
81945
+ if (variable.escapes()
81197
81946
  || variable.writes().some(w => { var _a; return ((_a = (0, _procedure_analysis_1.plainClear)(w.statement)) === null || _a === void 0 ? void 0 : _a.getStart().equals(w.token.getStart())) !== true; })) {
81198
81947
  return;
81199
81948
  }
@@ -81456,6 +82205,8 @@ var __importStar = (this && this.__importStar) || (function () {
81456
82205
  Object.defineProperty(exports, "__esModule", ({ value: true }));
81457
82206
  exports.CopyChain = exports.CopyChainConf = void 0;
81458
82207
  const Expressions = __importStar(__webpack_require__(/*! ../abap/2_statements/expressions */ "./node_modules/@abaplint/core/build/src/abap/2_statements/expressions/index.js"));
82208
+ const Statements = __importStar(__webpack_require__(/*! ../abap/2_statements/statements */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/index.js"));
82209
+ const basic_1 = __webpack_require__(/*! ../abap/types/basic */ "./node_modules/@abaplint/core/build/src/abap/types/basic/index.js");
81459
82210
  const edit_helper_1 = __webpack_require__(/*! ../edit_helper */ "./node_modules/@abaplint/core/build/src/edit_helper.js");
81460
82211
  const issue_1 = __webpack_require__(/*! ../issue */ "./node_modules/@abaplint/core/build/src/issue.js");
81461
82212
  const _basic_rule_config_1 = __webpack_require__(/*! ./_basic_rule_config */ "./node_modules/@abaplint/core/build/src/rules/_basic_rule_config.js");
@@ -81473,28 +82224,49 @@ class CopyChain {
81473
82224
  key: "copy_chain",
81474
82225
  title: "Copy chain",
81475
82226
  shortDescription: `Finds a local variable that only passes a value on from one variable to another`,
81476
- extendedInformation: `Reports DATA v TYPE ... [CLEAR v.] v = src. ... y = v. when all of the following hold:
82227
+ extendedInformation: `Reports a local variable that only passes a value on, in two cases.
81477
82228
 
81478
- * v is a local variable of a METHOD, FORM or FUNCTION, declared with DATA without VALUE
82229
+ 1. DATA v TYPE ... [CLEAR v.] v = src. ... y = v. when all of the following hold:
82230
+
82231
+ * v is a local variable of a METHOD, FORM or FUNCTION, declared with DATA without VALUE, or with
82232
+ DATA(v) = VALUE t( ). with an explicit type and nothing in the parentheses
81479
82233
  * v has exactly one write besides CLEAR: v = src., and all CLEARs are before it
81480
82234
  * v has exactly one read, the complete source of y = v.
81481
- * src is a local variable, its address is never taken, and it is not written from v = src. until y = v.
82235
+ * src is a local variable, or a pass-by-value or RETURNING parameter of a method or form, its address is never
82236
+ taken, and it is not written from v = src. until y = v.
81482
82237
  * v = src. is executed before y = v. on every path: y = v. is in the same block or a nested block,
81483
82238
  and both are in the same loop
81484
82239
  * v and src are both reference types, or have identical types; for other types the copy into v can convert,
81485
82240
  e.g. truncate a string into a c field. For DATA(y) = v. the types must be identical, else y changes its type
81486
82241
  * the declaration, the CLEARs and v = src. are not chained
81487
82242
 
81488
- Objects with syntax errors, macros or unknown statements are skipped, as are procedures with dynamic code,
81489
- INCLUDE, test seams, enhancements or native SQL.
81490
-
81491
82243
  The quick fix deletes the declaration, the CLEARs and v = src., and changes the read to y = src.
81492
- No release requirement.`,
82244
+
82245
+ 2. DATA a TYPE REF TO c. CREATE OBJECT a. b = a. when all of the following hold:
82246
+
82247
+ * a is a local variable, declared with DATA without VALUE and not chained, its address is never taken
82248
+ * the only other references to a are CREATE OBJECT a. and the complete source of b = a., which is the next
82249
+ statement in the same block, apart from declarations like DATA in between
82250
+ * CREATE OBJECT has at most TYPE and EXPORTING, it is not chained. With EXCEPTIONS, a classic exception in the
82251
+ constructor would leave b unchanged instead of initial
82252
+ * b is a local variable, or a pass-by-value or RETURNING parameter, whose address is never taken, it is
82253
+ declared before the CREATE OBJECT and not used in its parameters, so the constructor cannot see b
82254
+ * b = a. passes the syntax check, so the static type of b accepts the class of a
82255
+
82256
+ The quick fix deletes the declaration of a and b = a., and creates the object in b, keeping TYPE and EXPORTING.
82257
+ If the static types of a and b differ, it adds TYPE with the class of a.
82258
+
82259
+ Objects with syntax errors, macros or unknown statements are skipped, as are procedures with dynamic code,
82260
+ INCLUDE, test seams, enhancements or native SQL. No release requirement.`,
81493
82261
  tags: [_irule_1.RuleTag.Quickfix],
81494
82262
  badExample: `DATA lo_copy TYPE REF TO zcl_node.
81495
82263
  lo_copy = lo_node.
81496
- ro_result = lo_copy.`,
81497
- goodExample: `ro_result = lo_node.`,
82264
+ ro_result = lo_copy.
82265
+ DATA lo_new TYPE REF TO zcl_node.
82266
+ CREATE OBJECT lo_new.
82267
+ ro_other = lo_new.`,
82268
+ goodExample: `ro_result = lo_node.
82269
+ CREATE OBJECT ro_other.`,
81498
82270
  };
81499
82271
  }
81500
82272
  getConfig() {
@@ -81520,7 +82292,7 @@ ro_result = lo_copy.`,
81520
82292
  }
81521
82293
  for (const variable of procedure.locals()) {
81522
82294
  const declaration = variable.declaration;
81523
- if (variable.inline || !(0, _procedure_analysis_1.plainData)(declaration) || declaration.getColon() !== undefined
82295
+ if ((0, _procedure_analysis_1.initialDeclaration)(variable) === undefined || declaration.getColon() !== undefined
81524
82296
  || variable.escapes()) {
81525
82297
  continue;
81526
82298
  }
@@ -81541,6 +82313,13 @@ ro_result = lo_copy.`,
81541
82313
  }
81542
82314
  const write = writes[0];
81543
82315
  const read = reads[0];
82316
+ if (write.statement.get() instanceof Statements.CreateObject) {
82317
+ const issue = clears.length === 0 ? this.createObject(procedure, variable, write, read) : undefined;
82318
+ if (issue !== undefined) {
82319
+ issues.push(issue);
82320
+ }
82321
+ continue;
82322
+ }
81544
82323
  const assign = (0, _procedure_analysis_1.plainMove)(write.statement);
81545
82324
  const use = (0, _procedure_analysis_1.plainMove)(read.statement);
81546
82325
  if (assign === undefined || use === undefined || assign.operator !== "=" || use.operator !== "="
@@ -81578,6 +82357,46 @@ ro_result = lo_copy.`,
81578
82357
  }
81579
82358
  return issues;
81580
82359
  }
82360
+ /** DATA a TYPE REF TO c. CREATE OBJECT a. b = a. */
82361
+ createObject(procedure, variable, write, read) {
82362
+ var _a, _b, _c;
82363
+ const create = (0, _procedure_analysis_1.plainCreateObject)(write.statement);
82364
+ const use = (0, _procedure_analysis_1.plainMove)(read.statement);
82365
+ const token = use === undefined ? undefined : (0, _procedure_analysis_1.singleTarget)(use.target);
82366
+ const target = token === undefined ? undefined : procedure.private(procedure.identifierAt(token));
82367
+ if (create === undefined || use === undefined || use.operator !== "=" || target === undefined
82368
+ || ((_a = (0, _procedure_analysis_1.singleTarget)(create.target)) === null || _a === void 0 ? void 0 : _a.getStart().equals(write.token.getStart())) !== true
82369
+ || ((_b = (0, _procedure_analysis_1.singleField)(use.source)) === null || _b === void 0 ? void 0 : _b.getStart().equals(read.token.getStart())) !== true
82370
+ || read.statement !== procedure.nextExecuted(write.statement) || !procedure.isSimple(read.statement)
82371
+ || target.occurrences.some(o => o.statement === write.statement)
82372
+ || (target.declaration !== undefined && procedure.index(target.declaration) > write.index)) {
82373
+ return undefined;
82374
+ }
82375
+ const type = variable.identifier.getType();
82376
+ const targetType = target.identifier.getType();
82377
+ let text = (0, _procedure_analysis_1.nodeText)(procedure.file, use.target);
82378
+ if (!(type instanceof basic_1.ObjectReferenceType)
82379
+ || !(targetType instanceof basic_1.ObjectReferenceType || targetType instanceof basic_1.GenericObjectReferenceType)) {
82380
+ return undefined;
82381
+ }
82382
+ else if (create.type === undefined && !(0, _procedure_analysis_1.identicalTypes)(type, targetType)) {
82383
+ // the class of a, as written in its declaration
82384
+ const declared = (_c = variable.declaration.findDirectExpression(Expressions.DataDefinition)) === null || _c === void 0 ? void 0 : _c.findDirectExpression(Expressions.Type);
82385
+ const name = declared === null || declared === void 0 ? void 0 : declared.findDirectExpression(Expressions.TypeName);
82386
+ if (name === undefined || (declared === null || declared === void 0 ? void 0 : declared.getTokens().length) !== 4) {
82387
+ return undefined;
82388
+ }
82389
+ text = text + " TYPE " + (0, _procedure_analysis_1.nodeText)(procedure.file, name);
82390
+ }
82391
+ const fix = (0, _procedure_analysis_1.mergeEdits)(procedure.file, [
82392
+ (0, _procedure_analysis_1.deleteStatement)(procedure.file, variable.declaration),
82393
+ edit_helper_1.EditHelper.replaceToken(procedure.file, write.token, text),
82394
+ (0, _procedure_analysis_1.deleteStatement)(procedure.file, read.statement)
82395
+ ]);
82396
+ const message = "Copy chain via " + variable.identifier.getName() + ", create the object in "
82397
+ + (0, _procedure_analysis_1.nodeText)(procedure.file, use.target) + " directly";
82398
+ return issue_1.Issue.atStatement(procedure.file, read.statement, message, this.getMetadata().key, this.conf.severity, fix);
82399
+ }
81581
82400
  }
81582
82401
  exports.CopyChain = CopyChain;
81583
82402
  //# sourceMappingURL=copy_chain.js.map
@@ -81647,7 +82466,9 @@ class CopyOfInitialVariable {
81647
82466
  shortDescription: `Finds assignments from a local variable that is always initial`,
81648
82467
  extendedInformation: `Reports DATA v TYPE ... [CLEAR v.] x = v. when all of the following hold:
81649
82468
 
81650
- * v is a local variable of a METHOD, FORM or FUNCTION, declared with DATA without VALUE
82469
+ * v is a local variable of a METHOD, FORM or FUNCTION, declared with DATA without VALUE, or with
82470
+ DATA(v) = VALUE t( ). with an explicit type and nothing in the parentheses. Both make v initial, the latter
82471
+ every time it is executed, so v is initial wherever it is read, also in loops
81651
82472
  * v is never changed, except by CLEAR v., and its address is never taken
81652
82473
  * v has exactly one read, the complete source of x = v., where x is not an inline declaration
81653
82474
  * x and v are both reference types, or have identical types. Initial values convert differently between
@@ -81690,7 +82511,7 @@ ro_result = lo_empty.`,
81690
82511
  const declaration = variable.declaration;
81691
82512
  const reads = variable.reads();
81692
82513
  const writes = variable.writes();
81693
- if (variable.inline || !(0, _procedure_analysis_1.plainData)(declaration) || declaration.getColon() !== undefined
82514
+ if ((0, _procedure_analysis_1.initialDeclaration)(variable) === undefined || declaration.getColon() !== undefined
81694
82515
  || variable.escapes() || reads.length !== 1
81695
82516
  || writes.some(w => { var _a; return ((_a = (0, _procedure_analysis_1.plainClear)(w.statement)) === null || _a === void 0 ? void 0 : _a.getStart().equals(w.token.getStart())) !== true
81696
82517
  || w.statement.getColon() !== undefined; })) {
@@ -90709,6 +91530,7 @@ __exportStar(__webpack_require__(/*! ./begin_single_include */ "./node_modules/@
90709
91530
  __exportStar(__webpack_require__(/*! ./boolean_flag_via_if */ "./node_modules/@abaplint/core/build/src/rules/boolean_flag_via_if.js"), exports);
90710
91531
  __exportStar(__webpack_require__(/*! ./bound_check_after_create */ "./node_modules/@abaplint/core/build/src/rules/bound_check_after_create.js"), exports);
90711
91532
  __exportStar(__webpack_require__(/*! ./call_transaction_authority_check */ "./node_modules/@abaplint/core/build/src/rules/call_transaction_authority_check.js"), exports);
91533
+ __exportStar(__webpack_require__(/*! ./cast_via_generic_reference */ "./node_modules/@abaplint/core/build/src/rules/cast_via_generic_reference.js"), exports);
90712
91534
  __exportStar(__webpack_require__(/*! ./catch_and_raise */ "./node_modules/@abaplint/core/build/src/rules/catch_and_raise.js"), exports);
90713
91535
  __exportStar(__webpack_require__(/*! ./cds_association_name */ "./node_modules/@abaplint/core/build/src/rules/cds_association_name.js"), exports);
90714
91536
  __exportStar(__webpack_require__(/*! ./cds_check_syntax */ "./node_modules/@abaplint/core/build/src/rules/cds_check_syntax.js"), exports);
@@ -90729,6 +91551,7 @@ __exportStar(__webpack_require__(/*! ./check_syntax */ "./node_modules/@abaplint
90729
91551
  __exportStar(__webpack_require__(/*! ./check_text_elements */ "./node_modules/@abaplint/core/build/src/rules/check_text_elements.js"), exports);
90730
91552
  __exportStar(__webpack_require__(/*! ./check_transformation_exists */ "./node_modules/@abaplint/core/build/src/rules/check_transformation_exists.js"), exports);
90731
91553
  __exportStar(__webpack_require__(/*! ./clear_after_declaration */ "./node_modules/@abaplint/core/build/src/rules/clear_after_declaration.js"), exports);
91554
+ __exportStar(__webpack_require__(/*! ./clear_before_overwrite */ "./node_modules/@abaplint/core/build/src/rules/clear_before_overwrite.js"), exports);
90732
91555
  __exportStar(__webpack_require__(/*! ./clear_exporting_parameters */ "./node_modules/@abaplint/core/build/src/rules/clear_exporting_parameters.js"), exports);
90733
91556
  __exportStar(__webpack_require__(/*! ./class_attribute_names */ "./node_modules/@abaplint/core/build/src/rules/class_attribute_names.js"), exports);
90734
91557
  __exportStar(__webpack_require__(/*! ./classic_exceptions_overlap */ "./node_modules/@abaplint/core/build/src/rules/classic_exceptions_overlap.js"), exports);
@@ -103647,21 +104470,51 @@ var __importStar = (this && this.__importStar) || (function () {
103647
104470
  Object.defineProperty(exports, "__esModule", ({ value: true }));
103648
104471
  exports.SingleUseExpressionTemp = exports.SingleUseExpressionTempConf = void 0;
103649
104472
  const Expressions = __importStar(__webpack_require__(/*! ../abap/2_statements/expressions */ "./node_modules/@abaplint/core/build/src/abap/2_statements/expressions/index.js"));
104473
+ const Statements = __importStar(__webpack_require__(/*! ../abap/2_statements/statements */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/index.js"));
103650
104474
  const nodes_1 = __webpack_require__(/*! ../abap/nodes */ "./node_modules/@abaplint/core/build/src/abap/nodes/index.js");
103651
104475
  const _reference_1 = __webpack_require__(/*! ../abap/5_syntax/_reference */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_reference.js");
103652
104476
  const edit_helper_1 = __webpack_require__(/*! ../edit_helper */ "./node_modules/@abaplint/core/build/src/edit_helper.js");
103653
104477
  const issue_1 = __webpack_require__(/*! ../issue */ "./node_modules/@abaplint/core/build/src/issue.js");
104478
+ const version_1 = __webpack_require__(/*! ../version */ "./node_modules/@abaplint/core/build/src/version.js");
103654
104479
  const _basic_rule_config_1 = __webpack_require__(/*! ./_basic_rule_config */ "./node_modules/@abaplint/core/build/src/rules/_basic_rule_config.js");
103655
104480
  const _irule_1 = __webpack_require__(/*! ./_irule */ "./node_modules/@abaplint/core/build/src/rules/_irule.js");
103656
104481
  const _procedure_analysis_1 = __webpack_require__(/*! ./_procedure_analysis */ "./node_modules/@abaplint/core/build/src/rules/_procedure_analysis.js");
103657
104482
  class SingleUseExpressionTempConf extends _basic_rule_config_1.BasicRuleConfig {
103658
104483
  }
103659
104484
  exports.SingleUseExpressionTempConf = SingleUseExpressionTempConf;
103660
- /** builtin functions whose result type follows the type of the argument */
103661
- const NUMERIC_FUNCTIONS = new Set(["abs", "sign", "ceil", "floor", "trunc", "frac", "ipow", "nmax", "nmin",
103662
- "round", "rescale"]);
103663
104485
  /** constructor expressions that can build the result directly in the target */
103664
104486
  const IN_PLACE = new Set(["VALUE", "CORRESPONDING", "COND", "SWITCH", "REDUCE", "FILTER"]);
104487
+ /** constructor expressions with an explicit type: KEYWORD type( ... ) */
104488
+ const CONSTRUCTORS = new Set(["VALUE", "CORRESPONDING", "COND", "SWITCH", "REDUCE", "FILTER", "CONV", "EXACT", "REF"]);
104489
+ /** types that an integer literal converts to without an exception */
104490
+ const LITERAL_CONVERSIONS = new Set(["I", "INT8", "F", "DECFLOAT16", "DECFLOAT34", "STRING"]);
104491
+ /** where the variable is read */
104492
+ var Placement;
104493
+ (function (Placement) {
104494
+ /** y = v. */
104495
+ Placement["Move"] = "move";
104496
+ /** v->m( ) or v->attr */
104497
+ Placement["Receiver"] = "receiver";
104498
+ /** m( v ), m( p = v ), CALL METHOD m EXPORTING p = v, NEW c( p = v ), CREATE OBJECT o EXPORTING p = v */
104499
+ Placement["Parameter"] = "parameter";
104500
+ /** APPEND v TO itab. */
104501
+ Placement["Append"] = "append";
104502
+ /** IF v = x., ELSEIF v = x., xsdbool( v = x ) */
104503
+ Placement["Comparison"] = "comparison";
104504
+ /** |{ v }| */
104505
+ Placement["Template"] = "template";
104506
+ })(Placement || (Placement = {}));
104507
+ var ExpressionKind;
104508
+ (function (ExpressionKind) {
104509
+ /** literal, string template without embeddings, CONV t( literal ) or a constant: the same value everywhere */
104510
+ ExpressionKind["Constant"] = "constant";
104511
+ /** reads only private variables and constants, cannot raise an exception or run other code */
104512
+ ExpressionKind["Pure"] = "pure";
104513
+ /** a data object that other code can change */
104514
+ ExpressionKind["Data"] = "data";
104515
+ /** method calls, NEW and everything else */
104516
+ ExpressionKind["Other"] = "other";
104517
+ })(ExpressionKind || (ExpressionKind = {}));
103665
104518
  class SingleUseExpressionTemp {
103666
104519
  constructor() {
103667
104520
  this.conf = new SingleUseExpressionTempConf();
@@ -103670,28 +104523,62 @@ class SingleUseExpressionTemp {
103670
104523
  return {
103671
104524
  key: "single_use_expression_temp",
103672
104525
  title: "Single use expression temporary",
103673
- shortDescription: `Finds DATA(a) = <expression>. directly followed by x = a., assign the expression to x`,
103674
- extendedInformation: `Reports DATA(a) = <expression>. when all of the following hold:
103675
-
103676
- * a is a local variable of a METHOD, FORM or FUNCTION, with no other reference than the next statement,
103677
- which is x = a., in the same block
104526
+ shortDescription: `Finds a local variable that only passes the value of an expression on to its single use`,
104527
+ extendedInformation: `Reports DATA(v) = <expression>. and DATA v TYPE t. [CLEAR v.] v = <expression>.
104528
+ when all of the following hold:
104529
+
104530
+ * v is a local variable of a METHOD, FORM or FUNCTION, its address is never taken. DATA(v) has no other
104531
+ reference than its single read; DATA v TYPE t. without VALUE and not chained, or DATA(v) = VALUE t( ). with
104532
+ nothing in the parentheses, has exactly one write besides CLEARs, v = <expression>., all CLEARs are before it,
104533
+ and exactly one read after it
104534
+ * the read is one of
104535
+ * the complete source of y = v.
104536
+ * the receiver of a method call or attribute access: v->m( ), v->attr
104537
+ * an actual parameter for an importing parameter: m( v ), m( p = v ), CALL METHOD m EXPORTING p = v,
104538
+ NEW c( p = v ) or CREATE OBJECT o EXPORTING p = v
104539
+ * APPEND v TO itab.
104540
+ * an operand of a comparison with =, <>, <, >, <=, >= or their word forms, in IF, ELSEIF or xsdbool( )
104541
+ * an embedding in a string template: |{ v }|
104542
+ * the read is in the statement directly after the assignment, in the same block, ignoring declarations like
104543
+ DATA and TYPES in between, unless the expression is
104544
+ constant: a literal, a string template without embeddings, CONV t( <integer literal> ) to a type that cannot
104545
+ overflow, or a constant. Then the assignment must be executed before the read on every path: the read is in
104546
+ the block of the assignment or a nested block
103678
104547
  * the result type of the expression does not depend on the target: no arithmetic or bit operators, no unary
103679
- minus, no numeric functions like abs( ) or trunc( ) whose type follows the argument, no CONV or EXACT,
103680
- no # type inference at the top. String templates, && concatenations, literals, method calls, NEW, CAST,
104548
+ minus, no numeric functions like abs( ) or trunc( ) whose type follows the argument, no CONV or EXACT except
104549
+ the constant CONV t( literal ), no # type inference at the top. String templates, && concatenations, literals, method calls, NEW, CAST,
103681
104550
  constructor expressions with explicit type and table expressions are fine
104551
+ * for DATA v TYPE t. and DATA(v) = VALUE t( )., the static type of the expression is identical to t. Into y = v. also a reference whose
104552
+ static type is a subtype of t, unless y is an inline declaration, and an expression whose type depends on the
104553
+ target, like arithmetic, if y has a type identical to t
103682
104554
  * VALUE, CORRESPONDING, COND, SWITCH, REDUCE and FILTER can build their result directly in the target,
103683
- they are only inlined into a new variable, or a local variable that they do not read and whose address
104555
+ they are only moved into a new variable, or a local variable that they do not read and whose address
103684
104556
  is never taken
103685
- * plain data objects are left for unnecessary_copy
104557
+ * the order of evaluation does not change: if the expression is not constant, nothing that is evaluated before the
104558
+ read in the same statement calls a method or function, constructs an object, accesses a table or an attribute
104559
+ via a reference, does arithmetic, or reads data other than local variables, pass-by-value parameters and
104560
+ constants; the read is not inside COND, SWITCH, VALUE or other constructor expressions except NEW, and not
104561
+ after AND, OR or EQUIV, where it might not be evaluated. This does not apply to expressions that only read
104562
+ local variables and cannot raise an exception
104563
+ * a data object that other code can change is only moved if it keeps its value until the read, see
104564
+ unnecessary_copy, a data object read by DATA(v) = <data object>. is left for unnecessary_copy, and a local
104565
+ variable copied into y = v. for copy_chain
104566
+ * a character constant in IF, ELSEIF, WHILE, CHECK, CASE or WHEN is left for constant_condition
103686
104567
 
103687
104568
  Objects with syntax errors, macros or unknown statements are skipped, as are procedures with dynamic code,
103688
104569
  INCLUDE, test seams, enhancements or native SQL.
103689
104570
 
103690
- The quick fix deletes the declaration and writes x = <expression>. No release requirement.`,
104571
+ The quick fix deletes the declaration, the CLEARs and the assignment, and replaces the read of v with the
104572
+ expression. Moving the expression of DATA v TYPE t. into other places than y = v. requires 7.40 SP02 or above,
104573
+ ABAP Cloud or open-abap.`,
103691
104574
  tags: [_irule_1.RuleTag.Quickfix],
103692
104575
  badExample: `DATA(lo_conf) = NEW zcl_conf( ).
103693
- mo_conf = lo_conf.`,
103694
- goodExample: `mo_conf = NEW zcl_conf( ).`,
104576
+ mo_conf = lo_conf.
104577
+ DATA lv_key TYPE string.
104578
+ lv_key = |abapdoc|.
104579
+ lo_meta = NEW zcl_meta( iv_key = lv_key ).`,
104580
+ goodExample: `mo_conf = NEW zcl_conf( ).
104581
+ lo_meta = NEW zcl_meta( iv_key = |abapdoc| ).`,
103695
104582
  };
103696
104583
  }
103697
104584
  getConfig() {
@@ -103710,61 +104597,501 @@ mo_conf = lo_conf.`,
103710
104597
  if (analysis === undefined) {
103711
104598
  return [];
103712
104599
  }
104600
+ const config = this.reg.getConfig();
104601
+ const expressions = (0, version_1.releaseAtLeast)(config.getRelease(), version_1.Release.v740sp02)
104602
+ || config.getLanguageVersion() === version_1.LanguageVersion.Cloud || config.isOpenABAP();
103713
104603
  const issues = [];
103714
104604
  for (const procedure of analysis.procedures) {
103715
104605
  if (procedure.supported === false) {
103716
104606
  continue;
103717
104607
  }
103718
104608
  for (const variable of procedure.locals()) {
103719
- const declaration = variable.declaration;
103720
- const move = variable.inline ? (0, _procedure_analysis_1.plainMove)(declaration) : undefined;
103721
- const read = variable.occurrences[0];
103722
- if (move === undefined || move.operator !== "="
103723
- || move.target.getChildren().length !== 1
103724
- || move.target.findDirectExpression(Expressions.InlineData) === undefined
103725
- || variable.occurrences.length !== 1 || read.kind !== _procedure_analysis_1.OccurrenceKind.Read
103726
- || read.statement !== procedure.statements[procedure.index(declaration) + 1]
103727
- || procedure.block(read.statement) !== procedure.block(declaration)
103728
- || !procedure.isSimple(read.statement)) {
104609
+ const temp = (_a = (variable.inline ? this.inlineTemp(procedure, variable) : undefined)) !== null && _a !== void 0 ? _a : this.declaredTemp(variable);
104610
+ const read = variable.reads()[0];
104611
+ if (temp === undefined || read === undefined) {
103729
104612
  continue;
103730
104613
  }
103731
- const use = (0, _procedure_analysis_1.plainMove)(read.statement);
103732
- if (use === undefined || use.operator !== "="
103733
- || ((_a = (0, _procedure_analysis_1.singleField)(use.source)) === null || _a === void 0 ? void 0 : _a.getStart().equals(read.token.getStart())) !== true
103734
- || (0, _procedure_analysis_1.dataSource)(procedure, move.source) !== undefined
103735
- || !this.targetIndependent(procedure, move.source)
103736
- || !this.noAliasing(procedure, move.source, use.target)) {
104614
+ const placement = this.placement(procedure, read.statement, read.token);
104615
+ if (placement === undefined
104616
+ || (!variable.inline && placement !== Placement.Move && expressions === false)
104617
+ || !this.valid(procedure, temp, read, placement)) {
103737
104618
  continue;
103738
104619
  }
103739
- const fix = (0, _procedure_analysis_1.mergeEdits)(procedure.file, [
103740
- edit_helper_1.EditHelper.replaceToken(procedure.file, read.token, (0, _procedure_analysis_1.nodeText)(procedure.file, move.source)),
103741
- (0, _procedure_analysis_1.deleteStatement)(procedure.file, declaration)
103742
- ]);
103743
- const message = "Assign the expression directly instead of via " + variable.identifier.getName();
103744
- issues.push(issue_1.Issue.atStatement(procedure.file, declaration, message, this.getMetadata().key, this.conf.severity, fix));
104620
+ const edits = [edit_helper_1.EditHelper.replaceToken(procedure.file, read.token, (0, _procedure_analysis_1.nodeText)(procedure.file, temp.source))];
104621
+ for (const statement of temp.remove) {
104622
+ edits.push((0, _procedure_analysis_1.deleteStatement)(procedure.file, statement));
104623
+ }
104624
+ const fix = (0, _procedure_analysis_1.mergeEdits)(procedure.file, edits);
104625
+ const message = "Use the expression directly instead of via " + variable.identifier.getName();
104626
+ issues.push(issue_1.Issue.atStatement(procedure.file, temp.write, message, this.getMetadata().key, this.conf.severity, fix));
103745
104627
  }
103746
104628
  }
103747
104629
  return issues;
103748
104630
  }
104631
+ /** DATA(v) = <expression>. with a single read, plain data objects are left for unnecessary_copy */
104632
+ inlineTemp(procedure, variable) {
104633
+ const declaration = variable.declaration;
104634
+ const move = (0, _procedure_analysis_1.plainMove)(declaration);
104635
+ if (move === undefined || move.operator !== "="
104636
+ || move.target.getChildren().length !== 1
104637
+ || move.target.findDirectExpression(Expressions.InlineData) === undefined
104638
+ || variable.occurrences.length !== 1 || variable.occurrences[0].kind !== _procedure_analysis_1.OccurrenceKind.Read
104639
+ || (0, _procedure_analysis_1.dataSource)(procedure, move.source) !== undefined
104640
+ || !this.targetIndependent(procedure, move.source)) {
104641
+ return undefined;
104642
+ }
104643
+ return { variable, write: declaration, source: move.source, remove: [declaration], typed: false };
104644
+ }
104645
+ /** DATA v TYPE t. or DATA(v) = VALUE t( )., [CLEAR v.] v = <expression>. with a single read after it */
104646
+ declaredTemp(variable) {
104647
+ var _a, _b;
104648
+ const declaration = variable.declaration;
104649
+ if ((0, _procedure_analysis_1.initialDeclaration)(variable) === undefined || declaration.getColon() !== undefined || variable.escapes()) {
104650
+ return undefined;
104651
+ }
104652
+ const clears = [];
104653
+ const writes = [];
104654
+ for (const write of variable.writes()) {
104655
+ if (((_a = (0, _procedure_analysis_1.plainClear)(write.statement)) === null || _a === void 0 ? void 0 : _a.getStart().equals(write.token.getStart())) === true
104656
+ && write.statement.getColon() === undefined) {
104657
+ clears.push(write);
104658
+ }
104659
+ else {
104660
+ writes.push(write);
104661
+ }
104662
+ }
104663
+ const reads = variable.reads();
104664
+ if (writes.length !== 1 || reads.length !== 1) {
104665
+ return undefined;
104666
+ }
104667
+ const write = writes[0];
104668
+ const move = (0, _procedure_analysis_1.plainMove)(write.statement);
104669
+ if (move === undefined || move.operator !== "="
104670
+ || ((_b = (0, _procedure_analysis_1.singleTarget)(move.target)) === null || _b === void 0 ? void 0 : _b.getStart().equals(write.token.getStart())) !== true
104671
+ || clears.some(c => c.index > write.index)
104672
+ || reads[0].index <= write.index) {
104673
+ return undefined;
104674
+ }
104675
+ return { variable, write: write.statement, source: move.source,
104676
+ remove: [declaration, ...clears.map(c => c.statement), write.statement], typed: true };
104677
+ }
104678
+ placement(procedure, statement, token) {
104679
+ var _a, _b, _c, _d;
104680
+ if (statement.getColon() !== undefined) {
104681
+ return undefined;
104682
+ }
104683
+ const move = (0, _procedure_analysis_1.plainMove)(statement);
104684
+ if (move !== undefined && move.operator === "=" && ((_a = (0, _procedure_analysis_1.singleField)(move.source)) === null || _a === void 0 ? void 0 : _a.getStart().equals(token.getStart())) === true) {
104685
+ return Placement.Move;
104686
+ }
104687
+ const path = (0, _procedure_analysis_1.expressionPath)(statement, token);
104688
+ if (path.some(e => e.get() instanceof Expressions.Target || e.get() instanceof Expressions.MethodSource
104689
+ || e.get() instanceof Expressions.Dereference)) {
104690
+ return undefined;
104691
+ }
104692
+ const tokens = procedure.tokensOf(statement);
104693
+ const index = tokens.findIndex(t => t.getStart().equals(token.getStart()));
104694
+ if (((_b = tokens[index + 1]) === null || _b === void 0 ? void 0 : _b.getStr()) === "->") {
104695
+ return ((_c = tokens[index + 2]) === null || _c === void 0 ? void 0 : _c.getStr()) === "*" ? undefined : Placement.Receiver;
104696
+ }
104697
+ let sourceIndex = path.length - 1;
104698
+ while (sourceIndex >= 0 && !(path[sourceIndex].get() instanceof Expressions.Source)
104699
+ && !(path[sourceIndex].get() instanceof Expressions.SimpleSource4)) {
104700
+ sourceIndex--;
104701
+ }
104702
+ const source = path[sourceIndex];
104703
+ if (source === undefined || source.countTokens() !== 1
104704
+ || source.findDirectExpression(Expressions.FieldChain) === undefined) {
104705
+ return undefined;
104706
+ }
104707
+ const parent = path[sourceIndex - 1];
104708
+ if (parent === undefined) {
104709
+ return statement.get() instanceof Statements.Append && statement.getChildren()[1] === source
104710
+ ? Placement.Append : undefined;
104711
+ }
104712
+ else if (parent.get() instanceof Expressions.StringTemplateSource) {
104713
+ return Placement.Template;
104714
+ }
104715
+ else if (parent.get() instanceof Expressions.Compare) {
104716
+ if (parent.findDirectExpression(Expressions.CompareOperator) === undefined) {
104717
+ return undefined;
104718
+ }
104719
+ let owner = sourceIndex - 2;
104720
+ while (((_d = path[owner]) === null || _d === void 0 ? void 0 : _d.get()) instanceof Expressions.Cond) {
104721
+ owner--;
104722
+ }
104723
+ if (owner === sourceIndex - 2) {
104724
+ return undefined;
104725
+ }
104726
+ else if (owner < 0) {
104727
+ const type = statement.get();
104728
+ return type instanceof Statements.If || type instanceof Statements.ElseIf ? Placement.Comparison : undefined;
104729
+ }
104730
+ const keyword = path[owner].getFirstToken().getStr().toUpperCase();
104731
+ return path[owner].get() instanceof Expressions.Source && (keyword === "XSDBOOL" || keyword === "BOOLC")
104732
+ ? Placement.Comparison : undefined;
104733
+ }
104734
+ return (0, _procedure_analysis_1.formalParameter)(procedure, statement, token) !== undefined ? Placement.Parameter : undefined;
104735
+ }
104736
+ valid(procedure, temp, read, placement) {
104737
+ const statement = read.statement;
104738
+ const kind = this.kind(procedure, temp.source);
104739
+ if (kind === ExpressionKind.Constant) {
104740
+ if (!procedure.dominates(temp.write, statement)
104741
+ || (!temp.typed && (0, _procedure_analysis_1.isCondition)(statement) && (0, _procedure_analysis_1.characterConstant)(procedure, temp.source) !== undefined)) {
104742
+ return false;
104743
+ }
104744
+ }
104745
+ else if (statement !== procedure.nextExecuted(temp.write)
104746
+ || !(procedure.isSimple(statement) || statement.get() instanceof Statements.If)) {
104747
+ return false;
104748
+ }
104749
+ if (!this.typeMatches(procedure, temp, statement, placement)) {
104750
+ return false;
104751
+ }
104752
+ else if (placement === Placement.Receiver && !this.chainable(temp.source)) {
104753
+ return false;
104754
+ }
104755
+ const data = (0, _procedure_analysis_1.dataSource)(procedure, temp.source);
104756
+ if (kind === ExpressionKind.Constant) {
104757
+ return true;
104758
+ }
104759
+ else if ((data === null || data === void 0 ? void 0 : data.kind) === _procedure_analysis_1.SourceKind.Local && (0, _procedure_analysis_1.singleField)(temp.source) !== undefined && placement === Placement.Move) {
104760
+ // left for copy_chain
104761
+ return false;
104762
+ }
104763
+ else if (!this.notWrittenIn(procedure, temp.source, statement)) {
104764
+ return false;
104765
+ }
104766
+ else if (kind === ExpressionKind.Pure) {
104767
+ return true;
104768
+ }
104769
+ else if (!this.evaluatedFirst(procedure, statement, read.token) || !this.alwaysEvaluated(statement, read.token)) {
104770
+ return false;
104771
+ }
104772
+ return data === undefined || (0, _procedure_analysis_1.unchangedBetween)(procedure, data, temp.write, statement, read.token);
104773
+ }
104774
+ kind(procedure, source) {
104775
+ var _a;
104776
+ const data = (0, _procedure_analysis_1.dataSource)(procedure, source);
104777
+ if (this.constant(source) || (data === null || data === void 0 ? void 0 : data.kind) === _procedure_analysis_1.SourceKind.Immutable) {
104778
+ return ExpressionKind.Constant;
104779
+ }
104780
+ else if (((data === null || data === void 0 ? void 0 : data.kind) === _procedure_analysis_1.SourceKind.Local && ((_a = data.base) === null || _a === void 0 ? void 0 : _a.escapes()) === false) || this.pure(procedure, source)) {
104781
+ return ExpressionKind.Pure;
104782
+ }
104783
+ else if (data !== undefined) {
104784
+ return ExpressionKind.Data;
104785
+ }
104786
+ return ExpressionKind.Other;
104787
+ }
104788
+ /** a literal, a string template without embeddings, or CONV t( literal ) that cannot raise an exception */
104789
+ constant(source) {
104790
+ var _a;
104791
+ const children = source.getChildren();
104792
+ const first = children[0];
104793
+ if (children.length === 1 && first instanceof nodes_1.ExpressionNode) {
104794
+ return first.get() instanceof Expressions.Constant
104795
+ || (first.get() instanceof Expressions.StringTemplate
104796
+ && first.findDirectExpression(Expressions.StringTemplateSource) === undefined);
104797
+ }
104798
+ if (children.length !== 5 || source.getFirstToken().getStr().toUpperCase() !== "CONV") {
104799
+ return false;
104800
+ }
104801
+ const type = ((_a = source.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _a === void 0 ? void 0 : _a.concatTokens().toUpperCase()) || "";
104802
+ const body = source.findDirectExpression(Expressions.ConvBody);
104803
+ const value = (body === null || body === void 0 ? void 0 : body.getChildren().length) === 1 ? body.findDirectExpression(Expressions.Source) : undefined;
104804
+ const literal = (value === null || value === void 0 ? void 0 : value.getChildren().length) === 1 ? value.findDirectExpression(Expressions.Constant) : undefined;
104805
+ return literal !== undefined && LITERAL_CONVERSIONS.has(type)
104806
+ && (literal.findDirectExpression(Expressions.Integer) !== undefined || type === "STRING");
104807
+ }
104808
+ /** reads only private variables and constants, and cannot raise an exception: string templates and
104809
+ * concatenations of such fields, xsdbool( ) and boolc( ) of IS INITIAL, IS BOUND, IS INSTANCE OF and
104810
+ * comparisons without conversion */
104811
+ pure(procedure, source) {
104812
+ const children = source.getChildren();
104813
+ const first = children[0];
104814
+ const keyword = first instanceof nodes_1.TokenNode ? first.getFirstToken().getStr().toUpperCase() : "";
104815
+ if (children.length === 1 && first instanceof nodes_1.ExpressionNode) {
104816
+ return this.pureOperand(procedure, first);
104817
+ }
104818
+ else if (children.length === 3 && children[1] instanceof nodes_1.TokenNode && children[1].getFirstToken().getStr() === "&&"
104819
+ && children[0] instanceof nodes_1.ExpressionNode && children[2] instanceof nodes_1.ExpressionNode) {
104820
+ return this.pureOperand(procedure, children[0]) && this.pure(procedure, children[2]);
104821
+ }
104822
+ else if (children.length === 4 && (keyword === "XSDBOOL" || keyword === "BOOLC")) {
104823
+ const cond = source.findDirectExpression(Expressions.Cond);
104824
+ return cond !== undefined && this.pureCondition(procedure, cond);
104825
+ }
104826
+ return false;
104827
+ }
104828
+ /** a literal, a private variable or constant, or a string template that embeds only those, without formatting */
104829
+ pureOperand(procedure, operand) {
104830
+ if (operand.get() instanceof Expressions.Constant) {
104831
+ return true;
104832
+ }
104833
+ else if (operand.get() instanceof Expressions.FieldChain) {
104834
+ return operand.countTokens() === 1 && this.privateField(procedure, operand.getFirstToken());
104835
+ }
104836
+ else if (operand.get() instanceof Expressions.StringTemplate) {
104837
+ return operand.findDirectExpressions(Expressions.StringTemplateSource).every(s => s.getChildren().length === 1
104838
+ && this.operandType(procedure, s.findDirectExpression(Expressions.Source)) !== undefined);
104839
+ }
104840
+ return false;
104841
+ }
104842
+ pureCondition(procedure, cond) {
104843
+ for (const child of cond.getChildren()) {
104844
+ if (child instanceof nodes_1.TokenNode) {
104845
+ continue;
104846
+ }
104847
+ else if (child.get() instanceof Expressions.Cond) {
104848
+ if (!this.pureCondition(procedure, child)) {
104849
+ return false;
104850
+ }
104851
+ }
104852
+ else if (child.get() instanceof Expressions.Compare) {
104853
+ const operands = child.findDirectExpressions(Expressions.Source);
104854
+ const words = child.getChildren().filter(c => c instanceof nodes_1.TokenNode).map(c => c.getFirstToken().getStr().toUpperCase());
104855
+ const operator = child.findDirectExpression(Expressions.CompareOperator);
104856
+ const types = operands.map(o => this.operandType(procedure, o));
104857
+ if (types.some(t => t === undefined)) {
104858
+ return false;
104859
+ }
104860
+ else if (operands.length === 1 && words[words.length - 1] !== "SUPPLIED" && words[words.length - 1] !== "REQUESTED"
104861
+ && words.includes("IS") && child.findDirectExpression(Expressions.CompareOperator) === undefined) {
104862
+ // IS [NOT] INITIAL, IS [NOT] BOUND, IS [NOT] INSTANCE OF
104863
+ continue;
104864
+ }
104865
+ else if (operands.length === 2 && operator !== undefined
104866
+ && ((0, _procedure_analysis_1.identicalTypes)(types[0], types[1]) || ((0, _procedure_analysis_1.isReferenceType)(types[0]) && (0, _procedure_analysis_1.isReferenceType)(types[1])))) {
104867
+ continue;
104868
+ }
104869
+ return false;
104870
+ }
104871
+ else {
104872
+ return false;
104873
+ }
104874
+ }
104875
+ return true;
104876
+ }
104877
+ /** type of a private variable, a constant or a literal, which can be read without side effects */
104878
+ operandType(procedure, source) {
104879
+ if (source.getChildren().length !== 1 || source.countTokens() !== 1) {
104880
+ return undefined;
104881
+ }
104882
+ const constant = source.findDirectExpression(Expressions.Constant);
104883
+ if (constant !== undefined) {
104884
+ return (0, _procedure_analysis_1.literalType)(constant);
104885
+ }
104886
+ const field = (0, _procedure_analysis_1.singleField)(source);
104887
+ if (field === undefined || !this.privateField(procedure, field)) {
104888
+ return undefined;
104889
+ }
104890
+ return procedure.identifierAt(field).getType();
104891
+ }
104892
+ privateField(procedure, token) {
104893
+ const identifier = procedure.identifierAt(token);
104894
+ return (0, _procedure_analysis_1.isImmutable)(identifier) || procedure.private(identifier) !== undefined;
104895
+ }
104896
+ /** the variables read by the expression are not changed by the statement that reads the temporary,
104897
+ * except as the target of a move, which is written after its source is evaluated */
104898
+ notWrittenIn(procedure, source, statement) {
104899
+ var _a;
104900
+ const target = (_a = (0, _procedure_analysis_1.plainMove)(statement)) === null || _a === void 0 ? void 0 : _a.target;
104901
+ const inTarget = (token) => target !== undefined
104902
+ && !token.getStart().isBefore(target.getFirstToken().getStart()) && !target.getLastToken().getStart().isBefore(token.getStart());
104903
+ for (const token of source.getTokens()) {
104904
+ const variable = procedure.variable(procedure.identifierAt(token));
104905
+ if ((variable === null || variable === void 0 ? void 0 : variable.occurrences.some(o => o.statement === statement && o.kind !== _procedure_analysis_1.OccurrenceKind.Read && !inTarget(o.token))) === true) {
104906
+ return false;
104907
+ }
104908
+ }
104909
+ return true;
104910
+ }
104911
+ /** nothing evaluated before the token in the statement can run other code, raise an exception or read data that
104912
+ * other code changes. The target of a move is evaluated after its source */
104913
+ evaluatedFirst(procedure, statement, token) {
104914
+ var _a, _b;
104915
+ const position = token.getStart();
104916
+ const before = (node) => node.getLastToken().getStart().isBefore(position);
104917
+ for (const type of [Expressions.MethodCall, Expressions.NewObject, Expressions.Cast, Expressions.TableExpression,
104918
+ Expressions.ArithOperator, Expressions.Dereference]) {
104919
+ if (statement.findAllExpressionsRecursive(type).some(before)) {
104920
+ return false;
104921
+ }
104922
+ }
104923
+ for (const source of statement.findAllExpressionsRecursive(Expressions.Source)) {
104924
+ const keyword = source.getFirstToken().getStr().toUpperCase();
104925
+ if (before(source) && CONSTRUCTORS.has(keyword) && source.findDirectExpression(Expressions.TypeNameOrInfer) !== undefined) {
104926
+ return false;
104927
+ }
104928
+ }
104929
+ // the target of a move or CREATE OBJECT is written after the source and the parameters are evaluated
104930
+ let target = (_a = (0, _procedure_analysis_1.plainMove)(statement)) === null || _a === void 0 ? void 0 : _a.target;
104931
+ if (statement.get() instanceof Statements.CreateObject) {
104932
+ target = statement.findDirectExpression(Expressions.Target);
104933
+ if ((target === null || target === void 0 ? void 0 : target.countTokens()) !== 1) {
104934
+ return false;
104935
+ }
104936
+ }
104937
+ const tokens = procedure.tokensOf(statement);
104938
+ for (let i = 0; i < tokens.length && tokens[i].getStart().isBefore(position); i++) {
104939
+ if (target !== undefined && !tokens[i].getStart().isBefore(target.getFirstToken().getStart())
104940
+ && !target.getLastToken().getStart().isBefore(tokens[i].getStart())) {
104941
+ continue;
104942
+ }
104943
+ const references = procedure.analysis.referencesAt(procedure.file.getFilename(), tokens[i]);
104944
+ if (references.some(r => r.referenceType === _reference_1.ReferenceType.DataReadReference
104945
+ || r.referenceType === _reference_1.ReferenceType.DataWriteReference)) {
104946
+ // a private variable or constant, and a reference only to call a method: v->m( ... token ... )
104947
+ if (!this.privateField(procedure, tokens[i])
104948
+ || (((_b = tokens[i + 1]) === null || _b === void 0 ? void 0 : _b.getStr()) === "->"
104949
+ && (tokens[i + 2] === undefined || procedure.methodAt(tokens[i + 2]) === undefined))) {
104950
+ return false;
104951
+ }
104952
+ }
104953
+ }
104954
+ return true;
104955
+ }
104956
+ /** the token is evaluated whenever the statement is: not in a branch of COND or SWITCH, not in an iteration of
104957
+ * VALUE, REDUCE or FILTER, and not after AND, OR or EQUIV */
104958
+ alwaysEvaluated(statement, token) {
104959
+ const position = token.getStart();
104960
+ for (const node of (0, _procedure_analysis_1.expressionPath)(statement, token)) {
104961
+ if (node.get() instanceof Expressions.Source) {
104962
+ const keyword = node.getFirstToken().getStr().toUpperCase();
104963
+ if (CONSTRUCTORS.has(keyword) && node.findDirectExpression(Expressions.TypeNameOrInfer) !== undefined) {
104964
+ return false;
104965
+ }
104966
+ }
104967
+ else if (node.get() instanceof Expressions.Cond) {
104968
+ for (const child of node.getChildren()) {
104969
+ const word = child.getFirstToken().getStr().toUpperCase();
104970
+ if (child instanceof nodes_1.TokenNode && child.getFirstToken().getStart().isBefore(position)
104971
+ && (word === "AND" || word === "OR" || word === "EQUIV")) {
104972
+ return false;
104973
+ }
104974
+ }
104975
+ }
104976
+ else if (node.get() instanceof Expressions.For || node.get() instanceof Expressions.Let) {
104977
+ return false;
104978
+ }
104979
+ }
104980
+ return true;
104981
+ }
104982
+ /** the expression can be followed by -> */
104983
+ chainable(source) {
104984
+ const children = source.getChildren();
104985
+ return children.length === 1 && (children[0].get() instanceof Expressions.FieldChain
104986
+ || children[0].get() instanceof Expressions.MethodCallChain);
104987
+ }
104988
+ typeMatches(procedure, temp, statement, placement) {
104989
+ const use = placement === Placement.Move ? (0, _procedure_analysis_1.plainMove)(statement) : undefined;
104990
+ if (use !== undefined && !this.noAliasing(procedure, temp.source, use.target)) {
104991
+ return false;
104992
+ }
104993
+ else if (!temp.typed) {
104994
+ // the variable has the type of the expression
104995
+ return true;
104996
+ }
104997
+ const type = temp.variable.identifier.getType();
104998
+ const independent = this.targetIndependent(procedure, temp.source);
104999
+ if (use === undefined) {
105000
+ return independent && this.sameType(procedure, temp, type);
105001
+ }
105002
+ const inline = use.target.findFirstExpression(Expressions.InlineData) !== undefined;
105003
+ if (independent) {
105004
+ if (this.sameType(procedure, temp, type)) {
105005
+ return true;
105006
+ }
105007
+ // an up cast into v, and v into y, is an up cast into y
105008
+ const expression = (0, _procedure_analysis_1.expressionType)(procedure, temp.source);
105009
+ const constructed = this.constructedType(temp.source);
105010
+ const reference = expression === undefined ? (constructed === null || constructed === void 0 ? void 0 : constructed.reference) === true && constructed.name !== "#"
105011
+ : (0, _procedure_analysis_1.isReferenceType)(expression);
105012
+ return inline === false && reference && (0, _procedure_analysis_1.isReferenceType)(type);
105013
+ }
105014
+ // the type of the expression follows the target
105015
+ const target = inline ? undefined : (0, _procedure_analysis_1.targetType)(procedure, use.target);
105016
+ return target !== undefined && (0, _procedure_analysis_1.identicalTypes)(target, type);
105017
+ }
105018
+ /** the expression has exactly the type of the declaration DATA v TYPE t. */
105019
+ sameType(procedure, temp, type) {
105020
+ const expression = (0, _procedure_analysis_1.expressionType)(procedure, temp.source);
105021
+ if (expression !== undefined) {
105022
+ return (0, _procedure_analysis_1.identicalTypes)(expression, type);
105023
+ }
105024
+ // constructor expressions with an explicit type, compared by the name of the type
105025
+ const declared = this.declaredType(temp.variable);
105026
+ const constructed = this.constructedType(temp.source);
105027
+ return declared !== undefined && constructed !== undefined && constructed.name !== "#"
105028
+ && constructed.name === declared.name && constructed.reference === declared.reference;
105029
+ }
105030
+ /** the type name of DATA v TYPE t., DATA v TYPE REF TO t. or DATA(v) = VALUE t( ). */
105031
+ declaredType(variable) {
105032
+ var _a, _b;
105033
+ const declaration = variable.declaration;
105034
+ if ((0, _procedure_analysis_1.initialDeclaration)(variable) === _procedure_analysis_1.InitialDeclaration.Value) {
105035
+ return this.constructedType((0, _procedure_analysis_1.plainMove)(declaration).source);
105036
+ }
105037
+ const type = (_a = declaration.findDirectExpression(Expressions.DataDefinition)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(Expressions.Type);
105038
+ const name = (_b = type === null || type === void 0 ? void 0 : type.findDirectExpression(Expressions.TypeName)) === null || _b === void 0 ? void 0 : _b.concatTokens().toUpperCase();
105039
+ const words = type === null || type === void 0 ? void 0 : type.getTokens().map(t => t.getStr().toUpperCase()).join(" ");
105040
+ if (name === undefined) {
105041
+ return undefined;
105042
+ }
105043
+ else if (words === "TYPE " + name) {
105044
+ return { name, reference: false };
105045
+ }
105046
+ else if (words === "TYPE REF TO " + name) {
105047
+ return { name, reference: true };
105048
+ }
105049
+ return undefined;
105050
+ }
105051
+ /** VALUE t( ... ), CONV t( ... ) etc., NEW c( ... ) and CAST c( ... ) with an explicit type */
105052
+ constructedType(source) {
105053
+ const children = source.getChildren();
105054
+ const last = children[children.length - 1];
105055
+ const keyword = source.getFirstToken().getStr().toUpperCase();
105056
+ let name;
105057
+ let reference = false;
105058
+ if (CONSTRUCTORS.has(keyword) && keyword !== "REF" && last instanceof nodes_1.TokenNode && last.getFirstToken().getStr() === ")") {
105059
+ name = source.findDirectExpression(Expressions.TypeNameOrInfer);
105060
+ }
105061
+ else if (children.length === 1 && children[0].get() instanceof Expressions.MethodCallChain
105062
+ && children[0].getChildren().length === 1) {
105063
+ const constructor = children[0].getChildren()[0];
105064
+ if (constructor.get() instanceof Expressions.NewObject || constructor.get() instanceof Expressions.Cast) {
105065
+ name = constructor.findDirectExpression(Expressions.TypeNameOrInfer);
105066
+ reference = true;
105067
+ }
105068
+ }
105069
+ const text = name === null || name === void 0 ? void 0 : name.concatTokens().toUpperCase();
105070
+ return text === undefined ? undefined : { name: text, reference };
105071
+ }
103749
105072
  /** the type of the expression does not depend on where it is assigned to */
103750
105073
  targetIndependent(procedure, source) {
103751
- var _a, _b;
105074
+ var _a, _b, _c;
103752
105075
  const first = source.getFirstToken().getStr().toUpperCase();
103753
- if (first === "CONV" || first === "EXACT"
105076
+ if (this.constant(source)) {
105077
+ return true;
105078
+ }
105079
+ else if (first === "CONV" || first === "EXACT"
103754
105080
  || ((_a = source.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _a === void 0 ? void 0 : _a.concatTokens()) === "#"
105081
+ || ((_b = this.constructedType(source)) === null || _b === void 0 ? void 0 : _b.name) === "#"
103755
105082
  || source.findFirstExpression(Expressions.ArithOperator) !== undefined
103756
105083
  || source.getTokens().some(t => t.getStr().toUpperCase().startsWith("BIT-"))
103757
105084
  || !this.concatenationOnly(source)) {
103758
105085
  return false;
103759
105086
  }
103760
105087
  for (const call of source.findAllExpressionsRecursive(Expressions.MethodCall)) {
103761
- const name = (_b = call.findDirectExpression(Expressions.MethodName)) === null || _b === void 0 ? void 0 : _b.getFirstToken();
105088
+ const name = (_c = call.findDirectExpression(Expressions.MethodName)) === null || _c === void 0 ? void 0 : _c.getFirstToken();
103762
105089
  if (name === undefined) {
103763
105090
  return false;
103764
105091
  }
103765
105092
  const builtin = procedure.analysis.referencesAt(procedure.file.getFilename(), name)
103766
105093
  .some(r => r.referenceType === _reference_1.ReferenceType.BuiltinMethodReference);
103767
- if (builtin && NUMERIC_FUNCTIONS.has(name.getStr().toLowerCase())) {
105094
+ if (builtin && _procedure_analysis_1.NUMERIC_FUNCTIONS.has(name.getStr().toLowerCase())) {
103768
105095
  return false;
103769
105096
  }
103770
105097
  }
@@ -107032,14 +108359,17 @@ class UnnecessaryCopy {
107032
108359
  extendedInformation: `Reports DATA(a) = b. when all of the following hold:
107033
108360
 
107034
108361
  * a is a local variable of a METHOD, FORM or FUNCTION, and has exactly one more reference: a read,
107035
- which does not take its address, bind a field symbol to it, or pass it to USING, CHANGING or EXPORTING
108362
+ which does not take its address, bind a field symbol to it, or pass it to USING or CHANGING
107036
108363
  * b is a plain data object: variable, constant, attribute, or a chain of components and attributes,
107037
108364
  without calls, offsets, table expressions, field symbols or ->*
107038
108365
  * the declaration is executed before the read on every path: the read is in the block of the declaration
107039
108366
  or a nested block, and both are in the same loop
107040
- * b keeps its value from the declaration until the read: for a local b, no write to it and its address is
107041
- never taken; for other b, in addition no method, function or form call and no write except to local
107042
- variables, by value parameters and RETURNING in between
108367
+ * b keeps its value from the declaration until the read: for a local b, or a pass-by-value or RETURNING
108368
+ parameter b of a method or form, no write to it and its address is never taken; for other b, in addition no
108369
+ method, function or form call and no write except to local variables, by value parameters and RETURNING in
108370
+ between, and in the statement of the read no call before the read. A method called in that statement could
108371
+ change b while it reads the parameter that b is passed to by reference, so the read may only be the receiver
108372
+ of the call, or the actual parameter of a pass-by-value parameter of the only method called in the statement
107043
108373
 
107044
108374
  a has the type of b, so reading b instead does not change any conversion.
107045
108375
  A constant b read in IF, ELSEIF, WHILE, CHECK, CASE or WHEN is not reported, see constant_condition.
@@ -113279,14 +114609,42 @@ exports.ArithOperatorTranspiler = ArithOperatorTranspiler;
113279
114609
  Object.defineProperty(exports, "__esModule", ({ value: true }));
113280
114610
  exports.AttributeChainTranspiler = void 0;
113281
114611
  const core_1 = __webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js");
114612
+ const traversal_1 = __webpack_require__(/*! ../traversal */ "./node_modules/@abaplint/transpiler/build/src/traversal.js");
113282
114613
  const chunk_1 = __webpack_require__(/*! ../chunk */ "./node_modules/@abaplint/transpiler/build/src/chunk.js");
114614
+ const attribute_name_1 = __webpack_require__(/*! ./attribute_name */ "./node_modules/@abaplint/transpiler/build/src/expressions/attribute_name.js");
113283
114615
  class AttributeChainTranspiler {
113284
114616
  transpile(node, traversal) {
113285
114617
  const ret = new chunk_1.Chunk();
114618
+ let arrow = "";
113286
114619
  for (const c of node.getChildren()) {
113287
114620
  if (c.get() instanceof core_1.Expressions.AttributeName) {
113288
114621
  ret.appendChunk(traversal.traverse(c));
113289
114622
  }
114623
+ else if (c.get() instanceof core_1.Expressions.ArrowOrDash) {
114624
+ arrow = c.concatTokens();
114625
+ if (arrow === "-" || arrow === "->") {
114626
+ ret.append(".get()", c, traversal);
114627
+ }
114628
+ }
114629
+ else if (c instanceof core_1.Nodes.TokenNode && c.getFirstToken().getStr() === "*") {
114630
+ ret.append(".dereference()", c, traversal);
114631
+ }
114632
+ else if (c instanceof core_1.Nodes.ExpressionNode && c.get() instanceof core_1.Expressions.ComponentName) {
114633
+ if (arrow === "-") {
114634
+ // structure component
114635
+ const name = c.getFirstToken().getStr().toLowerCase();
114636
+ if (traversal_1.Traversal.isBracketComponent(name)) {
114637
+ ret.append(`["` + name + `"]`, c, traversal);
114638
+ }
114639
+ else {
114640
+ ret.append("." + name, c, traversal);
114641
+ }
114642
+ }
114643
+ else {
114644
+ // attribute of an object, eg. "node->get_first_token( )->start->row"
114645
+ ret.appendString(".").appendChunk(new attribute_name_1.AttributeNameTranspiler().transpile(c, traversal));
114646
+ }
114647
+ }
113290
114648
  else {
113291
114649
  ret.append("AttributeChainTodo", node, traversal);
113292
114650
  }
@@ -113649,7 +115007,7 @@ class CompareTranspiler {
113649
115007
  return new chunk_1.Chunk().appendString(pre + "abap.compare.in(").join([s0, s1]).appendString(")");
113650
115008
  }
113651
115009
  else if (sources.length === 2) {
113652
- const operator = traversal.traverse(node.findFirstExpression(core_1.Expressions.CompareOperator));
115010
+ const operator = traversal.traverse(node.findDirectExpression(core_1.Expressions.CompareOperator));
113653
115011
  const s0 = traversal.traverse(sources[0]);
113654
115012
  const s1 = traversal.traverse(sources[1]);
113655
115013
  return new chunk_1.Chunk().appendString(pre + "abap.compare.").appendChunk(operator).appendString("(").join([s0, s1]).appendString(")");