@abaplint/core 2.120.70 → 2.120.72
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 +10 -0
- package/build/src/abap/1_lexer/lexer.js +1 -1
- package/build/src/abap/1_lexer/lexer_stream.js +8 -0
- package/build/src/abap/2_statements/expressions/string_template_formatting.js +1 -1
- package/build/src/abap/3_structures/structures/_combi.js +61 -112
- package/build/src/abap/5_syntax/basic_types.js +4 -4
- package/build/src/abap/5_syntax/expressions/_check_database_work_area.js +127 -0
- package/build/src/abap/5_syntax/expressions/attribute_name.js +1 -1
- package/build/src/abap/5_syntax/expressions/compare.js +48 -0
- package/build/src/abap/5_syntax/expressions/conv_body.js +2 -2
- package/build/src/abap/5_syntax/expressions/field_chain.js +1 -1
- package/build/src/abap/5_syntax/expressions/form_param.js +1 -1
- package/build/src/abap/5_syntax/expressions/method_parameters.js +2 -2
- package/build/src/abap/5_syntax/expressions/select.js +1 -2
- package/build/src/abap/5_syntax/expressions/source.js +19 -3
- package/build/src/abap/5_syntax/statements/assign.js +15 -1
- package/build/src/abap/5_syntax/statements/insert_database.js +6 -3
- package/build/src/abap/5_syntax/statements/loop.js +1 -1
- package/build/src/abap/5_syntax/statements/modify_database.js +8 -4
- package/build/src/abap/5_syntax/statements/update_database.js +7 -3
- package/build/src/abap/5_syntax/statements/when_type.js +1 -1
- package/build/src/abap/types/class_attributes.js +1 -1
- package/build/src/cds/cds_lexer.js +1 -1
- package/build/src/lsp/help.js +2 -2
- package/build/src/objects/data_element.js +1 -1
- package/build/src/objects/table.js +1 -0
- package/build/src/registry.js +1 -1
- package/build/src/rules/align_pseudo_comments.js +1 -1
- package/build/src/rules/align_type_expressions.js +1 -1
- package/build/src/rules/cds_check_syntax.js +1 -1
- package/build/src/rules/change_if_to_case.js +1 -1
- package/build/src/rules/check_ddic.js +1 -1
- package/build/src/rules/class_attribute_names.js +1 -3
- package/build/src/rules/downport.js +122 -24
- package/build/src/rules/empty_structure.js +26 -0
- package/build/src/rules/functional_writing.js +1 -1
- package/build/src/rules/keyword_case.js +1 -1
- package/build/src/rules/local_class_naming.js +1 -1
- package/build/src/rules/many_parentheses.js +1 -1
- package/build/src/rules/obsolete_statement.js +3 -7
- package/build/src/rules/parser_missing_space.js +1 -2
- package/build/src/rules/prefer_raise_exception_new.js +2 -8
- package/build/src/rules/redundant_conversion.js +59 -2
- package/build/src/rules/xml_consistency.js +25 -1
- package/build/src/utils/form_length_stats.js +1 -1
- package/build/src/utils/method_length_stats.js +1 -1
- package/package.json +1 -1
|
@@ -531,7 +531,7 @@ Make sure to test the downported code, it might not always be completely correct
|
|
|
531
531
|
if (found) {
|
|
532
532
|
return found;
|
|
533
533
|
}
|
|
534
|
-
found = this.outlineGetReferenceSimple(high, lowFile);
|
|
534
|
+
found = this.outlineGetReferenceSimple(high, lowFile, highSyntax);
|
|
535
535
|
if (found) {
|
|
536
536
|
return found;
|
|
537
537
|
}
|
|
@@ -1206,7 +1206,17 @@ ${indentation}CATCH ${className} INTO ${targetName}.`;
|
|
|
1206
1206
|
const fix = edit_helper_1.EditHelper.replaceRange(lowFile, node.getStart(), node.getEnd(), code);
|
|
1207
1207
|
return issue_1.Issue.atToken(lowFile, node.getFirstToken(), "Outline DATA", this.getMetadata().key, this.conf.severity, fix);
|
|
1208
1208
|
}
|
|
1209
|
-
|
|
1209
|
+
/** the type of a reference to the source: LIKE REF TO it, or REF TO data when its type is generic,
|
|
1210
|
+
* as LIKE REF TO a generically typed field symbol or parameter does not activate */
|
|
1211
|
+
refTypeOf(sourceName, node, lowFile, highSyntax) {
|
|
1212
|
+
var _a;
|
|
1213
|
+
const spag = highSyntax.spaghetti.lookupPosition(node.getFirstToken().getStart(), lowFile.getFilename());
|
|
1214
|
+
if (((_a = spag === null || spag === void 0 ? void 0 : spag.findVariable(sourceName)) === null || _a === void 0 ? void 0 : _a.getType().isGeneric()) === true) {
|
|
1215
|
+
return "TYPE REF TO data";
|
|
1216
|
+
}
|
|
1217
|
+
return "LIKE REF TO " + sourceName;
|
|
1218
|
+
}
|
|
1219
|
+
outlineGetReferenceSimple(node, lowFile, highSyntax) {
|
|
1210
1220
|
var _a, _b, _c;
|
|
1211
1221
|
if (!(node.get() instanceof Statements.GetReference)) {
|
|
1212
1222
|
return undefined;
|
|
@@ -1223,7 +1233,8 @@ ${indentation}CATCH ${className} INTO ${targetName}.`;
|
|
|
1223
1233
|
const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
1224
1234
|
const firstToken = target.getFirstToken();
|
|
1225
1235
|
const lastToken = target.getLastToken();
|
|
1226
|
-
const
|
|
1236
|
+
const refType = this.refTypeOf(source.concatTokens(), node, lowFile, highSyntax);
|
|
1237
|
+
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getStart(), `DATA ${targetName} ${refType}.\n${indentation}`);
|
|
1227
1238
|
const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, firstToken.getStart(), lastToken.getEnd(), targetName);
|
|
1228
1239
|
const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
|
|
1229
1240
|
return issue_1.Issue.atToken(lowFile, node.getFirstToken(), "Outline DATA", this.getMetadata().key, this.conf.severity, fix);
|
|
@@ -1240,7 +1251,7 @@ ${indentation}CATCH ${className} INTO ${targetName}.`;
|
|
|
1240
1251
|
if (!(((_a = target === null || target === void 0 ? void 0 : target.getFirstChild()) === null || _a === void 0 ? void 0 : _a.get()) instanceof Expressions.InlineData)) {
|
|
1241
1252
|
return undefined;
|
|
1242
1253
|
}
|
|
1243
|
-
let type
|
|
1254
|
+
let type;
|
|
1244
1255
|
const source = node.findFirstExpression(Expressions.Source);
|
|
1245
1256
|
if (source === undefined) {
|
|
1246
1257
|
return undefined;
|
|
@@ -1330,8 +1341,8 @@ ${indentation}CATCH ${className} INTO ${targetName}.`;
|
|
|
1330
1341
|
if (!(node.get() instanceof Statements.Raise)) {
|
|
1331
1342
|
return undefined;
|
|
1332
1343
|
}
|
|
1333
|
-
let id
|
|
1334
|
-
let number
|
|
1344
|
+
let id;
|
|
1345
|
+
let number;
|
|
1335
1346
|
let startToken = node.findDirectTokenByText("ID");
|
|
1336
1347
|
if (startToken) {
|
|
1337
1348
|
const sources = node.findDirectExpressions(Expressions.Source);
|
|
@@ -1489,7 +1500,7 @@ ${indentation}${uniqueName2}->if_t100_message~t100key = ${uniqueName1}.\n`;
|
|
|
1489
1500
|
|| sourceRef.getChildren().length !== 1) {
|
|
1490
1501
|
return undefined;
|
|
1491
1502
|
}
|
|
1492
|
-
let code
|
|
1503
|
+
let code;
|
|
1493
1504
|
if (sourceRef.findFirstExpression(Expressions.TableExpression)) {
|
|
1494
1505
|
const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
|
|
1495
1506
|
code = `ASSIGN ${sourceRef.concatTokens()} TO FIELD-SYMBOL(<${uniqueName}>).
|
|
@@ -1681,7 +1692,7 @@ LOOP AT ${groupTargetName}tab ${groupTarget}.`;
|
|
|
1681
1692
|
|| !(tableExpression instanceof nodes_1.ExpressionNode)) {
|
|
1682
1693
|
return undefined;
|
|
1683
1694
|
}
|
|
1684
|
-
let condition
|
|
1695
|
+
let condition;
|
|
1685
1696
|
if (tableExpression.getChildren().length === 3) {
|
|
1686
1697
|
const index = tableExpression.findDirectExpression(Expressions.Source);
|
|
1687
1698
|
if (index === undefined) {
|
|
@@ -1865,7 +1876,7 @@ ${indentation}${uniqueName}`;
|
|
|
1865
1876
|
return undefined;
|
|
1866
1877
|
}
|
|
1867
1878
|
const op = secondChild.getFirstToken();
|
|
1868
|
-
let operator
|
|
1879
|
+
let operator;
|
|
1869
1880
|
switch (op.getStr()) {
|
|
1870
1881
|
case "+":
|
|
1871
1882
|
operator = " + ";
|
|
@@ -1907,7 +1918,7 @@ ${indentation}${uniqueName}`;
|
|
|
1907
1918
|
|| (formatting === null || formatting === void 0 ? void 0 : formatting.startsWith("ALPHA = ")) === false) {
|
|
1908
1919
|
continue;
|
|
1909
1920
|
}
|
|
1910
|
-
let functionName
|
|
1921
|
+
let functionName;
|
|
1911
1922
|
switch (formatting) {
|
|
1912
1923
|
case "ALPHA = IN":
|
|
1913
1924
|
functionName = "CONVERSION_EXIT_ALPHA_INPUT";
|
|
@@ -2203,13 +2214,12 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
2203
2214
|
const uniqueName = this.uniqueName(firstToken.getStart(), lowFile.getFilename(), highSyntax);
|
|
2204
2215
|
const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
|
|
2205
2216
|
let body = "";
|
|
2206
|
-
let name = "";
|
|
2207
2217
|
const switchBody = i.findDirectExpression(Expressions.SwitchBody);
|
|
2208
2218
|
if (switchBody === undefined) {
|
|
2209
2219
|
continue;
|
|
2210
2220
|
}
|
|
2211
2221
|
for (const l of ((_c = switchBody === null || switchBody === void 0 ? void 0 : switchBody.findDirectExpression(Expressions.Let)) === null || _c === void 0 ? void 0 : _c.findDirectExpressions(Expressions.InlineFieldDefinition)) || []) {
|
|
2212
|
-
name = l.getFirstToken().getStr();
|
|
2222
|
+
const name = l.getFirstToken().getStr();
|
|
2213
2223
|
body += indentation + `DATA(${name}) = ${(_d = switchBody.findFirstExpression(Expressions.Source)) === null || _d === void 0 ? void 0 : _d.concatTokens()}.\n`;
|
|
2214
2224
|
}
|
|
2215
2225
|
body += `DATA ${uniqueName} ${type}.\n`;
|
|
@@ -2267,7 +2277,6 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
2267
2277
|
const uniqueName = this.uniqueName(firstToken.getStart(), lowFile.getFilename(), highSyntax);
|
|
2268
2278
|
const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
|
|
2269
2279
|
let body = "";
|
|
2270
|
-
let name = "";
|
|
2271
2280
|
const reduceBody = i.findDirectExpression(Expressions.ReduceBody);
|
|
2272
2281
|
if (reduceBody === undefined) {
|
|
2273
2282
|
continue;
|
|
@@ -2278,7 +2287,7 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
2278
2287
|
}
|
|
2279
2288
|
let firstName = "";
|
|
2280
2289
|
for (const init of reduceBody.findDirectExpressions(Expressions.InlineFieldDefinition)) {
|
|
2281
|
-
name = init.getFirstToken().getStr();
|
|
2290
|
+
const name = init.getFirstToken().getStr();
|
|
2282
2291
|
if (firstName === "") {
|
|
2283
2292
|
firstName = name;
|
|
2284
2293
|
}
|
|
@@ -2510,7 +2519,7 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
2510
2519
|
after += c.concatTokens();
|
|
2511
2520
|
}
|
|
2512
2521
|
}
|
|
2513
|
-
let condition
|
|
2522
|
+
let condition;
|
|
2514
2523
|
if ((tableExpression === null || tableExpression === void 0 ? void 0 : tableExpression.getChildren().length) === 3) {
|
|
2515
2524
|
condition = "INDEX " + ((_d = tableExpression === null || tableExpression === void 0 ? void 0 : tableExpression.findDirectExpression(Expressions.Source)) === null || _d === void 0 ? void 0 : _d.concatTokens());
|
|
2516
2525
|
}
|
|
@@ -2661,7 +2670,7 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
2661
2670
|
continue;
|
|
2662
2671
|
}
|
|
2663
2672
|
const name = nameToken.getStr();
|
|
2664
|
-
let type
|
|
2673
|
+
let type;
|
|
2665
2674
|
if (high.concatTokens().toUpperCase().startsWith("APPEND INITIAL LINE TO ")) {
|
|
2666
2675
|
type = "LIKE LINE OF " + ((_b = high.findFirstExpression(Expressions.Target)) === null || _b === void 0 ? void 0 : _b.concatTokens());
|
|
2667
2676
|
}
|
|
@@ -3027,7 +3036,7 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
3027
3036
|
}
|
|
3028
3037
|
return undefined;
|
|
3029
3038
|
}
|
|
3030
|
-
getReference(node, lowFile,
|
|
3039
|
+
getReference(node, lowFile, highSyntax) {
|
|
3031
3040
|
var _a, _b, _c;
|
|
3032
3041
|
if (!(node.get() instanceof Statements.GetReference)) {
|
|
3033
3042
|
return undefined;
|
|
@@ -3041,7 +3050,7 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
3041
3050
|
if (targetName === undefined || sourceName === undefined) {
|
|
3042
3051
|
return undefined;
|
|
3043
3052
|
}
|
|
3044
|
-
const code = `DATA ${targetName}
|
|
3053
|
+
const code = `DATA ${targetName} ${this.refTypeOf(sourceName, node, lowFile, highSyntax)}.\n`;
|
|
3045
3054
|
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), code);
|
|
3046
3055
|
const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, inline.getFirstToken().getStart(), inline.getLastToken().getEnd(), targetName);
|
|
3047
3056
|
const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
|
|
@@ -3118,18 +3127,30 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
3118
3127
|
const uniqueName = this.uniqueName(node.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
|
|
3119
3128
|
const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
3120
3129
|
const sy = func === "LINE_EXISTS" ? "sy-subrc" : "sy-tabix";
|
|
3121
|
-
const code = `DATA ${uniqueName} LIKE sy-subrc.\n` +
|
|
3122
|
-
indentation + `READ TABLE ${tableName} ${condition}TRANSPORTING NO FIELDS.\n` +
|
|
3123
|
-
indentation + uniqueName + ` = ${sy}.\n` +
|
|
3124
|
-
indentation;
|
|
3125
3130
|
let insertAt = node.getFirstToken().getStart();
|
|
3126
3131
|
if (node.get() instanceof statements_1.ElseIf) {
|
|
3127
|
-
// assumption: no side effects in IF conditions
|
|
3132
|
+
// assumption: no side effects in IF conditions, except the READ itself, see below
|
|
3128
3133
|
insertAt = this.findStartOfIf(node, highFile);
|
|
3129
3134
|
if (insertAt === undefined) {
|
|
3130
3135
|
continue;
|
|
3131
3136
|
}
|
|
3132
3137
|
}
|
|
3138
|
+
// the READ sets sy-subrc and sy-tabix, which the built-in function does not,
|
|
3139
|
+
// keep the values for the code that reads them after the inserted READ
|
|
3140
|
+
const keep = [];
|
|
3141
|
+
const reads = this.readSyFields(insertAt, highFile);
|
|
3142
|
+
for (const field of ["sy-subrc", "sy-tabix"]) {
|
|
3143
|
+
if (reads.has(field)) {
|
|
3144
|
+
keep.push({ field, name: this.uniqueName(node.getFirstToken().getStart(), lowFile.getFilename(), highSyntax) });
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
const code = `DATA ${uniqueName} LIKE sy-subrc.\n` +
|
|
3148
|
+
keep.map(k => indentation + `DATA ${k.name} LIKE ${k.field}.\n`).join("") +
|
|
3149
|
+
keep.map(k => indentation + `${k.name} = ${k.field}.\n`).join("") +
|
|
3150
|
+
indentation + `READ TABLE ${tableName} ${condition}TRANSPORTING NO FIELDS.\n` +
|
|
3151
|
+
indentation + uniqueName + ` = ${sy}.\n` +
|
|
3152
|
+
keep.map(k => indentation + `${k.field} = ${k.name}.\n`).join("") +
|
|
3153
|
+
indentation;
|
|
3133
3154
|
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, insertAt, code);
|
|
3134
3155
|
const start = expression.getFirstToken().getStart();
|
|
3135
3156
|
const end = expression.getLastToken().getEnd();
|
|
@@ -3140,6 +3161,46 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
3140
3161
|
}
|
|
3141
3162
|
return undefined;
|
|
3142
3163
|
}
|
|
3164
|
+
/** sy-subrc and sy-tabix, when read from the position on, in the procedure around it or the file outside of one;
|
|
3165
|
+
* inside a loop from the start of the outermost loop, as the next pass reads what this one left */
|
|
3166
|
+
readSyFields(position, highFile) {
|
|
3167
|
+
const structure = highFile.getStructure();
|
|
3168
|
+
const contains = (s) => !position.isBefore(s.getFirstToken().getStart())
|
|
3169
|
+
&& !position.isAfter(s.getLastToken().getStart());
|
|
3170
|
+
let scope = undefined;
|
|
3171
|
+
for (const s of [Structures.Method, Structures.Form, Structures.FunctionModule]) {
|
|
3172
|
+
for (const p of (structure === null || structure === void 0 ? void 0 : structure.findAllStructuresRecursive(s)) || []) {
|
|
3173
|
+
if (contains(p)) {
|
|
3174
|
+
scope = p;
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
scope = scope || structure;
|
|
3179
|
+
let from = position;
|
|
3180
|
+
for (const s of [Structures.Loop, Structures.Do, Structures.While, Structures.Select]) {
|
|
3181
|
+
for (const l of (scope === null || scope === void 0 ? void 0 : scope.findAllStructuresRecursive(s)) || []) {
|
|
3182
|
+
if (contains(l) && l.getFirstToken().getStart().isBefore(from)) {
|
|
3183
|
+
from = l.getFirstToken().getStart();
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
const ret = new Set();
|
|
3188
|
+
for (const s of (scope === null || scope === void 0 ? void 0 : scope.findAllStatementNodes()) || []) {
|
|
3189
|
+
if (s.getLastToken().getStart().isBefore(from)) {
|
|
3190
|
+
continue;
|
|
3191
|
+
}
|
|
3192
|
+
const tokens = s.getTokens();
|
|
3193
|
+
for (let i = 0; i < tokens.length - 2; i++) {
|
|
3194
|
+
if (tokens[i].getStr().toUpperCase() === "SY" && tokens[i + 1].getStr() === "-") {
|
|
3195
|
+
const field = "sy-" + tokens[i + 2].getStr().toLowerCase();
|
|
3196
|
+
if (field === "sy-subrc" || field === "sy-tabix") {
|
|
3197
|
+
ret.add(field);
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
return ret;
|
|
3203
|
+
}
|
|
3143
3204
|
findStartOfIf(node, highFile) {
|
|
3144
3205
|
const structure = highFile.getStructure();
|
|
3145
3206
|
for (const c of (structure === null || structure === void 0 ? void 0 : structure.findAllStructuresRecursive(Structures.If)) || []) {
|
|
@@ -3152,6 +3213,7 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
3152
3213
|
return undefined;
|
|
3153
3214
|
}
|
|
3154
3215
|
newToCreateObject(low, high, lowFile, highSyntax) {
|
|
3216
|
+
var _a;
|
|
3155
3217
|
if (!(low.get() instanceof _statement_1.Unknown)) {
|
|
3156
3218
|
return undefined;
|
|
3157
3219
|
}
|
|
@@ -3167,7 +3229,7 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
3167
3229
|
&& found
|
|
3168
3230
|
&& source.concatTokens() === found.concatTokens()
|
|
3169
3231
|
&& target.findDirectExpression(Expressions.InlineData) === undefined) {
|
|
3170
|
-
const abap = this.newParameters(found, target.concatTokens(), highSyntax, lowFile);
|
|
3232
|
+
const abap = (_a = this.newDataReference(high, target, found, lowFile, highSyntax)) !== null && _a !== void 0 ? _a : this.newParameters(found, target.concatTokens(), highSyntax, lowFile);
|
|
3171
3233
|
if (abap !== undefined) {
|
|
3172
3234
|
fix = edit_helper_1.EditHelper.replaceRange(lowFile, high.getFirstToken().getStart(), high.getLastToken().getEnd(), abap);
|
|
3173
3235
|
}
|
|
@@ -3206,6 +3268,42 @@ ${indentation} output = ${uniqueName}.\n`;
|
|
|
3206
3268
|
return undefined;
|
|
3207
3269
|
}
|
|
3208
3270
|
}
|
|
3271
|
+
/** NEW into a data reference creates data, CREATE OBJECT cannot: "target = NEW #|type( [value] )"
|
|
3272
|
+
* with a variable typed REF TO a data type becomes CREATE DATA plus the assignment of the value */
|
|
3273
|
+
newDataReference(high, target, found, lowFile, highSyntax) {
|
|
3274
|
+
var _a, _b, _c;
|
|
3275
|
+
if (target.getChildren().length !== 1 || !(((_a = target.getFirstChild()) === null || _a === void 0 ? void 0 : _a.get()) instanceof Expressions.TargetField)) {
|
|
3276
|
+
return undefined;
|
|
3277
|
+
}
|
|
3278
|
+
const children = found.getChildren();
|
|
3279
|
+
const value = found.findDirectExpression(Expressions.Source);
|
|
3280
|
+
// NEW, the type, "(", optionally one value, ")"
|
|
3281
|
+
if (children.length !== 4 + (value ? 1 : 0)) {
|
|
3282
|
+
return undefined;
|
|
3283
|
+
}
|
|
3284
|
+
const name = target.concatTokens();
|
|
3285
|
+
const spag = highSyntax.spaghetti.lookupPosition(high.getFirstToken().getStart(), lowFile.getFilename());
|
|
3286
|
+
const type = (_b = spag === null || spag === void 0 ? void 0 : spag.findVariable(name)) === null || _b === void 0 ? void 0 : _b.getType();
|
|
3287
|
+
if (!(type instanceof basic_1.DataReference)) {
|
|
3288
|
+
return undefined;
|
|
3289
|
+
}
|
|
3290
|
+
const typeName = (_c = found.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _c === void 0 ? void 0 : _c.concatTokens();
|
|
3291
|
+
const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
|
|
3292
|
+
let abap = `CREATE DATA ${name}` + (typeName === "#" ? "" : ` TYPE ${typeName}`) + ".";
|
|
3293
|
+
if (value === undefined) {
|
|
3294
|
+
return abap;
|
|
3295
|
+
}
|
|
3296
|
+
else if (type.getType().isGeneric() === false) {
|
|
3297
|
+
return abap + `\n${indentation}${name}->* = ${value.concatTokens()}.`;
|
|
3298
|
+
}
|
|
3299
|
+
// a generic reference cannot be dereferenced as a target below v756
|
|
3300
|
+
const fs = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax, true);
|
|
3301
|
+
abap = `FIELD-SYMBOLS ${fs} TYPE any.\n` +
|
|
3302
|
+
indentation + abap + "\n" +
|
|
3303
|
+
indentation + `ASSIGN ${name}->* TO ${fs}.\n` +
|
|
3304
|
+
indentation + `${fs} = ${value.concatTokens()}.`;
|
|
3305
|
+
return abap;
|
|
3306
|
+
}
|
|
3209
3307
|
newParameters(found, name, highSyntax, lowFile) {
|
|
3210
3308
|
var _a, _b, _c;
|
|
3211
3309
|
const typeToken = (_a = found.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
|
|
@@ -63,6 +63,8 @@ class EmptyStructureConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
|
63
63
|
this.try = true;
|
|
64
64
|
/** Checks for empty WHEN blocks */
|
|
65
65
|
this.when = true;
|
|
66
|
+
/** Checks for empty CATCH blocks, unless the CATCH has pragma ##NO_HANDLER or pseudo comment "#EC NO_HANDLER */
|
|
67
|
+
this.catch = false;
|
|
66
68
|
// todo, other category containing ELSE
|
|
67
69
|
}
|
|
68
70
|
}
|
|
@@ -175,6 +177,21 @@ result = xsdbool( sy-subrc = 0 ).`,
|
|
|
175
177
|
}
|
|
176
178
|
}
|
|
177
179
|
}
|
|
180
|
+
if (this.getConfig().catch === true) {
|
|
181
|
+
for (const c of stru.findAllStructuresRecursive(Structures.Catch)) {
|
|
182
|
+
// a comment in the block does not count, the extended check (SLIN) reports it too
|
|
183
|
+
if (c.findDirectStructure(Structures.Body) !== undefined) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const catchStatement = c.getFirstStatement();
|
|
187
|
+
if (catchStatement === undefined || this.isNoHandler(catchStatement, statements)) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const token = c.getFirstToken();
|
|
191
|
+
const issue = issue_1.Issue.atToken(file, token, this.getDescription(c.get().constructor.name), this.getMetadata().key, this.conf.severity);
|
|
192
|
+
issues.push(issue);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
178
195
|
if (this.getConfig().when === true) {
|
|
179
196
|
const tries = stru.findAllStructuresRecursive(Structures.When);
|
|
180
197
|
for (const t of tries) {
|
|
@@ -188,6 +205,15 @@ result = xsdbool( sy-subrc = 0 ).`,
|
|
|
188
205
|
}
|
|
189
206
|
return issues;
|
|
190
207
|
}
|
|
208
|
+
isNoHandler(catchStatement, statements) {
|
|
209
|
+
if (catchStatement.getPragmas().some(p => p.getStr().toUpperCase() === "##NO_HANDLER")) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
const next = statements[statements.indexOf(catchStatement) + 1];
|
|
213
|
+
return (next === null || next === void 0 ? void 0 : next.get()) instanceof _statement_1.Comment
|
|
214
|
+
&& next.getStart().getRow() === catchStatement.getEnd().getRow()
|
|
215
|
+
&& next.concatTokens().toUpperCase().replace(/\s/g, "").includes("\"#ECNO_HANDLER");
|
|
216
|
+
}
|
|
191
217
|
}
|
|
192
218
|
exports.EmptyStructure = EmptyStructure;
|
|
193
219
|
//# sourceMappingURL=empty_structure.js.map
|
|
@@ -138,7 +138,7 @@ cl_abap_typedescr=>describe_by_name(
|
|
|
138
138
|
const methodSource = statNode.findDirectExpression(Expressions.MethodSource);
|
|
139
139
|
let methodSourceStr = methodSource === null || methodSource === void 0 ? void 0 : methodSource.concatTokens();
|
|
140
140
|
const methodBody = statNode.findDirectExpression(Expressions.MethodCallBody);
|
|
141
|
-
let methodBodyStr
|
|
141
|
+
let methodBodyStr;
|
|
142
142
|
if (methodBody) {
|
|
143
143
|
const methodCallParam = methodBody.findDirectExpression(Expressions.MethodCallParam);
|
|
144
144
|
if (methodCallParam && methodCallParam.getFirstToken().getStr() === "(") {
|
|
@@ -215,7 +215,7 @@ class KeywordCase extends _abap_rule_1.ABAPRule {
|
|
|
215
215
|
const firstToken = tokens[0].token;
|
|
216
216
|
const lastToken = tokens[tokens.length - 1].token;
|
|
217
217
|
const firstTokenValue = firstToken.getStr();
|
|
218
|
-
let description
|
|
218
|
+
let description;
|
|
219
219
|
if (first.keyword === true) {
|
|
220
220
|
description = `Keyword should be ${this.conf.style} case: "${firstTokenValue}"`;
|
|
221
221
|
}
|
|
@@ -86,7 +86,7 @@ ENDIF.
|
|
|
86
86
|
issues.push(...this.analyzeCondition(file, cond));
|
|
87
87
|
}
|
|
88
88
|
for (const sub of structure.findAllExpressionsMulti([Expressions.CondSub, Expressions.ComponentCondSub])) {
|
|
89
|
-
let cond
|
|
89
|
+
let cond;
|
|
90
90
|
if (sub.get() instanceof Expressions.CondSub) {
|
|
91
91
|
cond = sub.findDirectExpressions(Expressions.Cond);
|
|
92
92
|
}
|
|
@@ -486,13 +486,9 @@ ENDIF.`,
|
|
|
486
486
|
}
|
|
487
487
|
else if (statement instanceof Statements.ClassDefinitionLoad ||
|
|
488
488
|
statement instanceof Statements.InterfaceLoad) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
token = statementNode.getChildren()[2].getFirstToken();
|
|
495
|
-
}
|
|
489
|
+
const token = statement instanceof Statements.ClassDefinitionLoad
|
|
490
|
+
? statementNode.getChildren()[3].getFirstToken()
|
|
491
|
+
: statementNode.getChildren()[2].getFirstToken();
|
|
496
492
|
let startPosition = token.getStart();
|
|
497
493
|
startPosition = new position_1.Position(startPosition.getRow(), startPosition.getCol() - 1);
|
|
498
494
|
return edit_helper_1.EditHelper.deleteRange(file, startPosition, token.getEnd());
|
|
@@ -36,7 +36,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.ParserMissingSpace = exports.ParserMissingSpaceConf = void 0;
|
|
37
37
|
const Expressions = __importStar(require("../abap/2_statements/expressions"));
|
|
38
38
|
const issue_1 = require("../issue");
|
|
39
|
-
const position_1 = require("../position");
|
|
40
39
|
const _abap_rule_1 = require("./_abap_rule");
|
|
41
40
|
const nodes_1 = require("../abap/nodes");
|
|
42
41
|
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
@@ -69,7 +68,7 @@ This rule makes sure the spaces are consistently required across the language.`,
|
|
|
69
68
|
}
|
|
70
69
|
runParsed(file) {
|
|
71
70
|
const issues = [];
|
|
72
|
-
let start
|
|
71
|
+
let start;
|
|
73
72
|
for (const statement of file.getStatements()) {
|
|
74
73
|
const missing = this.missingSpace(statement);
|
|
75
74
|
if (missing) {
|
|
@@ -93,14 +93,8 @@ From 752 and up`,
|
|
|
93
93
|
}
|
|
94
94
|
getFix(file, statement, withExporting) {
|
|
95
95
|
const children = statement.getChildren();
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const fixText = "( " + children[5].concatTokens() + " ).";
|
|
99
|
-
contentFix = edit_helper_1.EditHelper.replaceRange(file, children[3].getLastToken().getEnd(), statement.getEnd(), fixText);
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
contentFix = edit_helper_1.EditHelper.replaceRange(file, children[3].getLastToken().getEnd(), statement.getEnd(), "( ).");
|
|
103
|
-
}
|
|
96
|
+
const fixText = withExporting ? "( " + children[5].concatTokens() + " )." : "( ).";
|
|
97
|
+
const contentFix = edit_helper_1.EditHelper.replaceRange(file, children[3].getLastToken().getEnd(), statement.getEnd(), fixText);
|
|
104
98
|
const replaceType = edit_helper_1.EditHelper.replaceToken(file, children[2].getFirstToken(), "NEW");
|
|
105
99
|
return edit_helper_1.EditHelper.merge(contentFix, replaceType);
|
|
106
100
|
}
|
|
@@ -60,7 +60,8 @@ class RedundantConversion {
|
|
|
60
60
|
key: "redundant_conversion",
|
|
61
61
|
title: "Redundant Conversion",
|
|
62
62
|
shortDescription: `Find redundant CONV expressions`,
|
|
63
|
-
extendedInformation: `Reports CONV expressions whose operand already has the conversion's target type
|
|
63
|
+
extendedInformation: `Reports CONV expressions whose operand already has the conversion's target type,
|
|
64
|
+
including the result of a built-in function with a fixed result type, like to_upper( ) or strlen( ).`,
|
|
64
65
|
tags: [_irule_1.RuleTag.Quickfix],
|
|
65
66
|
badExample: `DATA text TYPE string.
|
|
66
67
|
text = CONV string( text ).`,
|
|
@@ -97,7 +98,8 @@ text = text.`,
|
|
|
97
98
|
if (structure === undefined) {
|
|
98
99
|
continue;
|
|
99
100
|
}
|
|
100
|
-
|
|
101
|
+
// recursive, a CONV can be the argument of a built-in function, which is itself a Source
|
|
102
|
+
for (const source of structure.findAllExpressionsRecursive(Expressions.Source)) {
|
|
101
103
|
if (source.getFirstToken().getStr().toUpperCase() !== "CONV") {
|
|
102
104
|
continue;
|
|
103
105
|
}
|
|
@@ -156,6 +158,15 @@ text = text.`,
|
|
|
156
158
|
}
|
|
157
159
|
sourceType(source, scope) {
|
|
158
160
|
let context;
|
|
161
|
+
// boolc( <condition> ) is not a MethodCallChain, xsdbool( ) has the same shape but is not reported
|
|
162
|
+
const first = source.getFirstToken();
|
|
163
|
+
if (first.getStr().toUpperCase() === "BOOLC"
|
|
164
|
+
&& source.findDirectExpression(Expressions.Cond) !== undefined
|
|
165
|
+
&& source.getChildren().length === 4
|
|
166
|
+
&& scope.getData().references.some(reference => reference.referenceType === _reference_1.ReferenceType.BuiltinMethodReference
|
|
167
|
+
&& reference.position.getStart().equals(first.getStart()))) {
|
|
168
|
+
return basic_1.StringType.get();
|
|
169
|
+
}
|
|
159
170
|
for (const child of source.getChildren()) {
|
|
160
171
|
if (!(child instanceof nodes_1.ExpressionNode)) {
|
|
161
172
|
continue;
|
|
@@ -166,6 +177,15 @@ text = text.`,
|
|
|
166
177
|
return undefined;
|
|
167
178
|
}
|
|
168
179
|
}
|
|
180
|
+
else if (child.get() instanceof Expressions.MethodCallChain) {
|
|
181
|
+
if (source.getChildren().length !== 1) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
context = this.builtinResultType(child, scope);
|
|
185
|
+
if (context === undefined) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
169
189
|
else if (child.get() instanceof Expressions.Constant) {
|
|
170
190
|
context = this.infer(context, constant_1.Constant.runSyntax(child));
|
|
171
191
|
}
|
|
@@ -186,6 +206,32 @@ text = text.`,
|
|
|
186
206
|
}
|
|
187
207
|
return context;
|
|
188
208
|
}
|
|
209
|
+
/** The result type of a call to a built-in function whose result type does not depend on its arguments,
|
|
210
|
+
* undefined for anything else, e.g. abs( ) or xsdbool( ), or a method of the same name */
|
|
211
|
+
builtinResultType(chain, scope) {
|
|
212
|
+
var _a;
|
|
213
|
+
const children = chain.getChildren();
|
|
214
|
+
if (children.length !== 1 || !(children[0].get() instanceof Expressions.MethodCall)) {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
const name = (_a = children[0].findDirectExpression(Expressions.MethodName)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
|
|
218
|
+
if (name === undefined) {
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
const isBuiltin = scope.getData().references.some(reference => reference.referenceType === _reference_1.ReferenceType.BuiltinMethodReference
|
|
222
|
+
&& reference.position.getStart().equals(name.getStart()));
|
|
223
|
+
if (isBuiltin === false) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
const upper = name.getStr().toUpperCase();
|
|
227
|
+
if (RedundantConversion.stringResult.has(upper)) {
|
|
228
|
+
return basic_1.StringType.get();
|
|
229
|
+
}
|
|
230
|
+
else if (RedundantConversion.integerResult.has(upper)) {
|
|
231
|
+
return basic_1.IntegerType.get();
|
|
232
|
+
}
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
189
235
|
fieldChainType(fieldChain, scope) {
|
|
190
236
|
var _a;
|
|
191
237
|
const operand = (_a = scope.getData().references.find(reference => reference.referenceType === _reference_1.ReferenceType.DataReadReference
|
|
@@ -250,4 +296,15 @@ text = text.`,
|
|
|
250
296
|
}
|
|
251
297
|
}
|
|
252
298
|
exports.RedundantConversion = RedundantConversion;
|
|
299
|
+
// built-in functions that always return a string, whatever their arguments
|
|
300
|
+
RedundantConversion.stringResult = new Set([
|
|
301
|
+
"BOOLC", "CONCAT_LINES_OF", "CONDENSE", "ESCAPE", "FROM_MIXED", "INSERT", "REPEAT", "REPLACE",
|
|
302
|
+
"REVERSE", "SEGMENT", "SHIFT_LEFT", "SHIFT_RIGHT", "SUBSTRING", "SUBSTRING_AFTER", "SUBSTRING_BEFORE",
|
|
303
|
+
"SUBSTRING_FROM", "SUBSTRING_TO", "TO_LOWER", "TO_MIXED", "TO_UPPER", "TRANSLATE"
|
|
304
|
+
]);
|
|
305
|
+
// built-in functions that always return an i
|
|
306
|
+
RedundantConversion.integerResult = new Set([
|
|
307
|
+
"CHARLEN", "COUNT", "COUNT_ANY_NOT_OF", "COUNT_ANY_OF", "DBMAXLEN", "DISTANCE", "FIND", "FIND_ANY_NOT_OF",
|
|
308
|
+
"FIND_ANY_OF", "FIND_END", "LINES", "NUMOFCHAR", "STRLEN", "XSTRLEN"
|
|
309
|
+
]);
|
|
253
310
|
//# sourceMappingURL=redundant_conversion.js.map
|
|
@@ -67,6 +67,7 @@ class XMLConsistency {
|
|
|
67
67
|
* Naming for CLAS and INTF objects
|
|
68
68
|
* QUAN fields in TABL objects have reference table and field values
|
|
69
69
|
* Transparent TABL objects have delivery class and size category set
|
|
70
|
+
* TABL fields with a built-in type carry the INTTYPE abapGit serializes for it, for the confirmed pairs
|
|
70
71
|
* Lock parameter names in ENQU objects are max 16 characters
|
|
71
72
|
* Texts and translations do not exceed maximum allowed length.`,
|
|
72
73
|
tags: [_irule_1.RuleTag.Naming, _irule_1.RuleTag.Syntax],
|
|
@@ -326,15 +327,29 @@ class XMLConsistency {
|
|
|
326
327
|
}
|
|
327
328
|
runTable(obj, file) {
|
|
328
329
|
var _a, _b, _c, _d;
|
|
329
|
-
var _e;
|
|
330
|
+
var _e, _f;
|
|
330
331
|
const issues = [];
|
|
331
332
|
for (const field of (_e = obj.getFields()) !== null && _e !== void 0 ? _e : []) {
|
|
332
333
|
if (field.DATATYPE === "QUAN" && (!((_a = field.REFTABLE) === null || _a === void 0 ? void 0 : _a.trim()) || !((_b = field.REFFIELD) === null || _b === void 0 ? void 0 : _b.trim()))) {
|
|
333
334
|
const message = `QUAN field ${field.FIELDNAME} must have REFTABLE and REFFIELD set`;
|
|
334
335
|
issues.push(issue_1.Issue.atRow(file, 1, message, this.getMetadata().key, this.conf.severity));
|
|
335
336
|
}
|
|
337
|
+
const expected = field.DATATYPE ? XMLConsistency.intTypes[field.DATATYPE] : undefined;
|
|
338
|
+
if (expected !== undefined && field.INTTYPE !== undefined && field.INTTYPE !== expected) {
|
|
339
|
+
const message = `Field ${field.FIELDNAME}: INTTYPE "${field.INTTYPE}" does not match DATATYPE ${field.DATATYPE}, ` +
|
|
340
|
+
`abapGit serializes "${expected}"`;
|
|
341
|
+
issues.push(issue_1.Issue.atRow(file, 1, message, this.getMetadata().key, this.conf.severity));
|
|
342
|
+
}
|
|
336
343
|
}
|
|
337
344
|
if (obj.getTableCategory() === Objects.TableCategory.Transparent) {
|
|
345
|
+
// Confirmed reserved database field names, see issue #4331, DAY and HOUR reported by activation.
|
|
346
|
+
// TRESE also contains names such as TEXT and LENGTH which are allowed here.
|
|
347
|
+
const reservedFieldNames = ["ZONE", "HANDLER", "SECTION", "PARAMETER", "DAY", "HOUR"];
|
|
348
|
+
for (const field of (_f = obj.getFields()) !== null && _f !== void 0 ? _f : []) {
|
|
349
|
+
if (reservedFieldNames.includes(field.FIELDNAME.toUpperCase())) {
|
|
350
|
+
issues.push(issue_1.Issue.atRow(file, 1, `Table field name "${field.FIELDNAME}" is reserved`, this.getMetadata().key, this.conf.severity));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
338
353
|
if (!((_c = obj.getDeliveryClass()) === null || _c === void 0 ? void 0 : _c.trim())) {
|
|
339
354
|
const message = `Transparent table must have delivery class(CONTFLAG in DD02V) set`;
|
|
340
355
|
issues.push(issue_1.Issue.atRow(file, 1, message, this.getMetadata().key, this.conf.transparentTableSeverity));
|
|
@@ -348,4 +363,13 @@ class XMLConsistency {
|
|
|
348
363
|
}
|
|
349
364
|
}
|
|
350
365
|
exports.XMLConsistency = XMLConsistency;
|
|
366
|
+
// INTTYPE that abapGit serializes for a built-in DATATYPE, confirmed by exported tables;
|
|
367
|
+
// a different value activates, but shows as a diff on every pull
|
|
368
|
+
XMLConsistency.intTypes = {
|
|
369
|
+
"CHAR": "C", "CLNT": "C", "CUKY": "C", "LANG": "C",
|
|
370
|
+
"DATS": "D", "TIMS": "T",
|
|
371
|
+
"INT4": "X", "RAW": "X",
|
|
372
|
+
"DEC": "P", "CURR": "P",
|
|
373
|
+
"STRG": "g", "SSTR": "g", "RSTR": "y",
|
|
374
|
+
};
|
|
351
375
|
//# sourceMappingURL=xml_consistency.js.map
|