@abaplint/core 2.120.75 → 2.120.77
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 +3 -0
- 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/source.js +3 -9
- package/build/src/abap/5_syntax/expressions/target.js +14 -2
- 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/registry.js +1 -1
- package/build/src/rules/redundant_conversion.js +3 -0
- package/build/src/rules/use_message_class.js +64 -1
- package/package.json +1 -1
package/build/abaplint.d.ts
CHANGED
|
@@ -624,6 +624,8 @@ export declare class BuiltIn {
|
|
|
624
624
|
static searchBuiltin(name: string | undefined): IMethodDefinition | undefined;
|
|
625
625
|
private static readonly unnamedArgument;
|
|
626
626
|
static hasUnnamedArgument(name: string | undefined): boolean;
|
|
627
|
+
private static readonly argumentTypedReturn;
|
|
628
|
+
static returnsArgumentType(name: string | undefined): boolean;
|
|
627
629
|
static isPredicate(name: string | undefined): boolean | undefined;
|
|
628
630
|
getTypes(): TypedIdentifier[];
|
|
629
631
|
get(extras: string[]): TypedIdentifier[];
|
|
@@ -7811,6 +7813,7 @@ export declare class SyntaxLogic {
|
|
|
7811
7813
|
private readonly object;
|
|
7812
7814
|
private readonly reg;
|
|
7813
7815
|
private scope;
|
|
7816
|
+
private loopTables;
|
|
7814
7817
|
private helpers;
|
|
7815
7818
|
constructor(reg: IRegistry, object: ABAPObject);
|
|
7816
7819
|
run(): ISyntaxResult;
|
|
@@ -124,6 +124,12 @@ class BuiltIn {
|
|
|
124
124
|
}
|
|
125
125
|
return BuiltIn.unnamedArgument.has(name.toUpperCase());
|
|
126
126
|
}
|
|
127
|
+
static returnsArgumentType(name) {
|
|
128
|
+
if (name === undefined) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
return BuiltIn.argumentTypedReturn.has(name.toUpperCase());
|
|
132
|
+
}
|
|
127
133
|
static isPredicate(name) {
|
|
128
134
|
if (name === undefined) {
|
|
129
135
|
return undefined;
|
|
@@ -1231,4 +1237,6 @@ BuiltIn.unnamedArgument = new Set([
|
|
|
1231
1237
|
"FRAC", "LINES", "LOG", "LOG10", "NUMOFCHAR", "SIGN", "SIN", "SINH", "SQRT", "STRLEN", "TAN", "TANH",
|
|
1232
1238
|
"TRUNC", "XSTRLEN"
|
|
1233
1239
|
]);
|
|
1240
|
+
// numeric functions returning a value of the type of the argument
|
|
1241
|
+
BuiltIn.argumentTypedReturn = new Set(["ABS", "CEIL", "FLOOR", "FRAC", "SIGN", "TRUNC"]);
|
|
1234
1242
|
//# sourceMappingURL=_builtin.js.map
|
|
@@ -0,0 +1,126 @@
|
|
|
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.LoopTableChange = void 0;
|
|
37
|
+
const Expressions = __importStar(require("../2_statements/expressions"));
|
|
38
|
+
const Statements = __importStar(require("../2_statements/statements"));
|
|
39
|
+
const nodes_1 = require("../nodes");
|
|
40
|
+
const basic_1 = require("../types/basic");
|
|
41
|
+
const _scope_type_1 = require("./_scope_type");
|
|
42
|
+
const _syntax_input_1 = require("./_syntax_input");
|
|
43
|
+
/** In a method, the body of the table an enclosing LOOP AT walks cannot be replaced or freed,
|
|
44
|
+
* "you cannot access the entire table body in change mode in a loop. If this is known statically,
|
|
45
|
+
* a syntax error occurs in classes", see the ABAP keyword documentation for LOOP AT itab.
|
|
46
|
+
* Only tables named directly are handled, a field symbol or a reference is a runtime matter. */
|
|
47
|
+
class LoopTableChange {
|
|
48
|
+
constructor() {
|
|
49
|
+
this.tables = [];
|
|
50
|
+
}
|
|
51
|
+
/** returns true if the LOOP structure was pushed, then pop() must be called after it */
|
|
52
|
+
push(node, scope) {
|
|
53
|
+
var _a, _b;
|
|
54
|
+
if (scope.getType() !== _scope_type_1.ScopeType.Method) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const loop = node.findDirectStatement(Statements.Loop);
|
|
58
|
+
if (loop === undefined || loop.findDirectExpression(Expressions.LoopGroupBy) !== undefined) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const chain = (_b = (_a = loop.findDirectExpression(Expressions.LoopSource)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(Expressions.SimpleSource2)) === null || _b === void 0 ? void 0 : _b.findDirectExpression(Expressions.FieldChain);
|
|
62
|
+
const children = (chain === null || chain === void 0 ? void 0 : chain.getChildren()) || [];
|
|
63
|
+
if (children.length !== 1 && !(children.length === 2 && children[1].get() instanceof Expressions.TableBody)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const found = LoopTableChange.findTable(children[0], scope);
|
|
67
|
+
if (found === undefined) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
this.tables.push(found);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
pop() {
|
|
74
|
+
this.tables.pop();
|
|
75
|
+
}
|
|
76
|
+
check(node, input) {
|
|
77
|
+
if (this.tables.length === 0 || input.scope.getType() !== _scope_type_1.ScopeType.Method) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// DELETE itab WHERE is not refused, abapGit does it in zcl_abapgit_syntax_xml->order_matches,
|
|
81
|
+
// SORT is left out, it is not certain that it is checked statically
|
|
82
|
+
const s = node.get();
|
|
83
|
+
if (!(s instanceof Statements.Clear)
|
|
84
|
+
&& !(s instanceof Statements.Free)
|
|
85
|
+
&& !(s instanceof Statements.Refresh)
|
|
86
|
+
&& !(s instanceof Statements.Move)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
for (const target of node.findDirectExpressions(Expressions.Target)) {
|
|
90
|
+
const children = target.getChildren();
|
|
91
|
+
const body = children.length === 2 && children[1].get() instanceof Expressions.TableBody;
|
|
92
|
+
if (children.length !== 1 && body === false) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
else if (!(children[0].get() instanceof Expressions.TargetField)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const found = LoopTableChange.findTable(children[0], input.scope);
|
|
99
|
+
if (found === undefined || this.tables.includes(found) === false) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
else if (found.getType().isWithHeader() === true && body === false) {
|
|
103
|
+
// without [] the statement works on the header line
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const message = `"${found.getName()}" cannot be changed as a whole inside a LOOP over it`;
|
|
107
|
+
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
static findTable(node, scope) {
|
|
112
|
+
var _a;
|
|
113
|
+
if (!(node instanceof nodes_1.ExpressionNode)
|
|
114
|
+
|| node.getChildren().length !== 1
|
|
115
|
+
|| !(((_a = node.getFirstChild()) === null || _a === void 0 ? void 0 : _a.get()) instanceof Expressions.Field)) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
const found = scope.findVariable(node.concatTokens());
|
|
119
|
+
if (found === undefined || !(found.getType() instanceof basic_1.TableType)) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
return found;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.LoopTableChange = LoopTableChange;
|
|
126
|
+
//# sourceMappingURL=_loop_table_change.js.map
|
|
@@ -0,0 +1,83 @@
|
|
|
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.substringType = substringType;
|
|
37
|
+
exports.isFieldSymbolItself = isFieldSymbolItself;
|
|
38
|
+
const Expressions = __importStar(require("../../2_statements/expressions"));
|
|
39
|
+
const basic_1 = require("../../types/basic");
|
|
40
|
+
const _syntax_input_1 = require("../_syntax_input");
|
|
41
|
+
// The type of a substring with a static offset or length, used for reading and writing positions.
|
|
42
|
+
// On a character or byte field of fixed length the substring must lie inside the field, else it is
|
|
43
|
+
// a syntax error. Other types, a dynamic offset or length and "*" (undefined) are left alone, as is
|
|
44
|
+
// length 0. The offset is applied first, so the length is compared to what remains after the offset.
|
|
45
|
+
function substringType(context, offset, length, node, input) {
|
|
46
|
+
if (!(context instanceof basic_1.CharacterType) && !(context instanceof basic_1.HexType)) {
|
|
47
|
+
return context;
|
|
48
|
+
}
|
|
49
|
+
const fieldLength = context.getLength();
|
|
50
|
+
if (offset !== undefined && offset > 0) {
|
|
51
|
+
if (offset >= fieldLength) {
|
|
52
|
+
const message = `Offset ${offset} is outside the field of length ${fieldLength}`;
|
|
53
|
+
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
|
|
54
|
+
return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
|
|
55
|
+
}
|
|
56
|
+
return context instanceof basic_1.CharacterType ? new basic_1.CharacterType(fieldLength - offset) : new basic_1.HexType(fieldLength - offset);
|
|
57
|
+
}
|
|
58
|
+
if (length !== undefined && length > 0) {
|
|
59
|
+
if (length > fieldLength) {
|
|
60
|
+
const message = `Length ${length} exceeds the remaining length ${fieldLength} of the field`;
|
|
61
|
+
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
|
|
62
|
+
return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
|
|
63
|
+
}
|
|
64
|
+
return context instanceof basic_1.CharacterType ? new basic_1.CharacterType(length) : new basic_1.HexType(length);
|
|
65
|
+
}
|
|
66
|
+
return context;
|
|
67
|
+
}
|
|
68
|
+
// A field symbol typed "c" or "x" is generic, but its type is represented with length 1, so the
|
|
69
|
+
// offset or length at "index" is not checked when it directly follows the field symbol
|
|
70
|
+
function isFieldSymbolItself(children, index) {
|
|
71
|
+
var _a;
|
|
72
|
+
const first = (_a = children[0]) === null || _a === void 0 ? void 0 : _a.get();
|
|
73
|
+
if (!(first instanceof Expressions.SourceFieldSymbol) && !(first instanceof Expressions.TargetFieldSymbol)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
for (let i = 1; i < index; i++) {
|
|
77
|
+
if (!(children[i].get() instanceof Expressions.FieldOffset)) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=_substring.js.map
|
|
@@ -46,6 +46,7 @@ const attribute_name_1 = require("./attribute_name");
|
|
|
46
46
|
const table_expression_1 = require("./table_expression");
|
|
47
47
|
const field_offset_1 = require("./field_offset");
|
|
48
48
|
const field_length_1 = require("./field_length");
|
|
49
|
+
const _substring_1 = require("./_substring");
|
|
49
50
|
class AttributeChain {
|
|
50
51
|
static runSyntax(inputContext, node, input, type) {
|
|
51
52
|
if (inputContext instanceof void_type_1.VoidType) {
|
|
@@ -122,25 +123,11 @@ class AttributeChain {
|
|
|
122
123
|
}
|
|
123
124
|
else if (child instanceof nodes_1.ExpressionNode && child.get() instanceof Expressions.FieldOffset) {
|
|
124
125
|
const offset = field_offset_1.FieldOffset.runSyntax(child, input);
|
|
125
|
-
|
|
126
|
-
if (contextType instanceof basic_1.CharacterType) {
|
|
127
|
-
contextType = new basic_1.CharacterType(contextType.getLength() - offset);
|
|
128
|
-
}
|
|
129
|
-
else if (contextType instanceof basic_1.HexType) {
|
|
130
|
-
contextType = new basic_1.HexType(contextType.getLength() - offset);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
126
|
+
contextType = (0, _substring_1.substringType)(contextType, offset, undefined, child, input);
|
|
133
127
|
}
|
|
134
128
|
else if (child instanceof nodes_1.ExpressionNode && child.get() instanceof Expressions.FieldLength) {
|
|
135
129
|
const length = field_length_1.FieldLength.runSyntax(child, input);
|
|
136
|
-
|
|
137
|
-
if (contextType instanceof basic_1.CharacterType) {
|
|
138
|
-
contextType = new basic_1.CharacterType(length);
|
|
139
|
-
}
|
|
140
|
-
else if (contextType instanceof basic_1.HexType) {
|
|
141
|
-
contextType = new basic_1.HexType(length);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
130
|
+
contextType = (0, _substring_1.substringType)(contextType, undefined, length, child, input);
|
|
144
131
|
}
|
|
145
132
|
}
|
|
146
133
|
return contextType;
|
|
@@ -63,6 +63,11 @@ class Compare {
|
|
|
63
63
|
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), initialMessage));
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
|
+
const predicateMessage = this.checkPredicateOperand(sources, input);
|
|
67
|
+
if (predicateMessage !== undefined) {
|
|
68
|
+
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), predicateMessage));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
66
71
|
const typeUtils = new _type_utils_1.TypeUtils(input.scope);
|
|
67
72
|
const compareOperator = (_a = node.findDirectExpression(Expressions.CompareOperator)) === null || _a === void 0 ? void 0 : _a.concatTokens().toUpperCase();
|
|
68
73
|
if (compareOperator === "CS"
|
|
@@ -103,6 +108,31 @@ class Compare {
|
|
|
103
108
|
if ((chain === null || chain === void 0 ? void 0 : chain.length) === 4 && chain[0] instanceof nodes_1.TokenNode && (first === "XSDBOOL" || first === "BOOLC")) {
|
|
104
109
|
return `Built-in function "${chain[0].getFirstToken().getStr()}" cannot be the operand of IS INITIAL`;
|
|
105
110
|
}
|
|
111
|
+
const name = this.builtinCallName(node.findDirectExpression(Expressions.Source), input);
|
|
112
|
+
const returning = (_d = (_c = _builtin_1.BuiltIn.searchBuiltin(name)) === null || _c === void 0 ? void 0 : _c.getParameters().getReturning()) === null || _d === void 0 ? void 0 : _d.getType();
|
|
113
|
+
if (returning instanceof basic_1.StringType || returning instanceof basic_1.CharacterType) {
|
|
114
|
+
return `Built-in function "${name}" cannot be the operand of IS INITIAL`;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
// a predicate function, contains( ) or line_exists( ), is a relational expression and not a value,
|
|
119
|
+
// it can stand alone as a condition or inside boolc( ) or xsdbool( ), but cannot be an operand of a comparison
|
|
120
|
+
static checkPredicateOperand(sources, input) {
|
|
121
|
+
if (input.scope.getOpenABAP()) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
for (const source of sources) {
|
|
125
|
+
const name = this.builtinCallName(source, input);
|
|
126
|
+
if (_builtin_1.BuiltIn.isPredicate(name) === true) {
|
|
127
|
+
return `Predicate function "${name}" cannot be the operand of a comparison`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
// the name of the built-in function when the source is exactly one call of it, not hidden by a method of the same name
|
|
133
|
+
static builtinCallName(source, input) {
|
|
134
|
+
var _a;
|
|
135
|
+
const chain = source === null || source === void 0 ? void 0 : source.getChildren();
|
|
106
136
|
if ((chain === null || chain === void 0 ? void 0 : chain.length) !== 1 || !(chain[0] instanceof nodes_1.ExpressionNode) || !(chain[0].get() instanceof Expressions.MethodCallChain)) {
|
|
107
137
|
return undefined;
|
|
108
138
|
}
|
|
@@ -110,9 +140,8 @@ class Compare {
|
|
|
110
140
|
if (calls.length !== 1 || !(calls[0] instanceof nodes_1.ExpressionNode) || !(calls[0].get() instanceof Expressions.MethodCall)) {
|
|
111
141
|
return undefined;
|
|
112
142
|
}
|
|
113
|
-
const name = (
|
|
114
|
-
|
|
115
|
-
if (name === undefined || builtin === undefined) {
|
|
143
|
+
const name = (_a = calls[0].findDirectExpression(Expressions.MethodName)) === null || _a === void 0 ? void 0 : _a.concatTokens();
|
|
144
|
+
if (name === undefined || _builtin_1.BuiltIn.searchBuiltin(name) === undefined) {
|
|
116
145
|
return undefined;
|
|
117
146
|
}
|
|
118
147
|
// a method of the same name hides the built-in function
|
|
@@ -120,11 +149,7 @@ class Compare {
|
|
|
120
149
|
if (enclosing !== undefined && new _object_oriented_1.ObjectOriented(input.scope).searchMethodName(enclosing, name).method !== undefined) {
|
|
121
150
|
return undefined;
|
|
122
151
|
}
|
|
123
|
-
|
|
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;
|
|
152
|
+
return name;
|
|
128
153
|
}
|
|
129
154
|
}
|
|
130
155
|
exports.Compare = Compare;
|
|
@@ -48,6 +48,7 @@ const expressions_1 = require("../../2_statements/expressions");
|
|
|
48
48
|
const dereference_1 = require("./dereference");
|
|
49
49
|
const source_field_symbol_1 = require("./source_field_symbol");
|
|
50
50
|
const source_field_1 = require("./source_field");
|
|
51
|
+
const _substring_1 = require("./_substring");
|
|
51
52
|
const _syntax_input_1 = require("../_syntax_input");
|
|
52
53
|
const version_1 = require("../../../version");
|
|
53
54
|
class FieldChain {
|
|
@@ -160,24 +161,14 @@ class FieldChain {
|
|
|
160
161
|
}
|
|
161
162
|
else if (current.get() instanceof Expressions.FieldOffset && current instanceof nodes_1.ExpressionNode) {
|
|
162
163
|
const offset = field_offset_1.FieldOffset.runSyntax(current, input);
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
context = new basic_1.CharacterType(context.getLength() - offset);
|
|
166
|
-
}
|
|
167
|
-
else if (context instanceof basic_1.HexType) {
|
|
168
|
-
context = new basic_1.HexType(context.getLength() - offset);
|
|
169
|
-
}
|
|
164
|
+
if (!(0, _substring_1.isFieldSymbolItself)(children, i)) {
|
|
165
|
+
context = (0, _substring_1.substringType)(context, offset, undefined, current, input);
|
|
170
166
|
}
|
|
171
167
|
}
|
|
172
168
|
else if (current.get() instanceof Expressions.FieldLength && current instanceof nodes_1.ExpressionNode) {
|
|
173
169
|
const length = field_length_1.FieldLength.runSyntax(current, input);
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
context = new basic_1.CharacterType(length);
|
|
177
|
-
}
|
|
178
|
-
else if (context instanceof basic_1.HexType) {
|
|
179
|
-
context = new basic_1.HexType(length);
|
|
180
|
-
}
|
|
170
|
+
if (!(0, _substring_1.isFieldSymbolItself)(children, i)) {
|
|
171
|
+
context = (0, _substring_1.substringType)(context, undefined, length, current, input);
|
|
181
172
|
}
|
|
182
173
|
}
|
|
183
174
|
prev = current.concatTokens();
|
|
@@ -141,7 +141,12 @@ class MethodCallChain {
|
|
|
141
141
|
}
|
|
142
142
|
const param = current.findDirectExpression(Expressions.MethodCallParam);
|
|
143
143
|
if (param && method) {
|
|
144
|
-
method_call_param_1.MethodCallParam.runSyntax(param, input, method);
|
|
144
|
+
const argumentType = method_call_param_1.MethodCallParam.runSyntax(param, input, method);
|
|
145
|
+
if (method instanceof _builtin_1.BuiltInMethod
|
|
146
|
+
&& _builtin_1.BuiltIn.returnsArgumentType(methodName)
|
|
147
|
+
&& this.isNumeric(argumentType)) {
|
|
148
|
+
context = argumentType;
|
|
149
|
+
}
|
|
145
150
|
}
|
|
146
151
|
else if (param && context instanceof basic_1.VoidType) {
|
|
147
152
|
method_call_param_1.MethodCallParam.runSyntax(param, input, context);
|
|
@@ -158,6 +163,16 @@ class MethodCallChain {
|
|
|
158
163
|
return context;
|
|
159
164
|
}
|
|
160
165
|
//////////////////////////////////////
|
|
166
|
+
static isNumeric(type) {
|
|
167
|
+
return type instanceof basic_1.IntegerType
|
|
168
|
+
|| type instanceof basic_1.Integer8Type
|
|
169
|
+
|| type instanceof basic_1.PackedType
|
|
170
|
+
|| type instanceof basic_1.FloatType
|
|
171
|
+
|| type instanceof basic_1.FloatingPointType
|
|
172
|
+
|| type instanceof basic_1.DecFloatType
|
|
173
|
+
|| type instanceof basic_1.DecFloat16Type
|
|
174
|
+
|| type instanceof basic_1.DecFloat34Type;
|
|
175
|
+
}
|
|
161
176
|
static findTop(first, input, targetType) {
|
|
162
177
|
var _a;
|
|
163
178
|
if (first.get() instanceof Expressions.ClassName) {
|
|
@@ -46,6 +46,7 @@ const constant_1 = require("./constant");
|
|
|
46
46
|
const _check_offset_length_1 = require("./_check_offset_length");
|
|
47
47
|
const _check_text_symbol_1 = require("./_check_text_symbol");
|
|
48
48
|
class MethodCallParam {
|
|
49
|
+
/** returns the type of the unnamed argument, if any */
|
|
49
50
|
static runSyntax(node, input, method) {
|
|
50
51
|
if (!(node.get() instanceof Expressions.MethodCallParam)) {
|
|
51
52
|
const message = "MethodCallParam, unexpected input";
|
|
@@ -125,6 +126,7 @@ class MethodCallParam {
|
|
|
125
126
|
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
|
|
126
127
|
return;
|
|
127
128
|
}
|
|
129
|
+
return sourceType;
|
|
128
130
|
}
|
|
129
131
|
else if (child instanceof nodes_1.ExpressionNode && child.get() instanceof Expressions.ParameterListS) {
|
|
130
132
|
new method_parameters_1.MethodParameters().checkExporting(child, input, method);
|
|
@@ -137,6 +139,7 @@ class MethodCallParam {
|
|
|
137
139
|
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
|
|
138
140
|
return;
|
|
139
141
|
}
|
|
142
|
+
return undefined;
|
|
140
143
|
}
|
|
141
144
|
}
|
|
142
145
|
exports.MethodCallParam = MethodCallParam;
|
|
@@ -74,7 +74,7 @@ const version_1 = require("../../../version");
|
|
|
74
74
|
// TODO: refactor all these method parameters to objects, this is getting messy
|
|
75
75
|
class Source {
|
|
76
76
|
static runSyntax(node, input, targetType, writeReference = false, allowGenericDeference = false, readReference = true) {
|
|
77
|
-
var _a, _b
|
|
77
|
+
var _a, _b;
|
|
78
78
|
if (node === undefined) {
|
|
79
79
|
return undefined;
|
|
80
80
|
}
|
|
@@ -169,13 +169,7 @@ class Source {
|
|
|
169
169
|
this.traverseRemainingChildren(children, input);
|
|
170
170
|
return bodyType;
|
|
171
171
|
}
|
|
172
|
-
|
|
173
|
-
const nestedType = (_c = bodySource === null || bodySource === void 0 ? void 0 : bodySource.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _c === void 0 ? void 0 : _c.concatTokens();
|
|
174
|
-
// CONV # passes its target type into a nested inferred constructor, so
|
|
175
|
-
// showing both inferred types would produce duplicate inlay hints.
|
|
176
|
-
if (!(inferred === "#" && nestedType === "#")) {
|
|
177
|
-
this.addIfInferred(node, input, foundType);
|
|
178
|
-
}
|
|
172
|
+
this.addIfInferred(node, input, foundType);
|
|
179
173
|
this.traverseRemainingChildren(children, input);
|
|
180
174
|
return foundType;
|
|
181
175
|
}
|
|
@@ -233,7 +227,7 @@ class Source {
|
|
|
233
227
|
{
|
|
234
228
|
const foundType = this.determineType(node, input, targetType);
|
|
235
229
|
if ((foundType === null || foundType === void 0 ? void 0 : foundType.isGeneric()) === true
|
|
236
|
-
&& ((
|
|
230
|
+
&& ((_b = node.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _b === void 0 ? void 0 : _b.concatTokens()) === "#"
|
|
237
231
|
&& !(0, version_1.releaseAtLeast)(input.scope.getRelease(), version_1.Release.v756)
|
|
238
232
|
&& input.scope.getLanguageVersion() !== version_1.LanguageVersion.Cloud
|
|
239
233
|
&& !input.scope.getOpenABAP()) {
|
|
@@ -47,6 +47,7 @@ const table_expression_1 = require("./table_expression");
|
|
|
47
47
|
const expressions_1 = require("../../2_statements/expressions");
|
|
48
48
|
const field_length_1 = require("./field_length");
|
|
49
49
|
const cast_1 = require("./cast");
|
|
50
|
+
const _substring_1 = require("./_substring");
|
|
50
51
|
const _syntax_input_1 = require("../_syntax_input");
|
|
51
52
|
const version_1 = require("../../../version");
|
|
52
53
|
class Target {
|
|
@@ -154,6 +155,8 @@ class Target {
|
|
|
154
155
|
context = attribute_name_1.AttributeName.runSyntax(context, current, input, type);
|
|
155
156
|
}
|
|
156
157
|
}
|
|
158
|
+
// the substring is checked, but the type of the whole field is returned
|
|
159
|
+
let substring = context;
|
|
157
160
|
const offset = node.findDirectExpression(Expressions.FieldOffset);
|
|
158
161
|
if (offset) {
|
|
159
162
|
if (context instanceof basic_1.XStringType || context instanceof basic_1.StringType) {
|
|
@@ -161,7 +164,10 @@ class Target {
|
|
|
161
164
|
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
|
|
162
165
|
return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
|
|
163
166
|
}
|
|
164
|
-
field_offset_1.FieldOffset.runSyntax(offset, input);
|
|
167
|
+
const value = field_offset_1.FieldOffset.runSyntax(offset, input);
|
|
168
|
+
if (!(0, _substring_1.isFieldSymbolItself)(children, children.indexOf(offset))) {
|
|
169
|
+
substring = (0, _substring_1.substringType)(substring, value, undefined, offset, input);
|
|
170
|
+
}
|
|
165
171
|
}
|
|
166
172
|
const length = node.findDirectExpression(Expressions.FieldLength);
|
|
167
173
|
if (length) {
|
|
@@ -170,7 +176,13 @@ class Target {
|
|
|
170
176
|
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
|
|
171
177
|
return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
|
|
172
178
|
}
|
|
173
|
-
field_length_1.FieldLength.runSyntax(length, input);
|
|
179
|
+
const value = field_length_1.FieldLength.runSyntax(length, input);
|
|
180
|
+
if (!(0, _substring_1.isFieldSymbolItself)(children, children.indexOf(length))) {
|
|
181
|
+
substring = (0, _substring_1.substringType)(substring, undefined, value, length, input);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (substring instanceof basic_1.VoidType && !(context instanceof basic_1.VoidType)) {
|
|
185
|
+
return substring;
|
|
174
186
|
}
|
|
175
187
|
return context;
|
|
176
188
|
}
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.ReadTable = void 0;
|
|
37
37
|
const Expressions = __importStar(require("../../2_statements/expressions"));
|
|
38
|
+
const nodes_1 = require("../../nodes");
|
|
38
39
|
const basic_1 = require("../../types/basic");
|
|
39
40
|
const source_1 = require("../expressions/source");
|
|
40
41
|
const inline_data_1 = require("../expressions/inline_data");
|
|
@@ -43,6 +44,9 @@ const fstarget_1 = require("../expressions/fstarget");
|
|
|
43
44
|
const component_compare_simple_1 = require("../expressions/component_compare_simple");
|
|
44
45
|
const _type_utils_1 = require("../_type_utils");
|
|
45
46
|
const _syntax_input_1 = require("../_syntax_input");
|
|
47
|
+
const _builtin_1 = require("../_builtin");
|
|
48
|
+
const _object_oriented_1 = require("../_object_oriented");
|
|
49
|
+
const version_1 = require("../../../version");
|
|
46
50
|
class ReadTable {
|
|
47
51
|
runSyntax(node, input) {
|
|
48
52
|
const concat = node.concatTokens().toUpperCase();
|
|
@@ -69,6 +73,11 @@ class ReadTable {
|
|
|
69
73
|
const components = node.findDirectExpression(Expressions.ComponentCompareSimple);
|
|
70
74
|
if (components !== undefined) {
|
|
71
75
|
component_compare_simple_1.ComponentCompareSimple.runSyntax(components, input, rowType);
|
|
76
|
+
const keyMessage = this.checkKeyOperands(components, input);
|
|
77
|
+
if (keyMessage !== undefined) {
|
|
78
|
+
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), keyMessage));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
72
81
|
}
|
|
73
82
|
const indexSource = node.findExpressionAfterToken("INDEX");
|
|
74
83
|
if (indexSource) {
|
|
@@ -164,6 +173,36 @@ class ReadTable {
|
|
|
164
173
|
}
|
|
165
174
|
}
|
|
166
175
|
}
|
|
176
|
+
// before 7.40 a WITH [TABLE] KEY operand is not an expression position, a built-in function from 7.02 on
|
|
177
|
+
// is read as a functional method call there, "method TO_UPPER is unknown" on a system, while
|
|
178
|
+
// functional methods and the older built-in functions like lines( ) are accepted
|
|
179
|
+
checkKeyOperands(node, input) {
|
|
180
|
+
var _a, _b;
|
|
181
|
+
if (input.scope.getOpenABAP() || (0, version_1.releaseAtLeast)(input.scope.getRelease(), version_1.Release.v740sp02)) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
for (const operand of node.findDirectExpressionsMulti([Expressions.Source, Expressions.SimpleSource4])) {
|
|
185
|
+
const chain = operand.getChildren();
|
|
186
|
+
if (chain.length !== 1 || !(chain[0] instanceof nodes_1.ExpressionNode) || !(chain[0].get() instanceof Expressions.MethodCallChain)) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const calls = chain[0].getChildren();
|
|
190
|
+
if (calls.length !== 1 || !(calls[0] instanceof nodes_1.ExpressionNode) || !(calls[0].get() instanceof Expressions.MethodCall)) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const name = (_a = calls[0].findDirectExpression(Expressions.MethodName)) === null || _a === void 0 ? void 0 : _a.concatTokens();
|
|
194
|
+
if (name === undefined || ((_b = _builtin_1.BuiltIn.methods[name.toUpperCase()]) === null || _b === void 0 ? void 0 : _b.release) === undefined) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
// a method of the same name hides the built-in function
|
|
198
|
+
const enclosing = input.scope.findClassDefinition(input.scope.getEnclosingClassName());
|
|
199
|
+
if (enclosing !== undefined && new _object_oriented_1.ObjectOriented(input.scope).searchMethodName(enclosing, name).method !== undefined) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
return `Built-in function "${name}" cannot be a READ TABLE key operand before 7.40`;
|
|
203
|
+
}
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
167
206
|
}
|
|
168
207
|
exports.ReadTable = ReadTable;
|
|
169
208
|
//# sourceMappingURL=read_table.js.map
|
|
@@ -0,0 +1,105 @@
|
|
|
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.Cleanup = void 0;
|
|
37
|
+
const Statements = __importStar(require("../../2_statements/statements"));
|
|
38
|
+
const Structures = __importStar(require("../../3_structures/structures"));
|
|
39
|
+
const nodes_1 = require("../../nodes");
|
|
40
|
+
const _syntax_input_1 = require("../_syntax_input");
|
|
41
|
+
const LOOPS = [
|
|
42
|
+
Structures.Loop,
|
|
43
|
+
Structures.LoopAtScreen,
|
|
44
|
+
Structures.LoopExtract,
|
|
45
|
+
Structures.Do,
|
|
46
|
+
Structures.While,
|
|
47
|
+
Structures.Select,
|
|
48
|
+
Structures.With,
|
|
49
|
+
Structures.Provide,
|
|
50
|
+
];
|
|
51
|
+
// a CLEANUP block cannot contain statements that statically leave it: RETURN, and EXIT, CONTINUE or CHECK
|
|
52
|
+
// that do not belong to a loop inside the block, these end a loop outside the TRY or the processing block
|
|
53
|
+
class Cleanup {
|
|
54
|
+
static runSyntax(node, input) {
|
|
55
|
+
if (input.scope.getOpenABAP()) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const body = node.findDirectStructure(Structures.Body);
|
|
59
|
+
if (body !== undefined) {
|
|
60
|
+
this.walk(body, false, input);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
static walk(node, inLoop, input) {
|
|
64
|
+
for (const child of node.getChildren()) {
|
|
65
|
+
if (child instanceof nodes_1.StructureNode) {
|
|
66
|
+
const stru = child.get();
|
|
67
|
+
// a nested CLEANUP block is checked on its own, macro definitions and test injections do not run here
|
|
68
|
+
if (stru instanceof Structures.Cleanup
|
|
69
|
+
|| stru instanceof Structures.Define
|
|
70
|
+
|| stru instanceof Structures.TestInjection) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
this.walk(child, inLoop || LOOPS.some(l => stru instanceof l), input);
|
|
74
|
+
}
|
|
75
|
+
else if (child instanceof nodes_1.StatementNode) {
|
|
76
|
+
const name = this.leavingStatement(child, inLoop);
|
|
77
|
+
if (name !== undefined) {
|
|
78
|
+
const message = `${name} cannot leave a CLEANUP block`;
|
|
79
|
+
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, child.getFirstToken(), message));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
static leavingStatement(node, inLoop) {
|
|
85
|
+
const s = node.get();
|
|
86
|
+
if (s instanceof Statements.Return) {
|
|
87
|
+
return "RETURN";
|
|
88
|
+
}
|
|
89
|
+
else if (inLoop === true) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
else if (s instanceof Statements.Exit && node.findDirectTokenByText("FROM") === undefined) {
|
|
93
|
+
return "EXIT";
|
|
94
|
+
}
|
|
95
|
+
else if (s instanceof Statements.Continue) {
|
|
96
|
+
return "CONTINUE";
|
|
97
|
+
}
|
|
98
|
+
else if (s instanceof Statements.Check) {
|
|
99
|
+
return "CHECK";
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.Cleanup = Cleanup;
|
|
105
|
+
//# sourceMappingURL=cleanup.js.map
|
|
@@ -50,6 +50,7 @@ const type_mesh_1 = require("./structures/type_mesh");
|
|
|
50
50
|
const types_1 = require("./structures/types");
|
|
51
51
|
const statics_1 = require("./structures/statics");
|
|
52
52
|
const constants_1 = require("./structures/constants");
|
|
53
|
+
const cleanup_1 = require("./structures/cleanup");
|
|
53
54
|
const class_definition_1 = require("../types/class_definition");
|
|
54
55
|
const interface_definition_1 = require("../types/interface_definition");
|
|
55
56
|
const delete_memory_1 = require("./statements/delete_memory");
|
|
@@ -191,6 +192,7 @@ const _syntax_input_1 = require("./_syntax_input");
|
|
|
191
192
|
const assert_error_1 = require("./assert_error");
|
|
192
193
|
const field_group_1 = require("./statements/field_group");
|
|
193
194
|
const stuff_1 = require("../../stuff");
|
|
195
|
+
const _loop_table_change_1 = require("./_loop_table_change");
|
|
194
196
|
// -----------------------------------
|
|
195
197
|
const map = {};
|
|
196
198
|
function addToMap(handler) {
|
|
@@ -352,6 +354,7 @@ class SyntaxLogic {
|
|
|
352
354
|
};
|
|
353
355
|
this.issues = [];
|
|
354
356
|
this.deferred = [];
|
|
357
|
+
this.loopTables = new _loop_table_change_1.LoopTableChange();
|
|
355
358
|
this.reg.getDDICReferences().clear(this.object);
|
|
356
359
|
this.reg.getMSAGReferences().clear(this.object);
|
|
357
360
|
if (this.object instanceof objects_1.Program && this.object.isInclude()) {
|
|
@@ -417,6 +420,9 @@ class SyntaxLogic {
|
|
|
417
420
|
this.issues.push(issue);
|
|
418
421
|
}
|
|
419
422
|
traverse(node) {
|
|
423
|
+
const loop = node instanceof nodes_1.StructureNode
|
|
424
|
+
&& node.get() instanceof Structures.Loop
|
|
425
|
+
&& this.loopTables.push(node, this.scope);
|
|
420
426
|
for (const child of node.getChildren()) {
|
|
421
427
|
const isStructure = child instanceof nodes_1.StructureNode;
|
|
422
428
|
const isStatement = child instanceof nodes_1.StatementNode;
|
|
@@ -449,6 +455,9 @@ class SyntaxLogic {
|
|
|
449
455
|
this.traverse(child);
|
|
450
456
|
}
|
|
451
457
|
}
|
|
458
|
+
if (loop === true) {
|
|
459
|
+
this.loopTables.pop();
|
|
460
|
+
}
|
|
452
461
|
}
|
|
453
462
|
/**
|
|
454
463
|
* if this returns true, then the traversal should continue with next child
|
|
@@ -505,6 +514,9 @@ class SyntaxLogic {
|
|
|
505
514
|
// todo: skipped for now
|
|
506
515
|
return true;
|
|
507
516
|
}
|
|
517
|
+
else if (stru instanceof Structures.Cleanup) {
|
|
518
|
+
cleanup_1.Cleanup.runSyntax(node, input);
|
|
519
|
+
}
|
|
508
520
|
return false;
|
|
509
521
|
}
|
|
510
522
|
updateScopeStatement(node) {
|
|
@@ -534,6 +546,7 @@ class SyntaxLogic {
|
|
|
534
546
|
this.scope.addIdentifier(new data_2.Data().runSyntax(node, input));
|
|
535
547
|
return;
|
|
536
548
|
}
|
|
549
|
+
this.loopTables.check(node, input);
|
|
537
550
|
const name = s.constructor.name;
|
|
538
551
|
if (map[name]) {
|
|
539
552
|
map[name].runSyntax(node, input);
|
package/build/src/registry.js
CHANGED
|
@@ -189,6 +189,9 @@ text = text.`,
|
|
|
189
189
|
else if (child.get() instanceof Expressions.Constant) {
|
|
190
190
|
context = this.infer(context, constant_1.Constant.runSyntax(child));
|
|
191
191
|
}
|
|
192
|
+
else if (child.get() instanceof Expressions.StringTemplate) {
|
|
193
|
+
context = basic_1.StringType.get();
|
|
194
|
+
}
|
|
192
195
|
else if (child.get() instanceof Expressions.Source) {
|
|
193
196
|
context = this.infer(context, this.sourceType(child, scope));
|
|
194
197
|
}
|
|
@@ -40,7 +40,16 @@ const Expressions = __importStar(require("../abap/2_statements/expressions"));
|
|
|
40
40
|
const Statements = __importStar(require("../abap/2_statements/statements"));
|
|
41
41
|
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
42
42
|
const _irule_1 = require("./_irule");
|
|
43
|
+
const syntax_1 = require("../abap/5_syntax/syntax");
|
|
44
|
+
const _typed_identifier_1 = require("../abap/types/_typed_identifier");
|
|
45
|
+
const basic_1 = require("../abap/types/basic");
|
|
46
|
+
const tokens_1 = require("../abap/1_lexer/tokens");
|
|
43
47
|
class UseMessageClassConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
48
|
+
constructor() {
|
|
49
|
+
super(...arguments);
|
|
50
|
+
/** Allow MESSAGE with an object reference, eg. an exception, like MESSAGE lx_error TYPE 'E' */
|
|
51
|
+
this.allowExceptions = true;
|
|
52
|
+
}
|
|
44
53
|
}
|
|
45
54
|
exports.UseMessageClassConf = UseMessageClassConf;
|
|
46
55
|
class UseMessageClass extends _abap_rule_1.ABAPRule {
|
|
@@ -53,6 +62,9 @@ class UseMessageClass extends _abap_rule_1.ABAPRule {
|
|
|
53
62
|
key: "use_message_class",
|
|
54
63
|
title: "Use message class",
|
|
55
64
|
shortDescription: `Define messages in SE91 instead of using inline text or variables`,
|
|
65
|
+
extendedInformation: `If allowExceptions is enabled, MESSAGE with an object reference is allowed, eg. MESSAGE lx_error TYPE 'E'.
|
|
66
|
+
|
|
67
|
+
Void types are only allowed if the type name contains "CX_", as void class references cannot be distinguished from other void types.`,
|
|
56
68
|
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile],
|
|
57
69
|
badExample: `MESSAGE 'Something went wrong' TYPE 'E'.`,
|
|
58
70
|
goodExample: `MESSAGE e001(bc_msg).`,
|
|
@@ -64,13 +76,14 @@ class UseMessageClass extends _abap_rule_1.ABAPRule {
|
|
|
64
76
|
setConfig(conf) {
|
|
65
77
|
this.conf = conf;
|
|
66
78
|
}
|
|
67
|
-
runParsed(file,
|
|
79
|
+
runParsed(file, obj) {
|
|
68
80
|
const structure = file.getStructure();
|
|
69
81
|
if (structure === undefined) {
|
|
70
82
|
return [];
|
|
71
83
|
}
|
|
72
84
|
const issues = [];
|
|
73
85
|
const key = this.getMetadata().key;
|
|
86
|
+
let syntax = undefined;
|
|
74
87
|
for (const statement of structure.findAllStatements(Statements.Message)) {
|
|
75
88
|
// The parser populates MessageSource only for the class-based forms (e001(bc_msg) or ID/TYPE/NUMBER).
|
|
76
89
|
// The text form (MESSAGE <source> TYPE <source>) uses MessageSourceSource directly with no MessageSource child.
|
|
@@ -78,10 +91,60 @@ class UseMessageClass extends _abap_rule_1.ABAPRule {
|
|
|
78
91
|
if (statement.findDirectExpression(Expressions.MessageSource) !== undefined) {
|
|
79
92
|
continue;
|
|
80
93
|
}
|
|
94
|
+
if (this.conf.allowExceptions === true) {
|
|
95
|
+
syntax !== null && syntax !== void 0 ? syntax : (syntax = new syntax_1.SyntaxLogic(this.reg, obj).run());
|
|
96
|
+
if (this.isObjectReference(statement, file, syntax)) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
81
100
|
issues.push(issue_1.Issue.atStatement(file, statement, "Use a message class (SE91) instead of inline text or variable", key, this.conf.severity));
|
|
82
101
|
}
|
|
83
102
|
return issues;
|
|
84
103
|
}
|
|
104
|
+
isObjectReference(statement, file, syntax) {
|
|
105
|
+
var _a, _b;
|
|
106
|
+
const chain = (_b = (_a = statement.findDirectExpression(Expressions.MessageSourceSource)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(Expressions.Source)) === null || _b === void 0 ? void 0 : _b.findDirectExpression(Expressions.FieldChain);
|
|
107
|
+
if (chain === undefined) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
let type = undefined;
|
|
111
|
+
for (const child of chain.getChildren()) {
|
|
112
|
+
const c = child.get();
|
|
113
|
+
if (c instanceof Expressions.ComponentName) {
|
|
114
|
+
type = type instanceof basic_1.StructureType ? type.getComponentByName(child.concatTokens()) : undefined;
|
|
115
|
+
}
|
|
116
|
+
else if (c instanceof Expressions.SourceField
|
|
117
|
+
|| c instanceof Expressions.SourceFieldSymbol
|
|
118
|
+
|| c instanceof Expressions.AttributeName) {
|
|
119
|
+
type = this.findType(child.getFirstToken().getStart(), file, syntax);
|
|
120
|
+
}
|
|
121
|
+
else if (c instanceof Expressions.ClassName
|
|
122
|
+
|| c instanceof tokens_1.InstanceArrow
|
|
123
|
+
|| c instanceof tokens_1.StaticArrow
|
|
124
|
+
|| c instanceof tokens_1.Dash) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
if (type === undefined) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// void class references cannot be distinguished from other void types, guess based on the name
|
|
135
|
+
return type instanceof basic_1.ObjectReferenceType
|
|
136
|
+
|| type instanceof basic_1.GenericObjectReferenceType
|
|
137
|
+
|| (type instanceof basic_1.VoidType && /CX_/i.test(type.getVoided() || ""));
|
|
138
|
+
}
|
|
139
|
+
findType(start, file, syntax) {
|
|
140
|
+
const scope = syntax.spaghetti.lookupPosition(start, file.getFilename());
|
|
141
|
+
for (const r of (scope === null || scope === void 0 ? void 0 : scope.getData().references) || []) {
|
|
142
|
+
if (r.position.getStart().equals(start) && r.resolved instanceof _typed_identifier_1.TypedIdentifier) {
|
|
143
|
+
return r.resolved.getType();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
85
148
|
}
|
|
86
149
|
exports.UseMessageClass = UseMessageClass;
|
|
87
150
|
//# sourceMappingURL=use_message_class.js.map
|