@abaplint/core 2.80.7 → 2.81.0
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/abap/2_statements/expressions/sql_function.js +8 -3
- package/build/src/abap/2_statements/statements/move.js +3 -2
- package/build/src/abap/5_syntax/expressions/field_assignment.js +12 -6
- package/build/src/abap/5_syntax/statements/get_cursor.js +14 -0
- package/build/src/abap/5_syntax/syntax.js +2 -0
- package/build/src/lsp/references.js +6 -5
- package/build/src/registry.js +1 -1
- package/build/src/rules/index.js +2 -0
- package/build/src/rules/no_chained_assignment.js +56 -0
- package/build/src/rules/prefer_inline.js +3 -2
- package/build/src/rules/unnecessary_chaining.js +62 -0
- package/build/src/rules/unused_types.js +23 -18
- package/build/src/rules/unused_variables.js +107 -60
- package/package.json +10 -20
|
@@ -9,10 +9,15 @@ const sql_alias_field_1 = require("./sql_alias_field");
|
|
|
9
9
|
const sql_field_name_1 = require("./sql_field_name");
|
|
10
10
|
class SQLFunction extends combi_1.Expression {
|
|
11
11
|
getRunnable() {
|
|
12
|
+
const param = (0, combi_1.altPrio)(sql_field_name_1.SQLFieldName, sql_alias_field_1.SQLAliasField);
|
|
12
13
|
const uuid = (0, combi_1.ver)(version_1.Version.v754, (0, combi_1.seq)("uuid", (0, combi_1.tok)(tokens_1.ParenLeftW), (0, combi_1.tok)(tokens_1.WParenRightW)));
|
|
13
|
-
const abs = (0, combi_1.ver)(version_1.Version.
|
|
14
|
-
const
|
|
15
|
-
|
|
14
|
+
const abs = (0, combi_1.ver)(version_1.Version.v740sp05, (0, combi_1.seq)("abs", (0, combi_1.tok)(tokens_1.ParenLeftW), param, (0, combi_1.tok)(tokens_1.WParenRightW)));
|
|
15
|
+
const ceil = (0, combi_1.ver)(version_1.Version.v740sp05, (0, combi_1.seq)("ceil", (0, combi_1.tok)(tokens_1.ParenLeftW), param, (0, combi_1.tok)(tokens_1.WParenRightW)));
|
|
16
|
+
const floor = (0, combi_1.ver)(version_1.Version.v740sp05, (0, combi_1.seq)("floor", (0, combi_1.tok)(tokens_1.ParenLeftW), param, (0, combi_1.tok)(tokens_1.WParenRightW)));
|
|
17
|
+
const div = (0, combi_1.ver)(version_1.Version.v740sp05, (0, combi_1.seq)("div", (0, combi_1.tok)(tokens_1.ParenLeftW), param, ",", param, (0, combi_1.tok)(tokens_1.WParenRightW)));
|
|
18
|
+
const mod = (0, combi_1.ver)(version_1.Version.v740sp05, (0, combi_1.seq)("mod", (0, combi_1.tok)(tokens_1.ParenLeftW), param, ",", param, (0, combi_1.tok)(tokens_1.WParenRightW)));
|
|
19
|
+
const cast = (0, combi_1.ver)(version_1.Version.v750, (0, combi_1.seq)("cast", (0, combi_1.tok)(tokens_1.ParenLeftW), param, "AS CHAR", (0, combi_1.tok)(tokens_1.ParenLeftW), integer_1.Integer, (0, combi_1.tok)(tokens_1.WParenRightW), (0, combi_1.tok)(tokens_1.WParenRightW)));
|
|
20
|
+
return (0, combi_1.altPrio)(uuid, abs, ceil, floor, cast, div, mod);
|
|
16
21
|
}
|
|
17
22
|
}
|
|
18
23
|
exports.SQLFunction = SQLFunction;
|
|
@@ -10,9 +10,10 @@ class Move {
|
|
|
10
10
|
const mov = (0, combi_1.verNot)(version_1.Version.Cloud, "MOVE");
|
|
11
11
|
const move = (0, combi_1.seq)(mov, (0, combi_1.altPrio)((0, combi_1.seq)("EXACT", expressions_1.Source, "TO", expressions_1.Target), (0, combi_1.seq)(expressions_1.Source, (0, combi_1.altPrio)("?TO", "TO"), expressions_1.Target)));
|
|
12
12
|
const calcAssign = (0, combi_1.ver)(version_1.Version.v754, (0, combi_1.alt)((0, combi_1.seq)((0, combi_1.tok)(tokens_1.WPlus), "="), (0, combi_1.seq)((0, combi_1.tok)(tokens_1.WDash), "="), "/=", "*=", "&&="));
|
|
13
|
-
const
|
|
13
|
+
const chained = (0, combi_1.seq)("=", (0, combi_1.star)((0, combi_1.seq)(expressions_1.Target, "=")));
|
|
14
|
+
const equals = (0, combi_1.altPrio)((0, combi_1.altPrio)(chained, "?="), calcAssign);
|
|
14
15
|
// todo, move "?=" to CAST?
|
|
15
|
-
const eq = (0, combi_1.seq)(
|
|
16
|
+
const eq = (0, combi_1.seq)(expressions_1.Target, equals, expressions_1.Source);
|
|
16
17
|
return (0, combi_1.altPrio)(move, eq);
|
|
17
18
|
}
|
|
18
19
|
}
|
|
@@ -6,9 +6,8 @@ const basic_1 = require("../../types/basic");
|
|
|
6
6
|
const source_1 = require("./source");
|
|
7
7
|
class FieldAssignment {
|
|
8
8
|
runSyntax(node, scope, filename, targetType) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (name === undefined) {
|
|
9
|
+
const fieldSub = node.findDirectExpression(Expressions.FieldSub);
|
|
10
|
+
if (fieldSub === undefined) {
|
|
12
11
|
throw new Error("FieldAssignment, FieldSub node not found");
|
|
13
12
|
}
|
|
14
13
|
const s = node.findDirectExpression(Expressions.Source);
|
|
@@ -17,10 +16,17 @@ class FieldAssignment {
|
|
|
17
16
|
}
|
|
18
17
|
let type = undefined;
|
|
19
18
|
if (targetType instanceof basic_1.StructureType) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
let context = targetType;
|
|
20
|
+
for (const c of fieldSub.getChildren()) {
|
|
21
|
+
const text = c.concatTokens();
|
|
22
|
+
if (text !== "-" && context instanceof basic_1.StructureType) {
|
|
23
|
+
context = context.getComponentByName(text);
|
|
24
|
+
if (context === undefined && targetType.containsVoid() === false) {
|
|
25
|
+
throw new Error(`field ${text} does not exist in structure`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
23
28
|
}
|
|
29
|
+
type = context;
|
|
24
30
|
}
|
|
25
31
|
else if (targetType instanceof basic_1.VoidType) {
|
|
26
32
|
type = targetType;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GetCursor = void 0;
|
|
4
|
+
const Expressions = require("../../2_statements/expressions");
|
|
5
|
+
const target_1 = require("../expressions/target");
|
|
6
|
+
class GetCursor {
|
|
7
|
+
runSyntax(node, scope, filename) {
|
|
8
|
+
for (const t of node.findAllExpressions(Expressions.Target)) {
|
|
9
|
+
new target_1.Target().runSyntax(t, scope, filename);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.GetCursor = GetCursor;
|
|
14
|
+
//# sourceMappingURL=get_cursor.js.map
|
|
@@ -129,6 +129,7 @@ const modify_screen_1 = require("./statements/modify_screen");
|
|
|
129
129
|
const delete_cluster_1 = require("./statements/delete_cluster");
|
|
130
130
|
const unassign_1 = require("./statements/unassign");
|
|
131
131
|
const insert_textpool_1 = require("./statements/insert_textpool");
|
|
132
|
+
const get_cursor_1 = require("./statements/get_cursor");
|
|
132
133
|
// -----------------------------------
|
|
133
134
|
const map = {};
|
|
134
135
|
function addToMap(handler) {
|
|
@@ -148,6 +149,7 @@ if (Object.keys(map).length === 0) {
|
|
|
148
149
|
addToMap(new move_1.Move());
|
|
149
150
|
addToMap(new get_badi_1.GetBadi());
|
|
150
151
|
addToMap(new call_badi_1.CallBadi());
|
|
152
|
+
addToMap(new get_cursor_1.GetCursor());
|
|
151
153
|
addToMap(new replace_1.Replace());
|
|
152
154
|
addToMap(new truncate_dataset_1.TruncateDataset());
|
|
153
155
|
addToMap(new assert_1.Assert());
|
|
@@ -30,12 +30,12 @@ class References {
|
|
|
30
30
|
const locs = this.search(lookup.definitionId, lookup.scope);
|
|
31
31
|
return locs.map(_lsp_utils_1.LSPUtils.identiferToLocation);
|
|
32
32
|
}
|
|
33
|
+
// todo, cleanup this mehtod, some of the method parameters are not used anymore?
|
|
33
34
|
search(identifier, node, exitAfterFound = false, removeDuplicates = true) {
|
|
34
35
|
let ret = [];
|
|
35
36
|
// todo, this first assumes that the identifier is a variable?
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|| node.getIdentifier().stype === _scope_type_1.ScopeType.Form) {
|
|
37
|
+
const stype = node.getIdentifier().stype;
|
|
38
|
+
if (stype === _scope_type_1.ScopeType.Method || stype === _scope_type_1.ScopeType.FunctionModule || stype === _scope_type_1.ScopeType.Form) {
|
|
39
39
|
ret = this.findReferences(node, identifier);
|
|
40
40
|
}
|
|
41
41
|
if (ret.length > 1 && exitAfterFound === true) {
|
|
@@ -71,15 +71,16 @@ class References {
|
|
|
71
71
|
var _a;
|
|
72
72
|
const ret = [];
|
|
73
73
|
if (node.getIdentifier().stype !== _scope_type_1.ScopeType.BuiltIn) {
|
|
74
|
+
const upper = identifier.getName().toUpperCase();
|
|
74
75
|
// this is for finding the definitions
|
|
75
76
|
const vars = node.getData().vars;
|
|
76
|
-
const vid = vars[
|
|
77
|
+
const vid = vars[upper];
|
|
77
78
|
if (vid === null || vid === void 0 ? void 0 : vid.equals(identifier)) {
|
|
78
79
|
ret.push(vid);
|
|
79
80
|
}
|
|
80
81
|
// this is for finding the definitions
|
|
81
82
|
const types = node.getData().types;
|
|
82
|
-
const tid = types[
|
|
83
|
+
const tid = types[upper];
|
|
83
84
|
if (tid === null || tid === void 0 ? void 0 : tid.equals(identifier)) {
|
|
84
85
|
ret.push(tid);
|
|
85
86
|
}
|
package/build/src/registry.js
CHANGED
package/build/src/rules/index.js
CHANGED
|
@@ -92,6 +92,7 @@ __exportStar(require("./msag_consistency"), exports);
|
|
|
92
92
|
__exportStar(require("./names_no_dash"), exports);
|
|
93
93
|
__exportStar(require("./nesting"), exports);
|
|
94
94
|
__exportStar(require("./newline_between_methods"), exports);
|
|
95
|
+
__exportStar(require("./no_chained_assignment"), exports);
|
|
95
96
|
__exportStar(require("./no_public_attributes"), exports);
|
|
96
97
|
__exportStar(require("./no_yoda_conditions"), exports);
|
|
97
98
|
__exportStar(require("./object_naming"), exports);
|
|
@@ -131,6 +132,7 @@ __exportStar(require("./type_form_parameters"), exports);
|
|
|
131
132
|
__exportStar(require("./types_naming"), exports);
|
|
132
133
|
__exportStar(require("./uncaught_exception"), exports);
|
|
133
134
|
__exportStar(require("./unknown_types"), exports);
|
|
135
|
+
__exportStar(require("./unnecessary_chaining"), exports);
|
|
134
136
|
__exportStar(require("./unreachable_code"), exports);
|
|
135
137
|
__exportStar(require("./unsecure_fae"), exports);
|
|
136
138
|
__exportStar(require("./unused_ddic"), exports);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoChainedAssignment = exports.NoChainedAssignmentConf = void 0;
|
|
4
|
+
const Expressions = require("../abap/2_statements/expressions");
|
|
5
|
+
const Statements = require("../abap/2_statements/statements");
|
|
6
|
+
const issue_1 = require("../issue");
|
|
7
|
+
const _abap_rule_1 = require("./_abap_rule");
|
|
8
|
+
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
9
|
+
const _irule_1 = require("./_irule");
|
|
10
|
+
class NoChainedAssignmentConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
11
|
+
constructor() {
|
|
12
|
+
super(...arguments);
|
|
13
|
+
this.onlyConstants = false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.NoChainedAssignmentConf = NoChainedAssignmentConf;
|
|
17
|
+
class NoChainedAssignment extends _abap_rule_1.ABAPRule {
|
|
18
|
+
constructor() {
|
|
19
|
+
super(...arguments);
|
|
20
|
+
this.conf = new NoChainedAssignmentConf();
|
|
21
|
+
}
|
|
22
|
+
getMetadata() {
|
|
23
|
+
return {
|
|
24
|
+
key: "no_chained_assignment",
|
|
25
|
+
title: "No chained assignment",
|
|
26
|
+
shortDescription: `Find chained assingments and reports issues`,
|
|
27
|
+
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#dont-chain-assignments`,
|
|
28
|
+
tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Styleguide],
|
|
29
|
+
badExample: `var1 = var2 = var3.`,
|
|
30
|
+
goodExample: `var2 = var3.
|
|
31
|
+
var1 = var3.`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
getConfig() {
|
|
35
|
+
return this.conf;
|
|
36
|
+
}
|
|
37
|
+
setConfig(conf) {
|
|
38
|
+
this.conf = conf;
|
|
39
|
+
}
|
|
40
|
+
runParsed(file) {
|
|
41
|
+
const issues = [];
|
|
42
|
+
for (const s of file.getStatements()) {
|
|
43
|
+
if (!(s.get() instanceof Statements.Move)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (s.findDirectExpressions(Expressions.Target).length >= 2) {
|
|
47
|
+
const message = "No chained assignment";
|
|
48
|
+
const issue = issue_1.Issue.atStatement(file, s, message, this.getMetadata().key);
|
|
49
|
+
issues.push(issue);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return issues;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.NoChainedAssignment = NoChainedAssignment;
|
|
56
|
+
//# sourceMappingURL=no_chained_assignment.js.map
|
|
@@ -74,7 +74,7 @@ DATA(percentage) = CONV decfloat34( comment_number / abs_statement_number ) * 10
|
|
|
74
74
|
}
|
|
75
75
|
///////////////////////////
|
|
76
76
|
analyzeScope(node, obj) {
|
|
77
|
-
var _a;
|
|
77
|
+
var _a, _b;
|
|
78
78
|
const ret = [];
|
|
79
79
|
const vars = node.getData().vars;
|
|
80
80
|
for (const name in vars) {
|
|
@@ -113,7 +113,8 @@ DATA(percentage) = CONV decfloat34( comment_number / abs_statement_number ) * 10
|
|
|
113
113
|
|| statementType instanceof Statements.Catch
|
|
114
114
|
|| statementType instanceof Statements.ReadTable
|
|
115
115
|
|| statementType instanceof Statements.Loop)
|
|
116
|
-
|| ((_a = writeStatement === null || writeStatement === void 0 ? void 0 : writeStatement.concatTokens()) === null || _a === void 0 ? void 0 : _a.includes("?="))
|
|
116
|
+
|| ((_a = writeStatement === null || writeStatement === void 0 ? void 0 : writeStatement.concatTokens()) === null || _a === void 0 ? void 0 : _a.includes("?="))
|
|
117
|
+
|| ((_b = writeStatement === null || writeStatement === void 0 ? void 0 : writeStatement.concatTokens()) === null || _b === void 0 ? void 0 : _b.includes(" #("))) {
|
|
117
118
|
continue;
|
|
118
119
|
}
|
|
119
120
|
const statement = edit_helper_1.EditHelper.findStatement(identifier.getToken(), file);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnnecessaryChaining = exports.UnnecessaryChainingConf = void 0;
|
|
4
|
+
const issue_1 = require("../issue");
|
|
5
|
+
const _abap_rule_1 = require("./_abap_rule");
|
|
6
|
+
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
7
|
+
const _irule_1 = require("./_irule");
|
|
8
|
+
class UnnecessaryChainingConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
9
|
+
constructor() {
|
|
10
|
+
super(...arguments);
|
|
11
|
+
this.onlyConstants = false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.UnnecessaryChainingConf = UnnecessaryChainingConf;
|
|
15
|
+
class UnnecessaryChaining extends _abap_rule_1.ABAPRule {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
this.conf = new UnnecessaryChainingConf();
|
|
19
|
+
}
|
|
20
|
+
getMetadata() {
|
|
21
|
+
return {
|
|
22
|
+
key: "unnecessary_chaining",
|
|
23
|
+
title: "Unnecessary Chaining",
|
|
24
|
+
shortDescription: `Find unnecessary chaining, all statements are checked`,
|
|
25
|
+
extendedInformation: ``,
|
|
26
|
+
tags: [_irule_1.RuleTag.SingleFile],
|
|
27
|
+
badExample: `WRITE: bar.`,
|
|
28
|
+
goodExample: `WRITE bar.`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
getConfig() {
|
|
32
|
+
return this.conf;
|
|
33
|
+
}
|
|
34
|
+
setConfig(conf) {
|
|
35
|
+
this.conf = conf;
|
|
36
|
+
}
|
|
37
|
+
runParsed(file) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const issues = [];
|
|
40
|
+
const statements = file.getStatements();
|
|
41
|
+
for (let i = 0; i < statements.length; i++) {
|
|
42
|
+
const colon = statements[i].getColon();
|
|
43
|
+
if (colon === undefined) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const next = (_a = statements[i + 1]) === null || _a === void 0 ? void 0 : _a.getColon();
|
|
47
|
+
const prev = (_b = statements[i - 1]) === null || _b === void 0 ? void 0 : _b.getColon();
|
|
48
|
+
if (next !== undefined && colon.getStart().equals(next.getStart())) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
else if (prev !== undefined && colon.getStart().equals(prev.getStart())) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const message = "Unnecessary chaining";
|
|
55
|
+
const issue = issue_1.Issue.atStatement(file, statements[i], message, this.getMetadata().key);
|
|
56
|
+
issues.push(issue);
|
|
57
|
+
}
|
|
58
|
+
return issues;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.UnnecessaryChaining = UnnecessaryChaining;
|
|
62
|
+
//# sourceMappingURL=unnecessary_chaining.js.map
|
|
@@ -36,23 +36,25 @@ class WorkArea {
|
|
|
36
36
|
return this.workarea.length;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
39
|
+
/*
|
|
40
|
+
function removeDuplicates(list: readonly TypedIdentifier[]): readonly TypedIdentifier[] {
|
|
41
|
+
const deduplicated: TypedIdentifier[] = [];
|
|
42
|
+
for (const result of list) {
|
|
43
|
+
let cont = false;
|
|
44
|
+
for (const d of deduplicated) {
|
|
45
|
+
if (result.getStart().equals(d.getStart())) {
|
|
46
|
+
cont = true;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (cont === true) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
deduplicated.push(result);
|
|
54
|
+
}
|
|
55
|
+
return deduplicated;
|
|
55
56
|
}
|
|
57
|
+
*/
|
|
56
58
|
class UnusedTypesConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
57
59
|
constructor() {
|
|
58
60
|
super(...arguments);
|
|
@@ -103,7 +105,10 @@ class UnusedTypes {
|
|
|
103
105
|
return []; // exit early if all types are used in the current object
|
|
104
106
|
}
|
|
105
107
|
for (const o of this.reg.getObjects()) {
|
|
106
|
-
if (o
|
|
108
|
+
if (o === obj) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
else if (o instanceof _abap_object_1.ABAPObject) {
|
|
107
112
|
if (this.reg.isDependency(o)) {
|
|
108
113
|
continue; // do not search in dependencies
|
|
109
114
|
}
|
|
@@ -116,7 +121,7 @@ class UnusedTypes {
|
|
|
116
121
|
}
|
|
117
122
|
// what is left is unused
|
|
118
123
|
const ret = [];
|
|
119
|
-
for (const t of
|
|
124
|
+
for (const t of this.workarea.get()) {
|
|
120
125
|
const message = "Type \"" + t.getName() + "\" not used";
|
|
121
126
|
const file = obj.getABAPFileByName(t.getFilename());
|
|
122
127
|
if (file === undefined) {
|
|
@@ -8,10 +8,10 @@ const syntax_1 = require("../abap/5_syntax/syntax");
|
|
|
8
8
|
const _abap_object_1 = require("../objects/_abap_object");
|
|
9
9
|
const _scope_type_1 = require("../abap/5_syntax/_scope_type");
|
|
10
10
|
const objects_1 = require("../objects");
|
|
11
|
-
const references_1 = require("../lsp/references");
|
|
12
11
|
const edit_helper_1 = require("../edit_helper");
|
|
13
12
|
const Statements = require("../abap/2_statements/statements");
|
|
14
13
|
const _statement_1 = require("../abap/2_statements/statements/_statement");
|
|
14
|
+
const _reference_1 = require("../abap/5_syntax/_reference");
|
|
15
15
|
class UnusedVariablesConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
16
16
|
constructor() {
|
|
17
17
|
super(...arguments);
|
|
@@ -22,6 +22,39 @@ class UnusedVariablesConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
exports.UnusedVariablesConf = UnusedVariablesConf;
|
|
25
|
+
class WorkArea {
|
|
26
|
+
constructor() {
|
|
27
|
+
this.workarea = [];
|
|
28
|
+
}
|
|
29
|
+
push(id, count = 1) {
|
|
30
|
+
for (const w of this.workarea) {
|
|
31
|
+
if (id.equals(w.id)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
this.workarea.push({ id, count });
|
|
36
|
+
}
|
|
37
|
+
removeIfExists(id) {
|
|
38
|
+
if (id === undefined) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (let i = 0; i < this.workarea.length; i++) {
|
|
42
|
+
if (id.equals(this.workarea[i].id)) {
|
|
43
|
+
this.workarea[i].count--;
|
|
44
|
+
if (this.workarea[i].count === 0) {
|
|
45
|
+
this.workarea.splice(i, 1);
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
get() {
|
|
52
|
+
return this.workarea;
|
|
53
|
+
}
|
|
54
|
+
count() {
|
|
55
|
+
return this.workarea.length;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
25
58
|
class UnusedVariables {
|
|
26
59
|
constructor() {
|
|
27
60
|
this.conf = new UnusedVariablesConf();
|
|
@@ -33,11 +66,11 @@ class UnusedVariables {
|
|
|
33
66
|
shortDescription: `Checks for unused variables and constants`,
|
|
34
67
|
extendedInformation: `WARNING: slow
|
|
35
68
|
|
|
36
|
-
|
|
69
|
+
Experimental, might give false positives. Skips event parameters.
|
|
37
70
|
|
|
38
|
-
|
|
71
|
+
Note that this currently does not work if the source code uses macros.
|
|
39
72
|
|
|
40
|
-
|
|
73
|
+
Unused variables are not reported if the object contains syntax errors. Errors found in INCLUDES are reported for the main program.`,
|
|
41
74
|
tags: [_irule_1.RuleTag.Quickfix],
|
|
42
75
|
pragma: "##NEEDED",
|
|
43
76
|
pseudoComment: "EC NEEDED",
|
|
@@ -68,67 +101,91 @@ class UnusedVariables {
|
|
|
68
101
|
if (syntax.issues.length > 0) {
|
|
69
102
|
return [];
|
|
70
103
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
if (cont === true) {
|
|
104
|
+
this.workarea = new WorkArea();
|
|
105
|
+
const top = syntax.spaghetti.getTop();
|
|
106
|
+
this.buildWorkarea(top, obj);
|
|
107
|
+
if (this.workarea.count() === 0) {
|
|
108
|
+
return this.buildIssues(obj); // exit early if all types are used
|
|
109
|
+
}
|
|
110
|
+
this.findUses(top, obj);
|
|
111
|
+
for (const o of this.reg.getObjects()) {
|
|
112
|
+
if (o === obj) {
|
|
83
113
|
continue;
|
|
84
114
|
}
|
|
85
|
-
|
|
115
|
+
else if (o instanceof _abap_object_1.ABAPObject) {
|
|
116
|
+
if (this.reg.isDependency(o)) {
|
|
117
|
+
continue; // do not search in dependencies
|
|
118
|
+
}
|
|
119
|
+
const syntax = new syntax_1.SyntaxLogic(this.reg, o).run();
|
|
120
|
+
this.findUses(syntax.spaghetti.getTop(), o);
|
|
121
|
+
if (this.workarea.count() === 0) {
|
|
122
|
+
return this.buildIssues(obj); // exit early if all types are used
|
|
123
|
+
}
|
|
124
|
+
}
|
|
86
125
|
}
|
|
87
|
-
return
|
|
126
|
+
return this.buildIssues(obj);
|
|
88
127
|
}
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
128
|
+
findUses(node, obj) {
|
|
129
|
+
for (const r of node.getData().references) {
|
|
130
|
+
if (r.referenceType === _reference_1.ReferenceType.DataReadReference
|
|
131
|
+
|| r.referenceType === _reference_1.ReferenceType.DataWriteReference
|
|
132
|
+
|| r.referenceType === _reference_1.ReferenceType.TypeReference) {
|
|
133
|
+
this.workarea.removeIfExists(r.resolved);
|
|
134
|
+
}
|
|
96
135
|
}
|
|
97
136
|
for (const c of node.getChildren()) {
|
|
98
|
-
|
|
137
|
+
this.findUses(c, obj);
|
|
99
138
|
}
|
|
100
|
-
return ret;
|
|
101
139
|
}
|
|
102
|
-
|
|
140
|
+
buildWorkarea(node, obj) {
|
|
103
141
|
var _a;
|
|
142
|
+
const stype = node.getIdentifier().stype;
|
|
143
|
+
if (stype === _scope_type_1.ScopeType.OpenSQL) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
for (const c of node.getChildren()) {
|
|
147
|
+
this.buildWorkarea(c, obj);
|
|
148
|
+
}
|
|
149
|
+
if (stype !== _scope_type_1.ScopeType.BuiltIn) {
|
|
150
|
+
const vars = node.getData().vars;
|
|
151
|
+
for (const name in vars) {
|
|
152
|
+
const meta = vars[name].getMeta();
|
|
153
|
+
if (((_a = this.conf.skipNames) === null || _a === void 0 ? void 0 : _a.length) > 0
|
|
154
|
+
&& this.conf.skipNames.some((a) => a.toUpperCase() === name)) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
else if (name === "ME"
|
|
158
|
+
|| name === "SUPER"
|
|
159
|
+
|| meta.includes("event_parameter" /* EventParameter */)) {
|
|
160
|
+
// todo, workaround for "me" and "super", these should somehow be typed to built-in
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const isInline = meta.includes("inline" /* InlineDefinition */);
|
|
164
|
+
this.workarea.push(vars[name], isInline ? 2 : 1);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
buildIssues(obj) {
|
|
104
169
|
const ret = [];
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
&& this.conf.skipNames.some((a) => a.toUpperCase() === name)) {
|
|
170
|
+
for (const w of this.workarea.get()) {
|
|
171
|
+
const filename = w.id.getFilename();
|
|
172
|
+
if (this.reg.isFileDependency(filename) === true) {
|
|
109
173
|
continue;
|
|
110
174
|
}
|
|
111
|
-
if (
|
|
112
|
-
|| name === "SUPER"
|
|
113
|
-
|| vars[name].getMeta().includes("event_parameter" /* EventParameter */)) {
|
|
114
|
-
// todo, workaround for "me" and "super", these should somehow be typed to built-in
|
|
175
|
+
else if (obj instanceof objects_1.Program === false && obj.containsFile(filename) === false) {
|
|
115
176
|
continue;
|
|
116
177
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
&& this.isUsed(vars[name], node) === false) {
|
|
121
|
-
const message = "Variable \"" + name.toLowerCase() + "\" not used";
|
|
122
|
-
const statement = this.findStatement(vars[name]);
|
|
123
|
-
if (statement === null || statement === void 0 ? void 0 : statement.getPragmas().map(t => t.getStr()).includes(this.getMetadata().pragma + "")) {
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
else if (this.suppressedbyPseudo(statement, vars[name], obj)) {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
const fix = this.buildFix(vars[name], obj);
|
|
130
|
-
ret.push(issue_1.Issue.atIdentifier(vars[name], message, this.getMetadata().key, this.conf.severity, fix));
|
|
178
|
+
const statement = this.findStatement(w.id);
|
|
179
|
+
if (statement === null || statement === void 0 ? void 0 : statement.getPragmas().map(t => t.getStr()).includes(this.getMetadata().pragma + "")) {
|
|
180
|
+
continue;
|
|
131
181
|
}
|
|
182
|
+
else if (this.suppressedbyPseudo(statement, w.id, obj)) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const name = w.id.getName();
|
|
186
|
+
const message = "Variable \"" + name.toLowerCase() + "\" not used";
|
|
187
|
+
const fix = this.buildFix(w.id, obj);
|
|
188
|
+
ret.push(issue_1.Issue.atIdentifier(w.id, message, this.getMetadata().key, this.conf.severity, fix));
|
|
132
189
|
}
|
|
133
190
|
return ret;
|
|
134
191
|
}
|
|
@@ -151,16 +208,6 @@ class UnusedVariables {
|
|
|
151
208
|
}
|
|
152
209
|
return false;
|
|
153
210
|
}
|
|
154
|
-
isUsed(id, node) {
|
|
155
|
-
const isInline = id.getMeta().includes("inline" /* InlineDefinition */);
|
|
156
|
-
const found = new references_1.References(this.reg).search(id, node, true, isInline === false);
|
|
157
|
-
if (isInline === true) {
|
|
158
|
-
return found.length > 2; // inline definitions are always written to
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
return found.length > 1;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
211
|
findStatement(v) {
|
|
165
212
|
const file = this.reg.getFileByName(v.getFilename());
|
|
166
213
|
if (file === undefined) {
|
package/package.json
CHANGED
|
@@ -1,29 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abaplint/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.81.0",
|
|
4
4
|
"description": "abaplint - Core API",
|
|
5
5
|
"main": "build/src/index.js",
|
|
6
6
|
"typings": "build/abaplint.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"madge.orphans": "madge --extensions ts --ts-config tsconfig.json --orphans src/",
|
|
9
|
-
"madge.svg": "madge --extensions ts --ts-config tsconfig.json --image graph.svg src/",
|
|
10
|
-
"madge.circular": "madge --extensions ts --ts-config tsconfig.json --circular src/",
|
|
11
8
|
"lint": "eslint src/**/*.ts test/**/*.ts --format unix",
|
|
12
9
|
"lint:fix": "eslint src/**/*.ts test/**/*.ts --format unix --fix",
|
|
13
|
-
"compile": "tsc",
|
|
14
|
-
"
|
|
15
|
-
"pretest": "npm run compile",
|
|
16
|
-
"test": "mocha --timeout 1000",
|
|
10
|
+
"compile": "tsc && sh scripts/version.sh",
|
|
11
|
+
"test": "npm run compile && mocha --timeout 1000 && npm run lint && npm run schema && api-extractor run",
|
|
17
12
|
"test:only": "npm run compile && mocha",
|
|
18
13
|
"test:parallel": "npm run compile && mocha --timeout 1000 --parallel --reporter dot",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"postcoverage": "c8 report --reporter=html",
|
|
24
|
-
"preschema": "node scripts/schema.js > scripts/schema.ts",
|
|
25
|
-
"schema": "ts-json-schema-generator --tsconfig tsconfig_schema.json --jsDoc extended --path scripts/schema.ts > scripts/schema.json",
|
|
26
|
-
"postschema": "node scripts/schema_post.js"
|
|
14
|
+
"coverage": "npm run compile && c8 mocha && c8 report --reporter=html",
|
|
15
|
+
"schema": "node scripts/schema.js > scripts/schema.ts && ts-json-schema-generator --tsconfig tsconfig_schema.json --jsDoc extended --path scripts/schema.ts > scripts/schema.json && node scripts/schema_post.js",
|
|
16
|
+
"publish:minor": "npm --no-git-tag-version version minor && rm -rf build && npm install && npm run test && npm publish --access public",
|
|
17
|
+
"publish:patch": "npm --no-git-tag-version version patch && rm -rf build && npm install && npm run test && npm publish --access public"
|
|
27
18
|
},
|
|
28
19
|
"mocha": {
|
|
29
20
|
"recursive": true,
|
|
@@ -54,13 +45,12 @@
|
|
|
54
45
|
},
|
|
55
46
|
"homepage": "https://abaplint.org",
|
|
56
47
|
"devDependencies": {
|
|
57
|
-
"@microsoft/api-extractor": "^7.18.
|
|
48
|
+
"@microsoft/api-extractor": "^7.18.19",
|
|
58
49
|
"@types/chai": "^4.2.22",
|
|
59
50
|
"@types/mocha": "^9.0.0",
|
|
60
|
-
"@types/node": "^16.11.
|
|
51
|
+
"@types/node": "^16.11.7",
|
|
61
52
|
"chai": "^4.3.4",
|
|
62
|
-
"eslint": "^8.
|
|
63
|
-
"madge": "^5.0.1",
|
|
53
|
+
"eslint": "^8.2.0",
|
|
64
54
|
"mocha": "^9.1.3",
|
|
65
55
|
"c8": "^7.10.0",
|
|
66
56
|
"source-map-support": "^0.5.20",
|