@abaplint/core 2.120.69 → 2.120.71
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/string_template_formatting.js +1 -1
- package/build/src/abap/5_syntax/expressions/compare.js +48 -0
- package/build/src/registry.js +1 -1
- package/build/src/rules/cds_check_syntax.js +1 -1
- package/build/src/rules/redundant_conversion.js +59 -2
- package/build/src/rules/xml_consistency.js +9 -1
- package/package.json +1 -1
|
@@ -28,7 +28,7 @@ class StringTemplateFormatting extends combi_1.Expression {
|
|
|
28
28
|
const alpha = (0, combi_1.ver)(version_1.Release.v740sp02, (0, combi_1.seq)("ALPHA =", alphaOptions), { also: combi_1.AlsoIn.OpenABAP });
|
|
29
29
|
const xsd = (0, combi_1.ver)(version_1.Release.v740sp02, (0, combi_1.seq)("XSD =", zeroXSDOptions));
|
|
30
30
|
const country = (0, combi_1.seq)("COUNTRY =", _1.Source);
|
|
31
|
-
const formatting = (0, combi_1.altPrio)((0, combi_1.seq)("TIME =", dateTimeOptions), (0, combi_1.seq)("DATE =", dateTimeOptions), (0, combi_1.seq)("CASE =", caseOptions), (0, combi_1.seq)("
|
|
31
|
+
const formatting = (0, combi_1.altPrio)((0, combi_1.seq)("TIME =", dateTimeOptions), (0, combi_1.seq)("DATE =", dateTimeOptions), (0, combi_1.seq)("CASE =", caseOptions), (0, combi_1.seq)("ZERO =", zeroXSDOptions), xsd, (0, combi_1.seq)("STYLE =", styleOptions), (0, combi_1.seq)("CURRENCY =", _1.Source), (0, combi_1.per)(sign, number, decimals, width, pad, alpha, align, country, (0, combi_1.seq)("EXPONENT =", _1.Source)), (0, combi_1.per)(timezone, timestamp));
|
|
32
32
|
return formatting;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -35,12 +35,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.Compare = void 0;
|
|
37
37
|
const Expressions = __importStar(require("../../2_statements/expressions"));
|
|
38
|
+
const nodes_1 = require("../../nodes");
|
|
38
39
|
const source_1 = require("./source");
|
|
39
40
|
const method_call_chain_1 = require("./method_call_chain");
|
|
40
41
|
const source_field_symbol_1 = require("./source_field_symbol");
|
|
41
42
|
const _syntax_input_1 = require("../_syntax_input");
|
|
42
43
|
const basic_1 = require("../../types/basic");
|
|
43
44
|
const _type_utils_1 = require("../_type_utils");
|
|
45
|
+
const _builtin_1 = require("../_builtin");
|
|
46
|
+
const _object_oriented_1 = require("../_object_oriented");
|
|
44
47
|
class Compare {
|
|
45
48
|
static runSyntax(node, input) {
|
|
46
49
|
var _a;
|
|
@@ -55,6 +58,11 @@ class Compare {
|
|
|
55
58
|
for (const t of node.findDirectExpressions(Expressions.MethodCallChain)) {
|
|
56
59
|
method_call_chain_1.MethodCallChain.runSyntax(t, input);
|
|
57
60
|
}
|
|
61
|
+
const initialMessage = this.checkInitialOperand(node, input);
|
|
62
|
+
if (initialMessage !== undefined) {
|
|
63
|
+
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), initialMessage));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
58
66
|
const typeUtils = new _type_utils_1.TypeUtils(input.scope);
|
|
59
67
|
const compareOperator = (_a = node.findDirectExpression(Expressions.CompareOperator)) === null || _a === void 0 ? void 0 : _a.concatTokens().toUpperCase();
|
|
60
68
|
if (compareOperator === "CS"
|
|
@@ -78,6 +86,46 @@ class Compare {
|
|
|
78
86
|
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
|
|
79
87
|
}
|
|
80
88
|
}
|
|
89
|
+
// a built-in function with a character-like result cannot be the operand of IS [NOT] INITIAL,
|
|
90
|
+
// "Unexpected operator IS" on a system, while one with a numeric result, lines( ), can
|
|
91
|
+
static checkInitialOperand(node, input) {
|
|
92
|
+
var _a, _b, _c, _d;
|
|
93
|
+
if (input.scope.getOpenABAP()) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
const tokens = node.getChildren().filter(c => c instanceof nodes_1.TokenNode).map(c => c.getFirstToken().getStr().toUpperCase());
|
|
97
|
+
if (tokens.includes("IS") === false || tokens[tokens.length - 1] !== "INITIAL") {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
const chain = (_a = node.findDirectExpression(Expressions.Source)) === null || _a === void 0 ? void 0 : _a.getChildren();
|
|
101
|
+
// xsdbool( ) and boolc( ) are their own form in the Source grammar, a condition in parentheses
|
|
102
|
+
const first = (_b = chain === null || chain === void 0 ? void 0 : chain[0]) === null || _b === void 0 ? void 0 : _b.getFirstToken().getStr().toUpperCase();
|
|
103
|
+
if ((chain === null || chain === void 0 ? void 0 : chain.length) === 4 && chain[0] instanceof nodes_1.TokenNode && (first === "XSDBOOL" || first === "BOOLC")) {
|
|
104
|
+
return `Built-in function "${chain[0].getFirstToken().getStr()}" cannot be the operand of IS INITIAL`;
|
|
105
|
+
}
|
|
106
|
+
if ((chain === null || chain === void 0 ? void 0 : chain.length) !== 1 || !(chain[0] instanceof nodes_1.ExpressionNode) || !(chain[0].get() instanceof Expressions.MethodCallChain)) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const calls = chain[0].getChildren();
|
|
110
|
+
if (calls.length !== 1 || !(calls[0] instanceof nodes_1.ExpressionNode) || !(calls[0].get() instanceof Expressions.MethodCall)) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
const name = (_c = calls[0].findDirectExpression(Expressions.MethodName)) === null || _c === void 0 ? void 0 : _c.concatTokens();
|
|
114
|
+
const builtin = _builtin_1.BuiltIn.searchBuiltin(name);
|
|
115
|
+
if (name === undefined || builtin === undefined) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
// a method of the same name hides the built-in function
|
|
119
|
+
const enclosing = input.scope.findClassDefinition(input.scope.getEnclosingClassName());
|
|
120
|
+
if (enclosing !== undefined && new _object_oriented_1.ObjectOriented(input.scope).searchMethodName(enclosing, name).method !== undefined) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
const returning = (_d = builtin.getParameters().getReturning()) === null || _d === void 0 ? void 0 : _d.getType();
|
|
124
|
+
if (returning instanceof basic_1.StringType || returning instanceof basic_1.CharacterType) {
|
|
125
|
+
return `Built-in function "${name}" cannot be the operand of IS INITIAL`;
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
81
129
|
}
|
|
82
130
|
exports.Compare = Compare;
|
|
83
131
|
//# sourceMappingURL=compare.js.map
|
package/build/src/registry.js
CHANGED
|
@@ -15,7 +15,7 @@ exports.CDSCheckSyntaxConf = CDSCheckSyntaxConf;
|
|
|
15
15
|
const KNOWN_QUAN_DATA_ELEMENTS = ["MENGE_D"];
|
|
16
16
|
const KNOWN_CURR_DATA_ELEMENTS = ["BWERT", "DZWERT"];
|
|
17
17
|
// Reserved names, cannot be used as element names, see DDIC table TRESE
|
|
18
|
-
const RESERVED_ELEMENT_NAMES = ["BEGIN", "NUMBER", "POSITION"];
|
|
18
|
+
const RESERVED_ELEMENT_NAMES = ["BEGIN", "NUMBER", "POSITION", "ZONE", "HANDLER", "SECTION"];
|
|
19
19
|
const MAX_LABEL_LENGTH = 40;
|
|
20
20
|
// Annotations which are not supported in CDS view entities
|
|
21
21
|
const VIEW_ENTITY_FORBIDDEN_ANNOTATIONS = ["Semantics.unitOfMeasure", "Semantics.currencyCode"];
|
|
@@ -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
|
|
@@ -326,7 +326,7 @@ class XMLConsistency {
|
|
|
326
326
|
}
|
|
327
327
|
runTable(obj, file) {
|
|
328
328
|
var _a, _b, _c, _d;
|
|
329
|
-
var _e;
|
|
329
|
+
var _e, _f;
|
|
330
330
|
const issues = [];
|
|
331
331
|
for (const field of (_e = obj.getFields()) !== null && _e !== void 0 ? _e : []) {
|
|
332
332
|
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()))) {
|
|
@@ -335,6 +335,14 @@ class XMLConsistency {
|
|
|
335
335
|
}
|
|
336
336
|
}
|
|
337
337
|
if (obj.getTableCategory() === Objects.TableCategory.Transparent) {
|
|
338
|
+
// Confirmed reserved database field names, see issue #4331, DAY and HOUR reported by activation.
|
|
339
|
+
// TRESE also contains names such as TEXT and LENGTH which are allowed here.
|
|
340
|
+
const reservedFieldNames = ["ZONE", "HANDLER", "SECTION", "PARAMETER", "DAY", "HOUR"];
|
|
341
|
+
for (const field of (_f = obj.getFields()) !== null && _f !== void 0 ? _f : []) {
|
|
342
|
+
if (reservedFieldNames.includes(field.FIELDNAME.toUpperCase())) {
|
|
343
|
+
issues.push(issue_1.Issue.atRow(file, 1, `Table field name "${field.FIELDNAME}" is reserved`, this.getMetadata().key, this.conf.severity));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
338
346
|
if (!((_c = obj.getDeliveryClass()) === null || _c === void 0 ? void 0 : _c.trim())) {
|
|
339
347
|
const message = `Transparent table must have delivery class(CONTFLAG in DD02V) set`;
|
|
340
348
|
issues.push(issue_1.Issue.atRow(file, 1, message, this.getMetadata().key, this.conf.transparentTableSeverity));
|