@abaplint/core 2.120.76 → 2.120.78

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.
@@ -74,7 +74,7 @@ const version_1 = require("../../../version");
74
74
  // TODO: refactor all these method parameters to objects, this is getting messy
75
75
  class Source {
76
76
  static runSyntax(node, input, targetType, writeReference = false, allowGenericDeference = false, readReference = true) {
77
- var _a, _b, _c, _d;
77
+ var _a, _b;
78
78
  if (node === undefined) {
79
79
  return undefined;
80
80
  }
@@ -169,13 +169,7 @@ class Source {
169
169
  this.traverseRemainingChildren(children, input);
170
170
  return bodyType;
171
171
  }
172
- const bodySource = (_b = node.findDirectExpression(Expressions.ConvBody)) === null || _b === void 0 ? void 0 : _b.findDirectExpression(Expressions.Source);
173
- const nestedType = (_c = bodySource === null || bodySource === void 0 ? void 0 : bodySource.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _c === void 0 ? void 0 : _c.concatTokens();
174
- // CONV # passes its target type into a nested inferred constructor, so
175
- // showing both inferred types would produce duplicate inlay hints.
176
- if (!(inferred === "#" && nestedType === "#")) {
177
- this.addIfInferred(node, input, foundType);
178
- }
172
+ this.addIfInferred(node, input, foundType);
179
173
  this.traverseRemainingChildren(children, input);
180
174
  return foundType;
181
175
  }
@@ -233,7 +227,7 @@ class Source {
233
227
  {
234
228
  const foundType = this.determineType(node, input, targetType);
235
229
  if ((foundType === null || foundType === void 0 ? void 0 : foundType.isGeneric()) === true
236
- && ((_d = node.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _d === void 0 ? void 0 : _d.concatTokens()) === "#"
230
+ && ((_b = node.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _b === void 0 ? void 0 : _b.concatTokens()) === "#"
237
231
  && !(0, version_1.releaseAtLeast)(input.scope.getRelease(), version_1.Release.v756)
238
232
  && input.scope.getLanguageVersion() !== version_1.LanguageVersion.Cloud
239
233
  && !input.scope.getOpenABAP()) {
@@ -75,7 +75,7 @@ class Registry {
75
75
  }
76
76
  static abaplintVersion() {
77
77
  // magic, see build script "version.js"
78
- return "2.120.76";
78
+ return "2.120.78";
79
79
  }
80
80
  getDDICReferences() {
81
81
  return this.ddicReferences;
@@ -105,6 +105,7 @@ __exportStar(require("./inline_data_old_versions"), exports);
105
105
  __exportStar(require("./inline_packed_computation"), exports);
106
106
  __exportStar(require("./intf_referencing_clas"), exports);
107
107
  __exportStar(require("./invalid_table_index"), exports);
108
+ __exportStar(require("./is_assigned_in_loop"), exports);
108
109
  __exportStar(require("./keep_single_parameter_on_one_line"), exports);
109
110
  __exportStar(require("./keyword_case"), exports);
110
111
  __exportStar(require("./line_break_multiple_parameters"), exports);
@@ -170,6 +171,7 @@ __exportStar(require("./prefix_is_current_class"), exports);
170
171
  __exportStar(require("./range_row_values"), exports);
171
172
  __exportStar(require("./reduce_procedural_code"), exports);
172
173
  __exportStar(require("./reduce_string_templates"), exports);
174
+ __exportStar(require("./redundant_cast"), exports);
173
175
  __exportStar(require("./redundant_conversion"), exports);
174
176
  __exportStar(require("./release_idoc"), exports);
175
177
  __exportStar(require("./remove_descriptions"), exports);
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.IsAssignedInLoop = exports.IsAssignedInLoopConf = void 0;
37
+ const Statements = __importStar(require("../abap/2_statements/statements"));
38
+ const Expressions = __importStar(require("../abap/2_statements/expressions"));
39
+ const issue_1 = require("../issue");
40
+ const _basic_rule_config_1 = require("./_basic_rule_config");
41
+ const _abap_rule_1 = require("./_abap_rule");
42
+ const _irule_1 = require("./_irule");
43
+ const edit_helper_1 = require("../edit_helper");
44
+ const severity_1 = require("../severity");
45
+ class IsAssignedInLoopConf extends _basic_rule_config_1.BasicRuleConfig {
46
+ constructor() {
47
+ super(...arguments);
48
+ this.severity = severity_1.Severity.Warning;
49
+ }
50
+ }
51
+ exports.IsAssignedInLoopConf = IsAssignedInLoopConf;
52
+ class IsAssignedInLoop extends _abap_rule_1.ABAPRule {
53
+ constructor() {
54
+ super(...arguments);
55
+ this.conf = new IsAssignedInLoopConf();
56
+ }
57
+ getMetadata() {
58
+ return {
59
+ key: "is_assigned_in_loop",
60
+ title: "IS ASSIGNED after READ TABLE or ASSIGN in a loop",
61
+ shortDescription: `Reports IS [NOT] ASSIGNED after a READ TABLE ... ASSIGNING or ASSIGN inside a loop, without UNASSIGN before it`,
62
+ extendedInformation: `A failed READ TABLE ... ASSIGNING or ASSIGN keeps the previous binding of the field symbol. Inside a
63
+ loop, the field symbol still points to the row of an earlier iteration, so IS ASSIGNED is true
64
+ although the statement failed, and the code goes on with the wrong row.
65
+
66
+ Add UNASSIGN <fs> before the statement, inside the loop, or check sy-subrc after a READ TABLE.
67
+ The quick fix inserts the UNASSIGN; it is not offered for a field symbol declared inline in
68
+ the statement, or for a chained statement.
69
+
70
+ Not reported: a check that also reads sy-subrc, an ASSIGN that cannot fail (a static source
71
+ without dereference, table expression or dynamic part), and loops outside of the
72
+ method, form or function module of the statement. See also subrc_after_assign.`,
73
+ tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix],
74
+ badExample: `LOOP AT lt_cells INTO ls_cell.
75
+ READ TABLE lt_styles ASSIGNING <style> WITH KEY id = ls_cell-style.
76
+ IF <style> IS ASSIGNED.
77
+ ls_cell-format = <style>-format.
78
+ ENDIF.
79
+ ENDLOOP.`,
80
+ goodExample: `LOOP AT lt_cells INTO ls_cell.
81
+ UNASSIGN <style>.
82
+ READ TABLE lt_styles ASSIGNING <style> WITH KEY id = ls_cell-style.
83
+ IF <style> IS ASSIGNED.
84
+ ls_cell-format = <style>-format.
85
+ ENDIF.
86
+ ENDLOOP.`,
87
+ };
88
+ }
89
+ getConfig() {
90
+ return this.conf;
91
+ }
92
+ setConfig(conf) {
93
+ this.conf = conf;
94
+ }
95
+ runParsed(file) {
96
+ const issues = [];
97
+ const statements = file.getStatements();
98
+ for (let i = 0; i < statements.length; i++) {
99
+ const statement = statements[i];
100
+ const name = this.boundByFailable(statement);
101
+ if (name === undefined) {
102
+ continue;
103
+ }
104
+ const fs = name.toUpperCase();
105
+ const loop = this.innermostLoop(i, statements);
106
+ if (loop === undefined || this.unassignedBefore(loop, i, statements, fs) === true) {
107
+ continue;
108
+ }
109
+ const check = this.nextCheck(i, statements, fs);
110
+ if (check === undefined) {
111
+ continue;
112
+ }
113
+ const read = statement.get() instanceof Statements.ReadTable;
114
+ const message = `${read ? "READ TABLE" : "ASSIGN"} in a loop keeps the previous binding of ${name} when it fails, `
115
+ + `UNASSIGN ${name} before it` + (read ? " or check sy-subrc" : "");
116
+ const fix = this.buildFix(file, statement, name);
117
+ issues.push(issue_1.Issue.atStatement(file, check, message, this.getMetadata().key, this.conf.severity, fix));
118
+ }
119
+ return issues;
120
+ }
121
+ ////////////////
122
+ /** the field symbol bound by a READ TABLE ... ASSIGNING or an ASSIGN that can fail */
123
+ boundByFailable(statement) {
124
+ var _a, _b, _c;
125
+ const s = statement.get();
126
+ if (s instanceof Statements.ReadTable) {
127
+ const target = (_a = statement.findDirectExpression(Expressions.ReadTableTarget)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(Expressions.FSTarget);
128
+ return (_b = target === null || target === void 0 ? void 0 : target.findFirstExpression(Expressions.FieldSymbol)) === null || _b === void 0 ? void 0 : _b.concatTokens();
129
+ }
130
+ else if (s instanceof Statements.Assign) {
131
+ const source = statement.findDirectExpression(Expressions.AssignSource);
132
+ if (source === undefined) {
133
+ return undefined;
134
+ }
135
+ const first = source.getFirstToken().getStr().toUpperCase();
136
+ const failable = first === "COMPONENT"
137
+ || first === "TABLE"
138
+ || source.findFirstExpression(Expressions.Dynamic) !== undefined
139
+ || source.findFirstExpression(Expressions.Dereference) !== undefined
140
+ || source.findFirstExpression(Expressions.TableExpression) !== undefined
141
+ || statement.findDirectTokenByText("INCREMENT") !== undefined;
142
+ if (failable === false) {
143
+ return undefined;
144
+ }
145
+ const target = statement.findDirectExpression(Expressions.FSTarget);
146
+ return (_c = target === null || target === void 0 ? void 0 : target.findFirstExpression(Expressions.FieldSymbol)) === null || _c === void 0 ? void 0 : _c.concatTokens();
147
+ }
148
+ return undefined;
149
+ }
150
+ /** index of the innermost LOOP, DO, WHILE or SELECT loop around the statement, in the same block */
151
+ innermostLoop(index, statements) {
152
+ let depth = 0;
153
+ for (let i = index - 1; i >= 0 && this.startsBlock(statements[i]) === false; i--) {
154
+ if (this.endsLoop(statements[i])) {
155
+ depth++;
156
+ }
157
+ else if (this.startsLoop(statements[i])) {
158
+ if (depth === 0) {
159
+ return i;
160
+ }
161
+ depth--;
162
+ }
163
+ }
164
+ return undefined;
165
+ }
166
+ unassignedBefore(loop, index, statements, fs) {
167
+ for (let i = loop + 1; i < index; i++) {
168
+ if (statements[i].get() instanceof Statements.Unassign && this.mentions(statements[i], fs)) {
169
+ return true;
170
+ }
171
+ }
172
+ return false;
173
+ }
174
+ /** the first IS [NOT] ASSIGNED of the field symbol after the statement, before the end of the loop
175
+ * or another UNASSIGN or binding of it; a check that also reads sy-subrc is fine */
176
+ nextCheck(index, statements, fs) {
177
+ let depth = 0;
178
+ for (let i = index + 1; i < statements.length && this.startsBlock(statements[i]) === false; i++) {
179
+ const statement = statements[i];
180
+ if (this.startsLoop(statement)) {
181
+ depth++;
182
+ }
183
+ else if (this.endsLoop(statement)) {
184
+ if (depth === 0) {
185
+ return undefined;
186
+ }
187
+ depth--;
188
+ }
189
+ const concat = statement.concatTokens().toUpperCase();
190
+ if (concat.includes(fs + " IS ASSIGNED") || concat.includes(fs + " IS NOT ASSIGNED")) {
191
+ return concat.includes("SY-SUBRC") ? undefined : statement;
192
+ }
193
+ if ((statement.get() instanceof Statements.Unassign && this.mentions(statement, fs))
194
+ || this.binds(statement, fs)) {
195
+ return undefined;
196
+ }
197
+ }
198
+ return undefined;
199
+ }
200
+ mentions(statement, fs) {
201
+ return statement.findAllExpressions(Expressions.FieldSymbol).some(e => e.concatTokens().toUpperCase() === fs);
202
+ }
203
+ binds(statement, fs) {
204
+ return statement.findAllExpressions(Expressions.FSTarget)
205
+ .some(t => { var _a; return ((_a = t.findFirstExpression(Expressions.FieldSymbol)) === null || _a === void 0 ? void 0 : _a.concatTokens().toUpperCase()) === fs; });
206
+ }
207
+ startsLoop(statement) {
208
+ const s = statement.get();
209
+ return s instanceof Statements.Loop
210
+ || s instanceof Statements.Do
211
+ || s instanceof Statements.While
212
+ || s instanceof Statements.SelectLoop;
213
+ }
214
+ endsLoop(statement) {
215
+ const s = statement.get();
216
+ return s instanceof Statements.EndLoop
217
+ || s instanceof Statements.EndDo
218
+ || s instanceof Statements.EndWhile
219
+ || s instanceof Statements.EndSelect;
220
+ }
221
+ startsBlock(statement) {
222
+ const s = statement.get();
223
+ return s instanceof Statements.MethodImplementation
224
+ || s instanceof Statements.Form
225
+ || s instanceof Statements.FunctionModule
226
+ || s instanceof Statements.Module
227
+ || s instanceof Statements.EndMethod
228
+ || s instanceof Statements.EndForm
229
+ || s instanceof Statements.EndFunction
230
+ || s instanceof Statements.EndModule;
231
+ }
232
+ /** UNASSIGN on a line of its own before the statement, with the same indentation */
233
+ buildFix(file, statement, name) {
234
+ if (statement.getColon() !== undefined || statement.findFirstExpression(Expressions.InlineFS) !== undefined) {
235
+ return undefined;
236
+ }
237
+ const start = statement.getStart();
238
+ return edit_helper_1.EditHelper.insertAt(file, start, `UNASSIGN ${name}.\n` + " ".repeat(start.getCol() - 1));
239
+ }
240
+ }
241
+ exports.IsAssignedInLoop = IsAssignedInLoop;
242
+ //# sourceMappingURL=is_assigned_in_loop.js.map
@@ -144,7 +144,11 @@ ENDIF.
144
144
  && lastChildren[1].getChildren().length === 1
145
145
  && lastChildren[1].getFirstToken().getStr().startsWith("-") === false
146
146
  && lastChildren[2].getFirstToken().getStr() === ")") {
147
- const issue = issue_1.Issue.atToken(file, lastChildren[0].getFirstToken(), "Too many parentheses", this.getMetadata().key, this.conf.severity);
147
+ const fixText = lastChildren[1].concatTokens();
148
+ const start = lastChildren[0].getFirstToken().getStart();
149
+ const end = lastChildren[2].getLastToken().getEnd();
150
+ const fix = edit_helper_1.EditHelper.replaceRange(file, start, end, fixText);
151
+ const issue = issue_1.Issue.atToken(file, lastChildren[0].getFirstToken(), "Too many parentheses", this.getMetadata().key, this.conf.severity, fix);
148
152
  issues.push(issue);
149
153
  }
150
154
  }
@@ -40,6 +40,8 @@ const _abap_rule_1 = require("./_abap_rule");
40
40
  const sequential_blank_1 = require("./sequential_blank");
41
41
  const Statements = __importStar(require("../abap/2_statements/statements"));
42
42
  const _irule_1 = require("./_irule");
43
+ const edit_helper_1 = require("../edit_helper");
44
+ const position_1 = require("../position");
43
45
  var NewlineLogic;
44
46
  (function (NewlineLogic) {
45
47
  NewlineLogic["Exact"] = "exact";
@@ -69,7 +71,7 @@ class NewlineBetweenMethods extends _abap_rule_1.ABAPRule {
69
71
  key: "newline_between_methods",
70
72
  title: "New line between methods",
71
73
  shortDescription: `Checks for newlines between method implementations.`,
72
- tags: [_irule_1.RuleTag.Whitespace, _irule_1.RuleTag.SingleFile],
74
+ tags: [_irule_1.RuleTag.Whitespace, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.SingleFile],
73
75
  };
74
76
  }
75
77
  getMessage() {
@@ -98,18 +100,35 @@ class NewlineBetweenMethods extends _abap_rule_1.ABAPRule {
98
100
  continue;
99
101
  }
100
102
  let counter = 0;
101
- while (sequential_blank_1.SequentialBlank.isBlankOrWhitespace(rows[nextRow]) && (counter <= this.conf.count + 1)) {
103
+ while (sequential_blank_1.SequentialBlank.isBlankOrWhitespace(rows[nextRow])) {
102
104
  counter++;
103
105
  nextRow++;
104
106
  }
105
107
  if ((counter !== this.conf.count && this.conf.logic === NewlineLogic.Exact)
106
108
  || (counter >= this.conf.count && this.conf.logic === NewlineLogic.Less)
107
109
  || counter === 0) {
108
- issues.push(issue_1.Issue.atStatement(file, statement, this.getMessage(), this.getMetadata().key, this.conf.severity));
110
+ const fix = this.buildFix(file, rows, statement.getStart().getRow(), counter);
111
+ issues.push(issue_1.Issue.atStatement(file, statement, this.getMessage(), this.getMetadata().key, this.conf.severity, fix));
109
112
  }
110
113
  }
111
114
  return issues;
112
115
  }
116
+ buildFix(file, rows, row, blanks) {
117
+ let target = this.conf.count;
118
+ if (this.conf.logic === NewlineLogic.Less) {
119
+ target = blanks === 0 ? 1 : this.conf.count - 1;
120
+ }
121
+ if (blanks < target) {
122
+ const end = new position_1.Position(row, rows[row - 1].length + 1);
123
+ return edit_helper_1.EditHelper.insertAt(file, end, "\n".repeat(target - blanks));
124
+ }
125
+ else if (blanks > target) {
126
+ const start = new position_1.Position(row + target, rows[row + target - 1].length + 1);
127
+ const end = new position_1.Position(row + blanks, rows[row + blanks - 1].length + 1);
128
+ return edit_helper_1.EditHelper.deleteRange(file, start, end);
129
+ }
130
+ return undefined;
131
+ }
113
132
  isConfigurationValid() {
114
133
  if (this.conf.count < 1 || (this.conf.count === 1 && this.conf.logic === NewlineLogic.Less)) {
115
134
  return false;
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.RedundantCast = exports.RedundantCastConf = void 0;
37
+ const Expressions = __importStar(require("../abap/2_statements/expressions"));
38
+ const _reference_1 = require("../abap/5_syntax/_reference");
39
+ const syntax_1 = require("../abap/5_syntax/syntax");
40
+ const nodes_1 = require("../abap/nodes");
41
+ const _typed_identifier_1 = require("../abap/types/_typed_identifier");
42
+ const basic_1 = require("../abap/types/basic");
43
+ const issue_1 = require("../issue");
44
+ const _abap_object_1 = require("../objects/_abap_object");
45
+ const _basic_rule_config_1 = require("./_basic_rule_config");
46
+ const _irule_1 = require("./_irule");
47
+ const edit_helper_1 = require("../edit_helper");
48
+ const version_1 = require("../version");
49
+ class RedundantCastConf extends _basic_rule_config_1.BasicRuleConfig {
50
+ }
51
+ exports.RedundantCastConf = RedundantCastConf;
52
+ class RedundantCast {
53
+ constructor() {
54
+ this.conf = new RedundantCastConf();
55
+ }
56
+ getMetadata() {
57
+ return {
58
+ key: "redundant_cast",
59
+ title: "Redundant Cast",
60
+ shortDescription: `Find redundant CAST expressions`,
61
+ extendedInformation: `Reports CAST expressions whose operand is already a reference to the cast's target class or interface.`,
62
+ tags: [_irule_1.RuleTag.Quickfix],
63
+ badExample: `DATA ref TYPE REF TO lcl.
64
+ DATA(copy) = CAST lcl( ref ).`,
65
+ goodExample: `DATA ref TYPE REF TO lcl.
66
+ DATA(copy) = ref.`,
67
+ };
68
+ }
69
+ getConfig() {
70
+ return this.conf;
71
+ }
72
+ setConfig(conf) {
73
+ this.conf = conf;
74
+ }
75
+ initialize(reg) {
76
+ this.reg = reg;
77
+ return this;
78
+ }
79
+ run(obj) {
80
+ var _a;
81
+ if (!(0, version_1.releaseAtLeast)(this.reg.getConfig().getRelease(), version_1.Release.v740sp02)
82
+ && !this.reg.getConfig().isOpenABAP()) {
83
+ return [];
84
+ }
85
+ if (!(obj instanceof _abap_object_1.ABAPObject)) {
86
+ return [];
87
+ }
88
+ const syntax = new syntax_1.SyntaxLogic(this.reg, obj).run();
89
+ if (syntax.issues.length > 0) {
90
+ return [];
91
+ }
92
+ const issues = [];
93
+ for (const file of obj.getABAPFiles()) {
94
+ const structure = file.getStructure();
95
+ if (structure === undefined) {
96
+ continue;
97
+ }
98
+ for (const cast of structure.findAllExpressionsRecursive(Expressions.Cast)) {
99
+ const typeExpression = cast.findDirectExpression(Expressions.TypeNameOrInfer);
100
+ const source = cast.findDirectExpression(Expressions.Source);
101
+ if (typeExpression === undefined
102
+ || source === undefined
103
+ || cast.findDirectExpression(Expressions.Let) !== undefined) {
104
+ continue;
105
+ }
106
+ const scope = syntax.spaghetti.lookupPosition(cast.getFirstToken().getStart(), file.getFilename());
107
+ if (scope === undefined) {
108
+ continue;
109
+ }
110
+ const target = (_a = scope.getData().references.find(reference => reference.referenceType === _reference_1.ReferenceType.InferredType
111
+ && reference.position.getStart().equals(typeExpression.getFirstToken().getStart()))) === null || _a === void 0 ? void 0 : _a.resolved;
112
+ if (!(target instanceof _typed_identifier_1.TypedIdentifier)) {
113
+ continue;
114
+ }
115
+ const operandType = this.sourceType(source, scope);
116
+ if (operandType === undefined || this.sameType(target.getType(), operandType) === false) {
117
+ continue;
118
+ }
119
+ const closingParen = cast.getDirectTokens().find(token => token.getStr() === ")" && token.getStart().isAfter(source.getLastToken().getStart()));
120
+ if (closingParen === undefined) {
121
+ continue;
122
+ }
123
+ const fix = edit_helper_1.EditHelper.replaceRange(file, cast.getFirstToken().getStart(), closingParen.getEnd(), source.concatTokens());
124
+ issues.push(issue_1.Issue.atRange(file, cast.getFirstToken().getStart(), closingParen.getEnd(), "Redundant CAST expression", this.getMetadata().key, this.conf.severity, fix));
125
+ }
126
+ }
127
+ return issues;
128
+ }
129
+ /** The type of a plain variable or attribute, undefined for anything more complex */
130
+ sourceType(source, scope) {
131
+ var _a;
132
+ const children = source.getChildren();
133
+ if (children.length !== 1 || !(children[0].get() instanceof Expressions.FieldChain)) {
134
+ return undefined;
135
+ }
136
+ const fieldChain = children[0];
137
+ const last = fieldChain.getLastChild();
138
+ if (!(last instanceof nodes_1.ExpressionNode)
139
+ || !(last.get() instanceof Expressions.SourceField || last.get() instanceof Expressions.AttributeName)) {
140
+ return undefined;
141
+ }
142
+ const operand = (_a = scope.getData().references.find(reference => reference.referenceType === _reference_1.ReferenceType.DataReadReference
143
+ && reference.position.getStart().equals(last.getFirstToken().getStart()))) === null || _a === void 0 ? void 0 : _a.resolved;
144
+ if (!(operand instanceof _typed_identifier_1.TypedIdentifier)) {
145
+ return undefined;
146
+ }
147
+ return operand.getType();
148
+ }
149
+ sameType(first, second) {
150
+ if (!(first instanceof basic_1.ObjectReferenceType) || !(second instanceof basic_1.ObjectReferenceType)) {
151
+ return false;
152
+ }
153
+ return first.getIdentifierName().toUpperCase() === second.getIdentifierName().toUpperCase();
154
+ }
155
+ }
156
+ exports.RedundantCast = RedundantCast;
157
+ //# sourceMappingURL=redundant_cast.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.120.76",
3
+ "version": "2.120.78",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",