@abaplint/core 2.120.77 → 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.
- package/build/src/registry.js +1 -1
- package/build/src/rules/index.js +2 -0
- package/build/src/rules/is_assigned_in_loop.js +242 -0
- package/build/src/rules/many_parentheses.js +5 -1
- package/build/src/rules/newline_between_methods.js +22 -3
- package/build/src/rules/redundant_cast.js +157 -0
- package/package.json +1 -1
package/build/src/registry.js
CHANGED
package/build/src/rules/index.js
CHANGED
|
@@ -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
|
|
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])
|
|
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
|
-
|
|
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
|