@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.
- package/build/bundle.js +1449 -91
- 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(
|
|
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(
|
|
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 = " ".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() ===
|
|
61684
|
-
&& coverage.end.getCol() ===
|
|
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.
|
|
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, "")) ||
|
|
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
|
|
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 (
|
|
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.
|
|
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,
|
|
81112
|
-
|
|
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 (
|
|
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 (
|
|
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
|
|
82227
|
+
extendedInformation: `Reports a local variable that only passes a value on, in two cases.
|
|
81477
82228
|
|
|
81478
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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
|
|
103674
|
-
extendedInformation: `Reports DATA(
|
|
103675
|
-
|
|
103676
|
-
|
|
103677
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
|
103720
|
-
const
|
|
103721
|
-
|
|
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
|
|
103732
|
-
if (
|
|
103733
|
-
|| (
|
|
103734
|
-
||
|
|
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
|
|
103740
|
-
|
|
103741
|
-
(0, _procedure_analysis_1.deleteStatement)(procedure.file,
|
|
103742
|
-
|
|
103743
|
-
const
|
|
103744
|
-
|
|
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 (
|
|
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 = (
|
|
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
|
|
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,
|
|
107041
|
-
|
|
107042
|
-
variables, by value parameters and RETURNING in
|
|
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.
|
|
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(")");
|