@abaplint/core 2.120.74 → 2.120.76
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/abaplint.d.ts +5 -1
- package/build/src/abap/2_statements/combi.js +143 -51
- package/build/src/abap/5_syntax/_builtin.js +8 -0
- package/build/src/abap/5_syntax/_loop_table_change.js +126 -0
- package/build/src/abap/5_syntax/expressions/_substring.js +83 -0
- package/build/src/abap/5_syntax/expressions/attribute_chain.js +3 -16
- package/build/src/abap/5_syntax/expressions/compare.js +33 -8
- package/build/src/abap/5_syntax/expressions/field_chain.js +5 -14
- package/build/src/abap/5_syntax/expressions/method_call_chain.js +16 -1
- package/build/src/abap/5_syntax/expressions/method_call_param.js +3 -0
- package/build/src/abap/5_syntax/expressions/target.js +14 -2
- package/build/src/abap/5_syntax/statements/append.js +6 -0
- package/build/src/abap/5_syntax/statements/read_table.js +39 -0
- package/build/src/abap/5_syntax/structures/cleanup.js +105 -0
- package/build/src/abap/5_syntax/syntax.js +13 -0
- package/build/src/ddic.js +1 -1
- package/build/src/objects/domain.js +4 -1
- package/build/src/registry.js +1 -1
- package/build/src/rules/abapdoc_html_tag.js +123 -0
- package/build/src/rules/abapdoc_leading_at.js +117 -0
- package/build/src/rules/abapdoc_parameter_names.js +149 -0
- package/build/src/rules/cds_check_syntax.js +4 -4
- package/build/src/rules/change_if_to_case.js +3 -2
- package/build/src/rules/changing_sorted_line.js +235 -0
- package/build/src/rules/class_attribute_names.js +15 -6
- package/build/src/rules/delete_index_in_loop.js +163 -0
- package/build/src/rules/final_newline.js +78 -0
- package/build/src/rules/forbidden_identifier.js +4 -3
- package/build/src/rules/forbidden_void_type.js +4 -9
- package/build/src/rules/index.js +11 -0
- package/build/src/rules/inline_packed_computation.js +220 -0
- package/build/src/rules/intf_referencing_clas.js +3 -1
- package/build/src/rules/line_only_punc.js +1 -1
- package/build/src/rules/local_class_naming.js +15 -2
- package/build/src/rules/local_variable_names.js +18 -9
- package/build/src/rules/macro_naming.js +3 -2
- package/build/src/rules/method_parameter_names.js +17 -7
- package/build/src/rules/modify_only_own_db_tables.js +3 -2
- package/build/src/rules/no_prefixes.js +25 -12
- package/build/src/rules/object_naming.js +18 -3
- package/build/src/rules/prefer_inline_cast.js +276 -0
- package/build/src/rules/range_row_values.js +200 -0
- package/build/src/rules/redundant_conversion.js +3 -0
- package/build/src/rules/selection_screen_naming.js +21 -2
- package/build/src/rules/sicf_consistency.js +1 -2
- package/build/src/rules/sort_empty_key.js +188 -0
- package/build/src/rules/subrc_after_assign.js +220 -0
- package/build/src/rules/types_naming.js +3 -2
- package/build/src/rules/use_message_class.js +64 -1
- package/build/src/rules/wrong_abapdoc_position.js +58 -11
- package/build/src/rules/xml_consistency.js +6 -2
- package/build/src/utils/name_validator.js +7 -3
- package/package.json +1 -1
|
@@ -23,6 +23,7 @@ class LocalClassNaming extends _abap_rule_1.ABAPRule {
|
|
|
23
23
|
constructor() {
|
|
24
24
|
super(...arguments);
|
|
25
25
|
this.conf = new LocalClassNamingConf();
|
|
26
|
+
this.regex = this.compile();
|
|
26
27
|
}
|
|
27
28
|
getMetadata() {
|
|
28
29
|
return {
|
|
@@ -46,6 +47,15 @@ ENDCLASS.`,
|
|
|
46
47
|
}
|
|
47
48
|
setConfig(conf) {
|
|
48
49
|
this.conf = conf;
|
|
50
|
+
this.regex = this.compile();
|
|
51
|
+
}
|
|
52
|
+
compile() {
|
|
53
|
+
return {
|
|
54
|
+
local: new RegExp(this.conf.local, "i"),
|
|
55
|
+
exception: new RegExp(this.conf.exception, "i"),
|
|
56
|
+
test: new RegExp(this.conf.test, "i"),
|
|
57
|
+
ignorePatterns: name_validator_1.NameValidator.compileIgnorePatterns(this.conf),
|
|
58
|
+
};
|
|
49
59
|
}
|
|
50
60
|
runParsed(file, obj) {
|
|
51
61
|
const issues = [];
|
|
@@ -59,20 +69,23 @@ ENDCLASS.`,
|
|
|
59
69
|
}
|
|
60
70
|
const className = classDef.name;
|
|
61
71
|
let expected;
|
|
72
|
+
let regex;
|
|
62
73
|
if (classDef.isForTesting) {
|
|
63
74
|
expected = this.conf.test;
|
|
75
|
+
regex = this.regex.test;
|
|
64
76
|
}
|
|
65
77
|
else if (ddic.isException(classDef, obj)) {
|
|
66
78
|
expected = this.conf.exception;
|
|
79
|
+
regex = this.regex.exception;
|
|
67
80
|
}
|
|
68
81
|
else {
|
|
69
82
|
expected = this.conf.local;
|
|
83
|
+
regex = this.regex.local;
|
|
70
84
|
}
|
|
71
85
|
if (expected === undefined || expected.length === 0) {
|
|
72
86
|
continue;
|
|
73
87
|
}
|
|
74
|
-
|
|
75
|
-
if (name_validator_1.NameValidator.violatesRule(className, regex, this.conf)) {
|
|
88
|
+
if (name_validator_1.NameValidator.violatesRule(className, regex, this.conf, this.regex.ignorePatterns)) {
|
|
76
89
|
issues.push(issue_1.Issue.atIdentifier(classDef.identifier, this.getDescription(expected, className), this.getMetadata().key, this.conf.severity));
|
|
77
90
|
}
|
|
78
91
|
}
|
|
@@ -58,6 +58,7 @@ class LocalVariableNames extends _abap_rule_1.ABAPRule {
|
|
|
58
58
|
constructor() {
|
|
59
59
|
super(...arguments);
|
|
60
60
|
this.conf = new LocalVariableNamesConf();
|
|
61
|
+
this.regex = this.compile();
|
|
61
62
|
}
|
|
62
63
|
getMetadata() {
|
|
63
64
|
return {
|
|
@@ -85,6 +86,15 @@ ENDFORM.`,
|
|
|
85
86
|
}
|
|
86
87
|
setConfig(conf) {
|
|
87
88
|
this.conf = conf;
|
|
89
|
+
this.regex = this.compile();
|
|
90
|
+
}
|
|
91
|
+
compile() {
|
|
92
|
+
return {
|
|
93
|
+
expectedData: new RegExp(this.conf.expectedData, "i"),
|
|
94
|
+
expectedConstant: new RegExp(this.conf.expectedConstant, "i"),
|
|
95
|
+
expectedFS: new RegExp(this.conf.expectedFS, "i"),
|
|
96
|
+
ignorePatterns: name_validator_1.NameValidator.compileIgnorePatterns(this.conf),
|
|
97
|
+
};
|
|
88
98
|
}
|
|
89
99
|
runParsed(file) {
|
|
90
100
|
const ret = [];
|
|
@@ -118,7 +128,7 @@ ENDFORM.`,
|
|
|
118
128
|
const found = dat.findFirstExpression(Expressions.DefinitionName);
|
|
119
129
|
if (found) {
|
|
120
130
|
const token = found.getFirstToken();
|
|
121
|
-
ret = ret.concat(this.checkName(token, file, this.conf.expectedData));
|
|
131
|
+
ret = ret.concat(this.checkName(token, file, this.conf.expectedData, this.regex.expectedData));
|
|
122
132
|
}
|
|
123
133
|
}
|
|
124
134
|
// inline data
|
|
@@ -126,7 +136,7 @@ ENDFORM.`,
|
|
|
126
136
|
const found = dat.findFirstExpression(Expressions.TargetField);
|
|
127
137
|
if (found) {
|
|
128
138
|
const token = found.getFirstToken();
|
|
129
|
-
ret = ret.concat(this.checkName(token, file, this.conf.expectedData));
|
|
139
|
+
ret = ret.concat(this.checkName(token, file, this.conf.expectedData, this.regex.expectedData));
|
|
130
140
|
}
|
|
131
141
|
}
|
|
132
142
|
// data structures, data begin of, first level
|
|
@@ -137,14 +147,14 @@ ENDFORM.`,
|
|
|
137
147
|
const found = stat === null || stat === void 0 ? void 0 : stat.findFirstExpression(Expressions.DefinitionName);
|
|
138
148
|
if (found) {
|
|
139
149
|
const token = found.getFirstToken();
|
|
140
|
-
ret = ret.concat(this.checkName(token, file, this.conf.expectedData));
|
|
150
|
+
ret = ret.concat(this.checkName(token, file, this.conf.expectedData, this.regex.expectedData));
|
|
141
151
|
}
|
|
142
152
|
}
|
|
143
153
|
for (const fieldsymbol of structure.findAllStatements(Statements.FieldSymbol)) {
|
|
144
154
|
const found = fieldsymbol.findFirstExpression(Expressions.FieldSymbol);
|
|
145
155
|
if (found) {
|
|
146
156
|
const token = found.getFirstToken();
|
|
147
|
-
ret = ret.concat(this.checkName(token, file, this.conf.expectedFS));
|
|
157
|
+
ret = ret.concat(this.checkName(token, file, this.conf.expectedFS, this.regex.expectedFS));
|
|
148
158
|
}
|
|
149
159
|
}
|
|
150
160
|
// inline field symbols
|
|
@@ -152,7 +162,7 @@ ENDFORM.`,
|
|
|
152
162
|
const found = fieldsymbol.findFirstExpression(Expressions.TargetFieldSymbol);
|
|
153
163
|
if (found) {
|
|
154
164
|
const token = found.getFirstToken();
|
|
155
|
-
ret = ret.concat(this.checkName(token, file, this.conf.expectedFS));
|
|
165
|
+
ret = ret.concat(this.checkName(token, file, this.conf.expectedFS, this.regex.expectedFS));
|
|
156
166
|
}
|
|
157
167
|
}
|
|
158
168
|
const constants = structure.findAllStatements(Statements.Constant);
|
|
@@ -164,16 +174,15 @@ ENDFORM.`,
|
|
|
164
174
|
const found = constant.findFirstExpression(Expressions.DefinitionName);
|
|
165
175
|
if (found) {
|
|
166
176
|
const token = found.getFirstToken();
|
|
167
|
-
ret = ret.concat(this.checkName(token, file, this.conf.expectedConstant));
|
|
177
|
+
ret = ret.concat(this.checkName(token, file, this.conf.expectedConstant, this.regex.expectedConstant));
|
|
168
178
|
}
|
|
169
179
|
}
|
|
170
180
|
return ret;
|
|
171
181
|
}
|
|
172
|
-
checkName(token, file, expected) {
|
|
182
|
+
checkName(token, file, expected, regex) {
|
|
173
183
|
const ret = [];
|
|
174
|
-
const regex = new RegExp(expected, "i");
|
|
175
184
|
const name = token.getStr();
|
|
176
|
-
if (name_validator_1.NameValidator.violatesRule(name, regex, this.conf)) {
|
|
185
|
+
if (name_validator_1.NameValidator.violatesRule(name, regex, this.conf, this.regex.ignorePatterns)) {
|
|
177
186
|
const message = this.getDescription(expected, name);
|
|
178
187
|
const issue = issue_1.Issue.atToken(file, token, message, this.getMetadata().key, this.conf.severity);
|
|
179
188
|
ret.push(issue);
|
|
@@ -53,6 +53,7 @@ class MacroNaming extends _abap_rule_1.ABAPRule {
|
|
|
53
53
|
constructor() {
|
|
54
54
|
super(...arguments);
|
|
55
55
|
this.conf = new MacroNamingConf();
|
|
56
|
+
this.regex = new RegExp(this.conf.pattern, "i");
|
|
56
57
|
}
|
|
57
58
|
getMetadata() {
|
|
58
59
|
return {
|
|
@@ -72,10 +73,10 @@ END-OF-DEFINITION.`,
|
|
|
72
73
|
}
|
|
73
74
|
setConfig(conf) {
|
|
74
75
|
this.conf = conf;
|
|
76
|
+
this.regex = new RegExp(this.conf.pattern, "i");
|
|
75
77
|
}
|
|
76
78
|
runParsed(file, obj) {
|
|
77
79
|
const issues = [];
|
|
78
|
-
const testRegex = new RegExp(this.conf.pattern, "i");
|
|
79
80
|
if (obj instanceof objects_1.TypePool) {
|
|
80
81
|
return [];
|
|
81
82
|
}
|
|
@@ -88,7 +89,7 @@ END-OF-DEFINITION.`,
|
|
|
88
89
|
continue;
|
|
89
90
|
}
|
|
90
91
|
const token = expr.getFirstToken();
|
|
91
|
-
if (
|
|
92
|
+
if (this.regex.exec(token.getStr())) {
|
|
92
93
|
continue;
|
|
93
94
|
}
|
|
94
95
|
else {
|
|
@@ -27,6 +27,7 @@ exports.MethodParameterNamesConf = MethodParameterNamesConf;
|
|
|
27
27
|
class MethodParameterNames {
|
|
28
28
|
constructor() {
|
|
29
29
|
this.conf = new MethodParameterNamesConf();
|
|
30
|
+
this.regex = this.compile();
|
|
30
31
|
}
|
|
31
32
|
getMetadata() {
|
|
32
33
|
return {
|
|
@@ -52,6 +53,16 @@ class MethodParameterNames {
|
|
|
52
53
|
}
|
|
53
54
|
setConfig(conf) {
|
|
54
55
|
this.conf = conf;
|
|
56
|
+
this.regex = this.compile();
|
|
57
|
+
}
|
|
58
|
+
compile() {
|
|
59
|
+
return {
|
|
60
|
+
importing: new RegExp(this.conf.importing, "i"),
|
|
61
|
+
returning: new RegExp(this.conf.returning, "i"),
|
|
62
|
+
changing: new RegExp(this.conf.changing, "i"),
|
|
63
|
+
exporting: new RegExp(this.conf.exporting, "i"),
|
|
64
|
+
ignorePatterns: name_validator_1.NameValidator.compileIgnorePatterns(this.conf),
|
|
65
|
+
};
|
|
55
66
|
}
|
|
56
67
|
initialize(reg) {
|
|
57
68
|
this.reg = reg;
|
|
@@ -91,16 +102,16 @@ class MethodParameterNames {
|
|
|
91
102
|
for (const p of method.parameters) {
|
|
92
103
|
switch (p.direction) {
|
|
93
104
|
case _abap_file_information_1.MethodParameterDirection.Importing:
|
|
94
|
-
ret = ret.concat(this.checkParameter(p, this.conf.importing));
|
|
105
|
+
ret = ret.concat(this.checkParameter(p, this.conf.importing, this.regex.importing));
|
|
95
106
|
break;
|
|
96
107
|
case _abap_file_information_1.MethodParameterDirection.Exporting:
|
|
97
|
-
ret = ret.concat(this.checkParameter(p, this.conf.exporting));
|
|
108
|
+
ret = ret.concat(this.checkParameter(p, this.conf.exporting, this.regex.exporting));
|
|
98
109
|
break;
|
|
99
110
|
case _abap_file_information_1.MethodParameterDirection.Changing:
|
|
100
|
-
ret = ret.concat(this.checkParameter(p, this.conf.changing));
|
|
111
|
+
ret = ret.concat(this.checkParameter(p, this.conf.changing, this.regex.changing));
|
|
101
112
|
break;
|
|
102
113
|
case _abap_file_information_1.MethodParameterDirection.Returning:
|
|
103
|
-
ret = ret.concat(this.checkParameter(p, this.conf.returning));
|
|
114
|
+
ret = ret.concat(this.checkParameter(p, this.conf.returning, this.regex.returning));
|
|
104
115
|
break;
|
|
105
116
|
default:
|
|
106
117
|
break;
|
|
@@ -108,11 +119,10 @@ class MethodParameterNames {
|
|
|
108
119
|
}
|
|
109
120
|
return ret;
|
|
110
121
|
}
|
|
111
|
-
checkParameter(param, expected) {
|
|
122
|
+
checkParameter(param, expected, regex) {
|
|
112
123
|
const ret = [];
|
|
113
|
-
const regex = new RegExp(expected, "i");
|
|
114
124
|
const name = param.name;
|
|
115
|
-
if (name_validator_1.NameValidator.violatesRule(name, regex, this.conf)) {
|
|
125
|
+
if (name_validator_1.NameValidator.violatesRule(name, regex, this.conf, this.regex.ignorePatterns)) {
|
|
116
126
|
const message = this.getDescription(expected, name);
|
|
117
127
|
const issue = issue_1.Issue.atIdentifier(param.identifier, message, this.getMetadata().key, this.conf.severity);
|
|
118
128
|
ret.push(issue);
|
|
@@ -53,6 +53,7 @@ exports.ModifyOnlyOwnDBTablesConf = ModifyOnlyOwnDBTablesConf;
|
|
|
53
53
|
class ModifyOnlyOwnDBTables {
|
|
54
54
|
constructor() {
|
|
55
55
|
this.conf = new ModifyOnlyOwnDBTablesConf();
|
|
56
|
+
this.ownTables = new RegExp(this.conf.ownTables, "i");
|
|
56
57
|
}
|
|
57
58
|
getMetadata() {
|
|
58
59
|
return {
|
|
@@ -70,6 +71,7 @@ class ModifyOnlyOwnDBTables {
|
|
|
70
71
|
}
|
|
71
72
|
setConfig(conf) {
|
|
72
73
|
this.conf = conf;
|
|
74
|
+
this.ownTables = new RegExp(this.conf.ownTables, "i");
|
|
73
75
|
}
|
|
74
76
|
initialize(reg) {
|
|
75
77
|
this.reg = reg;
|
|
@@ -87,7 +89,6 @@ class ModifyOnlyOwnDBTables {
|
|
|
87
89
|
if (struc === undefined) {
|
|
88
90
|
return [];
|
|
89
91
|
}
|
|
90
|
-
const regExp = new RegExp(this.getConfig().ownTables, "i");
|
|
91
92
|
for (const s of file.getStatements()) {
|
|
92
93
|
const g = s.get();
|
|
93
94
|
if (g instanceof Statements.DeleteDatabase
|
|
@@ -105,7 +106,7 @@ class ModifyOnlyOwnDBTables {
|
|
|
105
106
|
continue;
|
|
106
107
|
}
|
|
107
108
|
const concat = databaseTable.concatTokens().toUpperCase();
|
|
108
|
-
if (
|
|
109
|
+
if (this.ownTables.test(concat) === false) {
|
|
109
110
|
// must contain a ReferenceType.TableVoidReference or a ReferenceType.TableReference if its a dependency
|
|
110
111
|
if (spaghetti === undefined) {
|
|
111
112
|
spaghetti = new syntax_1.SyntaxLogic(this.reg, obj).run().spaghetti;
|
|
@@ -71,6 +71,7 @@ class NoPrefixes extends _abap_rule_1.ABAPRule {
|
|
|
71
71
|
constructor() {
|
|
72
72
|
super(...arguments);
|
|
73
73
|
this.conf = new NoPrefixesConf();
|
|
74
|
+
this.regex = this.compile();
|
|
74
75
|
}
|
|
75
76
|
getMetadata() {
|
|
76
77
|
return {
|
|
@@ -94,6 +95,18 @@ https://github.com/SAP/styleguides/blob/main/clean-abap/sub-sections/AvoidEncodi
|
|
|
94
95
|
}
|
|
95
96
|
setConfig(conf) {
|
|
96
97
|
this.conf = conf;
|
|
98
|
+
this.regex = this.compile();
|
|
99
|
+
}
|
|
100
|
+
compile() {
|
|
101
|
+
const compile = (pattern) => pattern !== undefined && pattern !== "" ? new RegExp(pattern, "i") : undefined;
|
|
102
|
+
return {
|
|
103
|
+
data: compile(this.conf.data),
|
|
104
|
+
statics: compile(this.conf.statics),
|
|
105
|
+
fieldSymbols: compile(this.conf.fieldSymbols),
|
|
106
|
+
constants: compile(this.conf.constants),
|
|
107
|
+
types: compile(this.conf.types),
|
|
108
|
+
methodParameters: compile(this.conf.methodParameters),
|
|
109
|
+
};
|
|
97
110
|
}
|
|
98
111
|
runParsed(file) {
|
|
99
112
|
const ret = [];
|
|
@@ -107,38 +120,38 @@ https://github.com/SAP/styleguides/blob/main/clean-abap/sub-sections/AvoidEncodi
|
|
|
107
120
|
if (max === undefined || max < 1) {
|
|
108
121
|
max = 10;
|
|
109
122
|
}
|
|
110
|
-
if (
|
|
111
|
-
ret.push(...this.checkData(structure,
|
|
123
|
+
if (this.regex.data !== undefined) {
|
|
124
|
+
ret.push(...this.checkData(structure, this.regex.data, file));
|
|
112
125
|
}
|
|
113
126
|
if (ret.length >= max) {
|
|
114
127
|
return ret;
|
|
115
128
|
}
|
|
116
|
-
if (
|
|
117
|
-
ret.push(...this.checkStatics(structure,
|
|
129
|
+
if (this.regex.statics !== undefined) {
|
|
130
|
+
ret.push(...this.checkStatics(structure, this.regex.statics, file));
|
|
118
131
|
}
|
|
119
132
|
if (ret.length >= max) {
|
|
120
133
|
return ret;
|
|
121
134
|
}
|
|
122
|
-
if (
|
|
123
|
-
ret.push(...this.checkFieldSymbols(structure,
|
|
135
|
+
if (this.regex.fieldSymbols !== undefined) {
|
|
136
|
+
ret.push(...this.checkFieldSymbols(structure, this.regex.fieldSymbols, file));
|
|
124
137
|
}
|
|
125
138
|
if (ret.length >= max) {
|
|
126
139
|
return ret;
|
|
127
140
|
}
|
|
128
|
-
if (
|
|
129
|
-
ret.push(...this.checkConstants(structure,
|
|
141
|
+
if (this.regex.constants !== undefined) {
|
|
142
|
+
ret.push(...this.checkConstants(structure, this.regex.constants, file));
|
|
130
143
|
}
|
|
131
144
|
if (ret.length >= max) {
|
|
132
145
|
return ret;
|
|
133
146
|
}
|
|
134
|
-
if (
|
|
135
|
-
ret.push(...this.checkTypes(structure,
|
|
147
|
+
if (this.regex.types !== undefined) {
|
|
148
|
+
ret.push(...this.checkTypes(structure, this.regex.types, file));
|
|
136
149
|
}
|
|
137
150
|
if (ret.length >= max) {
|
|
138
151
|
return ret;
|
|
139
152
|
}
|
|
140
|
-
if (
|
|
141
|
-
ret.push(...this.checkMethodParameters(structure,
|
|
153
|
+
if (this.regex.methodParameters !== undefined) {
|
|
154
|
+
ret.push(...this.checkMethodParameters(structure, this.regex.methodParameters, file));
|
|
142
155
|
}
|
|
143
156
|
return ret;
|
|
144
157
|
}
|
|
@@ -78,6 +78,7 @@ exports.ObjectNamingConf = ObjectNamingConf;
|
|
|
78
78
|
class ObjectNaming {
|
|
79
79
|
constructor() {
|
|
80
80
|
this.conf = new ObjectNamingConf();
|
|
81
|
+
this.regex = this.compile();
|
|
81
82
|
}
|
|
82
83
|
getMetadata() {
|
|
83
84
|
return {
|
|
@@ -100,6 +101,20 @@ class ObjectNaming {
|
|
|
100
101
|
}
|
|
101
102
|
setConfig(conf) {
|
|
102
103
|
this.conf = conf;
|
|
104
|
+
this.regex = this.compile();
|
|
105
|
+
}
|
|
106
|
+
compile() {
|
|
107
|
+
const config = this.getConfig();
|
|
108
|
+
const patterns = new Map();
|
|
109
|
+
for (const [key, value] of Object.entries(config)) {
|
|
110
|
+
if (typeof value === "string" && key !== "patternKind" && key !== "severity") {
|
|
111
|
+
patterns.set(key, new RegExp(value, "i"));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
patterns,
|
|
116
|
+
ignorePatterns: name_validator_1.NameValidator.compileIgnorePatterns(config),
|
|
117
|
+
};
|
|
103
118
|
}
|
|
104
119
|
initialize(_reg) {
|
|
105
120
|
return this;
|
|
@@ -113,11 +128,11 @@ class ObjectNaming {
|
|
|
113
128
|
const config = this.getConfig();
|
|
114
129
|
// @ts-ignore
|
|
115
130
|
const pattern = config[abapType];
|
|
116
|
-
|
|
131
|
+
const regex = this.regex.patterns.get(abapType);
|
|
132
|
+
if (pattern === undefined || regex === undefined) {
|
|
117
133
|
return [];
|
|
118
134
|
}
|
|
119
|
-
|
|
120
|
-
if (name_validator_1.NameValidator.violatesRule(obj.getName(), regex, this.conf)) {
|
|
135
|
+
if (name_validator_1.NameValidator.violatesRule(obj.getName(), regex, this.conf, this.regex.ignorePatterns)) {
|
|
121
136
|
message = this.getDescription(pattern, obj.getName());
|
|
122
137
|
}
|
|
123
138
|
if (message) {
|
|
@@ -0,0 +1,276 @@
|
|
|
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.PreferInlineCast = exports.PreferInlineCastConf = void 0;
|
|
37
|
+
const Expressions = __importStar(require("../abap/2_statements/expressions"));
|
|
38
|
+
const Statements = __importStar(require("../abap/2_statements/statements"));
|
|
39
|
+
const _statement_1 = require("../abap/2_statements/statements/_statement");
|
|
40
|
+
const _reference_1 = require("../abap/5_syntax/_reference");
|
|
41
|
+
const syntax_1 = require("../abap/5_syntax/syntax");
|
|
42
|
+
const _identifier_1 = require("../abap/4_file_information/_identifier");
|
|
43
|
+
const _typed_identifier_1 = require("../abap/types/_typed_identifier");
|
|
44
|
+
const basic_1 = require("../abap/types/basic");
|
|
45
|
+
const edit_helper_1 = require("../edit_helper");
|
|
46
|
+
const issue_1 = require("../issue");
|
|
47
|
+
const _abap_object_1 = require("../objects/_abap_object");
|
|
48
|
+
const version_1 = require("../version");
|
|
49
|
+
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
50
|
+
const _irule_1 = require("./_irule");
|
|
51
|
+
class PreferInlineCastConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
52
|
+
}
|
|
53
|
+
exports.PreferInlineCastConf = PreferInlineCastConf;
|
|
54
|
+
class PreferInlineCast {
|
|
55
|
+
constructor() {
|
|
56
|
+
this.conf = new PreferInlineCastConf();
|
|
57
|
+
}
|
|
58
|
+
getMetadata() {
|
|
59
|
+
return {
|
|
60
|
+
key: "prefer_inline_cast",
|
|
61
|
+
title: "Prefer Inline CAST",
|
|
62
|
+
shortDescription: `Inline a CAST helper read only once in the next statement`,
|
|
63
|
+
extendedInformation: `Requires 7.40 SP02 or above, or ABAP Cloud, and no syntax errors.
|
|
64
|
+
|
|
65
|
+
Reports DATA(t) = CAST type( source ) when the helper has exactly one read and no other access,
|
|
66
|
+
except its declaration write, in the entire syntax scope (including earlier reads in loop bodies and exception handlers).
|
|
67
|
+
The immediately following statement must be a plain assignment, an attribute read, a receiver method call,
|
|
68
|
+
or a named IMPORTING argument of a functional method call (caller syntax p = t).
|
|
69
|
+
|
|
70
|
+
Only class/interface CAST targets, simple assignment targets and static access are supported.
|
|
71
|
+
For argument uses, the CAST operand must be a plain field chain without calls, the receiver and all
|
|
72
|
+
other arguments must be plain variables or literals, and the method signature must be known.
|
|
73
|
+
Explicit EXPORTING, IMPORTING and CHANGING sections are not supported.
|
|
74
|
+
Objects containing macros or dynamic access are skipped. Field symbols, reference-taking,
|
|
75
|
+
pragmas and chained statements are not supported.
|
|
76
|
+
|
|
77
|
+
Predicates such as IS INITIAL / IS BOUND and removal of statically redundant CASTs are left for a future version.`,
|
|
78
|
+
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.SingleFile],
|
|
79
|
+
badExample: `DATA(helper) = CAST lcl_node( object ).
|
|
80
|
+
result = helper->get_tokens( ).`,
|
|
81
|
+
goodExample: `result = CAST lcl_node( object )->get_tokens( ).`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
getConfig() {
|
|
85
|
+
return this.conf;
|
|
86
|
+
}
|
|
87
|
+
setConfig(conf) {
|
|
88
|
+
this.conf = conf;
|
|
89
|
+
}
|
|
90
|
+
initialize(reg) {
|
|
91
|
+
this.reg = reg;
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
run(obj) {
|
|
95
|
+
var _a;
|
|
96
|
+
if (!(obj instanceof _abap_object_1.ABAPObject)
|
|
97
|
+
|| (!(0, version_1.releaseAtLeast)(this.reg.getConfig().getRelease(), version_1.Release.v740sp02)
|
|
98
|
+
&& this.reg.getConfig().getLanguageVersion() !== version_1.LanguageVersion.Cloud)) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
// Neither macro expansion nor a dynamically named variable has reliable references.
|
|
102
|
+
let hasCast = false;
|
|
103
|
+
for (const file of obj.getABAPFiles()) {
|
|
104
|
+
for (const statement of file.getStatements()) {
|
|
105
|
+
hasCast || (hasCast = statement.findFirstExpression(Expressions.Cast) !== undefined);
|
|
106
|
+
const type = statement.get();
|
|
107
|
+
if (type instanceof _statement_1.Unknown || type instanceof Statements.Define
|
|
108
|
+
|| type instanceof _statement_1.MacroCall || type instanceof _statement_1.MacroContent || type instanceof _statement_1.MacroRecursion
|
|
109
|
+
|| statement.findFirstExpression(Expressions.Dynamic) !== undefined) {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (!hasCast) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
const syntax = new syntax_1.SyntaxLogic(this.reg, obj).run();
|
|
118
|
+
if (syntax.issues.length > 0) {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
const references = new Map();
|
|
122
|
+
const knownMethods = new Set();
|
|
123
|
+
for (const reference of this.references(syntax.spaghetti.getTop())) {
|
|
124
|
+
if (reference.resolved === undefined) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const key = this.identifierKey(reference.resolved);
|
|
128
|
+
const uses = references.get(key) || [];
|
|
129
|
+
uses.push(reference);
|
|
130
|
+
references.set(key, uses);
|
|
131
|
+
if (reference.referenceType === _reference_1.ReferenceType.MethodReference) {
|
|
132
|
+
knownMethods.add(this.identifierKey(reference.position));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const issues = [];
|
|
136
|
+
for (const file of obj.getABAPFiles()) {
|
|
137
|
+
const statements = file.getStatements();
|
|
138
|
+
for (let index = 0; index < statements.length - 1; index++) {
|
|
139
|
+
const declaration = statements[index];
|
|
140
|
+
const next = statements[index + 1];
|
|
141
|
+
const cast = this.castDeclaration(declaration);
|
|
142
|
+
const inline = declaration.findFirstExpression(Expressions.InlineData);
|
|
143
|
+
const name = (_a = inline === null || inline === void 0 ? void 0 : inline.findDirectExpression(Expressions.TargetField)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
|
|
144
|
+
if (cast === undefined || name === undefined) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const scope = syntax.spaghetti.lookupPosition(name.getStart(), file.getFilename());
|
|
148
|
+
const identifier = scope === null || scope === void 0 ? void 0 : scope.findVariable(name.getStr());
|
|
149
|
+
if (!(identifier instanceof _typed_identifier_1.TypedIdentifier) || !(identifier.getType() instanceof basic_1.ObjectReferenceType)
|
|
150
|
+
|| identifier.getType().containsVoid()
|
|
151
|
+
|| identifier.getType().isGeneric()) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const uses = references.get(this.identifierKey(identifier)) || [];
|
|
155
|
+
const reads = uses.filter(r => r.referenceType === _reference_1.ReferenceType.DataReadReference);
|
|
156
|
+
const writes = uses.filter(r => r.referenceType === _reference_1.ReferenceType.DataWriteReference);
|
|
157
|
+
if (reads.length !== 1 || writes.length !== 1
|
|
158
|
+
|| !declaration.includesToken(writes[0].position.getToken())
|
|
159
|
+
|| uses.length !== 2 || !next.includesToken(reads[0].position.getToken())
|
|
160
|
+
|| !this.supportedUse(next, reads[0].position, knownMethods, cast)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
// Replace the later occurrence first, also when both statements are on the same line.
|
|
164
|
+
const fix = edit_helper_1.EditHelper.merge(edit_helper_1.EditHelper.replaceToken(file, reads[0].position.getToken(), cast.concatTokensWithLinebreaks()), edit_helper_1.EditHelper.deleteStatement(file, declaration));
|
|
165
|
+
issues.push(issue_1.Issue.atToken(file, name, "Inline single-use CAST helper", this.getMetadata().key, this.conf.severity, fix));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return issues;
|
|
169
|
+
}
|
|
170
|
+
identifierKey(identifier) {
|
|
171
|
+
const start = identifier.getStart();
|
|
172
|
+
return identifier.getFilename() + "," + start.getRow() + "," + start.getCol();
|
|
173
|
+
}
|
|
174
|
+
references(node) {
|
|
175
|
+
return node.getData().references.concat(...node.getChildren().map(child => this.references(child)));
|
|
176
|
+
}
|
|
177
|
+
castDeclaration(statement) {
|
|
178
|
+
var _a;
|
|
179
|
+
if (!(statement.get() instanceof Statements.Move) || statement.getColon() !== undefined
|
|
180
|
+
|| statement.getPragmas().length > 0) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
const target = statement.findDirectExpression(Expressions.Target);
|
|
184
|
+
const source = statement.findDirectExpression(Expressions.Source);
|
|
185
|
+
const chain = source === null || source === void 0 ? void 0 : source.findDirectExpression(Expressions.MethodCallChain);
|
|
186
|
+
const cast = chain === null || chain === void 0 ? void 0 : chain.findDirectExpression(Expressions.Cast);
|
|
187
|
+
if ((target === null || target === void 0 ? void 0 : target.getChildren().length) !== 1 || target.findDirectExpression(Expressions.InlineData) === undefined
|
|
188
|
+
|| (source === null || source === void 0 ? void 0 : source.getChildren().length) !== 1 || (chain === null || chain === void 0 ? void 0 : chain.getChildren().length) !== 1
|
|
189
|
+
|| cast === undefined || cast.findDirectExpression(Expressions.Let) !== undefined
|
|
190
|
+
|| cast.findDirectExpression(Expressions.Dereference) !== undefined
|
|
191
|
+
|| ((_a = cast.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _a === void 0 ? void 0 : _a.concatTokens()) === "#"
|
|
192
|
+
|| statement.findFirstExpression(Expressions.SourceFieldSymbol) !== undefined
|
|
193
|
+
|| statement.getTokens().length !== target.countTokens() + source.countTokens() + 2
|
|
194
|
+
|| statement.getTokens()[target.countTokens()].getStr() !== "=") {
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
return cast;
|
|
198
|
+
}
|
|
199
|
+
supportedUse(statement, read, knownMethods, cast) {
|
|
200
|
+
var _a, _b, _c;
|
|
201
|
+
if (statement.getColon() !== undefined || statement.getPragmas().length > 0
|
|
202
|
+
|| statement.findFirstExpression(Expressions.SourceFieldSymbol) !== undefined
|
|
203
|
+
|| statement.findFirstExpression(Expressions.MethodParameters) !== undefined) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
let chain;
|
|
207
|
+
if (statement.get() instanceof Statements.Move) {
|
|
208
|
+
const target = statement.findDirectExpression(Expressions.Target);
|
|
209
|
+
const source = statement.findDirectExpression(Expressions.Source);
|
|
210
|
+
if (target === undefined || source === undefined || !this.simpleTarget(target)
|
|
211
|
+
|| statement.getTokens().length !== target.countTokens() + source.countTokens() + 2
|
|
212
|
+
|| statement.getTokens()[target.countTokens()].getStr() !== "="
|
|
213
|
+
|| source.getChildren().length !== 1) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
const field = source.findDirectExpression(Expressions.FieldChain);
|
|
217
|
+
if (field !== undefined) {
|
|
218
|
+
const tokens = field.getTokens();
|
|
219
|
+
return field.getFirstToken().getStart().equals(read.getStart())
|
|
220
|
+
&& (tokens.length === 1 || (tokens.length === 3 && tokens[1].getStr() === "->"
|
|
221
|
+
&& field.findDirectExpression(Expressions.AttributeName) !== undefined));
|
|
222
|
+
}
|
|
223
|
+
chain = source.findDirectExpression(Expressions.MethodCallChain);
|
|
224
|
+
}
|
|
225
|
+
else if (statement.get() instanceof Statements.Call) {
|
|
226
|
+
chain = statement.findDirectExpression(Expressions.MethodCallChain);
|
|
227
|
+
}
|
|
228
|
+
if (chain === undefined) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
const calls = chain.findDirectExpressions(Expressions.MethodCall);
|
|
232
|
+
const receiver = chain.findDirectExpression(Expressions.FieldChain);
|
|
233
|
+
if (calls.length !== 1 || (chain.getChildren().length !== 1 && chain.getChildren().length !== 3)
|
|
234
|
+
|| (receiver !== undefined && receiver.countTokens() !== 1)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
if (receiver === null || receiver === void 0 ? void 0 : receiver.getFirstToken().getStart().equals(read.getStart())) {
|
|
238
|
+
// The receiver is evaluated first; even calls in its arguments stay after the CAST.
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
// Named argument uses are limited to functional calls with a known signature.
|
|
242
|
+
const method = (_a = calls[0].findDirectExpression(Expressions.MethodName)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
|
|
243
|
+
if (!(statement.get() instanceof Statements.Move) || method === undefined
|
|
244
|
+
|| !this.plainCastOperand(cast)
|
|
245
|
+
|| !knownMethods.has(this.identifierKey(new _identifier_1.Identifier(method, read.getFilename())))) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
const parameters = (_c = (_b = calls[0].findDirectExpression(Expressions.MethodCallParam)) === null || _b === void 0 ? void 0 : _b.findDirectExpression(Expressions.ParameterListS)) === null || _c === void 0 ? void 0 : _c.findDirectExpressions(Expressions.ParameterS);
|
|
249
|
+
return parameters !== undefined && parameters.some(p => { var _a; return (_a = p.findDirectExpression(Expressions.Source)) === null || _a === void 0 ? void 0 : _a.getFirstToken().getStart().equals(read.getStart()); })
|
|
250
|
+
&& parameters.every(p => this.plainSource(p.findDirectExpression(Expressions.Source)));
|
|
251
|
+
}
|
|
252
|
+
plainCastOperand(cast) {
|
|
253
|
+
// Unlike receiver uses, argument uses can evaluate the receiver before the CAST operand.
|
|
254
|
+
// Exclude calls, table expressions and computed offsets/lengths from the field chain.
|
|
255
|
+
const source = cast.findDirectExpression(Expressions.Source);
|
|
256
|
+
const field = source === null || source === void 0 ? void 0 : source.findDirectExpression(Expressions.FieldChain);
|
|
257
|
+
return (source === null || source === void 0 ? void 0 : source.getChildren().length) === 1 && field !== undefined
|
|
258
|
+
&& field.findFirstExpression(Expressions.MethodCall) === undefined
|
|
259
|
+
&& field.findFirstExpression(Expressions.TableExpression) === undefined
|
|
260
|
+
&& field.findFirstExpression(Expressions.FieldOffset) === undefined
|
|
261
|
+
&& field.findFirstExpression(Expressions.FieldLength) === undefined;
|
|
262
|
+
}
|
|
263
|
+
simpleTarget(target) {
|
|
264
|
+
return target.countTokens() === 1 && target.findDirectExpression(Expressions.TargetField) !== undefined
|
|
265
|
+
|| target.getChildren().length === 1 && target.findDirectExpression(Expressions.InlineData) !== undefined;
|
|
266
|
+
}
|
|
267
|
+
plainSource(source) {
|
|
268
|
+
if ((source === null || source === void 0 ? void 0 : source.getChildren().length) !== 1) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
return source.findDirectExpression(Expressions.Constant) !== undefined
|
|
272
|
+
|| source.countTokens() === 1 && source.findDirectExpression(Expressions.FieldChain) !== undefined;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
exports.PreferInlineCast = PreferInlineCast;
|
|
276
|
+
//# sourceMappingURL=prefer_inline_cast.js.map
|