@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.
@@ -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
- if (offset !== undefined) {
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
- if (length !== undefined) {
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 = (_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) {
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
- 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;
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 (offset) {
164
- if (context instanceof basic_1.CharacterType) {
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 (length) {
175
- if (context instanceof basic_1.CharacterType) {
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, _c, _d;
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
- const bodySource = (_b = node.findDirectExpression(Expressions.ConvBody)) === null || _b === void 0 ? void 0 : _b.findDirectExpression(Expressions.Source);
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
- && ((_d = node.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _d === void 0 ? void 0 : _d.concatTokens()) === "#"
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);
@@ -75,7 +75,7 @@ class Registry {
75
75
  }
76
76
  static abaplintVersion() {
77
77
  // magic, see build script "version.js"
78
- return "2.120.75";
78
+ return "2.120.77";
79
79
  }
80
80
  getDDICReferences() {
81
81
  return this.ddicReferences;
@@ -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, _obj) {
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.120.75",
3
+ "version": "2.120.77",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",