@abaplint/core 2.102.14 → 2.102.16

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.
@@ -2015,7 +2015,6 @@ declare namespace Expressions {
2015
2015
  ComponentCondSub,
2016
2016
  ComponentCond,
2017
2017
  ComponentName,
2018
- SQLFields,
2019
2018
  ConcatenatedConstant,
2020
2019
  CondBody,
2021
2020
  CondSub,
@@ -2139,8 +2138,8 @@ declare namespace Expressions {
2139
2138
  Source,
2140
2139
  SQLAggregation,
2141
2140
  SQLAliasField,
2142
- SQLArithmetics,
2143
2141
  SQLArithmeticOperator,
2142
+ SQLArithmetics,
2144
2143
  SQLAsName,
2145
2144
  SQLCase,
2146
2145
  SQLCDSParameters,
@@ -2153,6 +2152,7 @@ declare namespace Expressions {
2153
2152
  SQLFieldList,
2154
2153
  SQLFieldName,
2155
2154
  SQLField,
2155
+ SQLFields,
2156
2156
  SQLForAllEntries,
2157
2157
  SQLFromSource,
2158
2158
  SQLFrom,
@@ -2185,6 +2185,7 @@ declare namespace Expressions {
2185
2185
  TextElementString,
2186
2186
  TextElement,
2187
2187
  Throw,
2188
+ TransportingFields,
2188
2189
  TypeNameOrInfer,
2189
2190
  TypeName,
2190
2191
  TypeParam,
@@ -3706,6 +3707,8 @@ declare interface IStructure {
3706
3707
  declare interface IStructureComponent {
3707
3708
  name: string;
3708
3709
  type: AbstractType;
3710
+ asInclude?: boolean;
3711
+ suffix?: string;
3709
3712
  }
3710
3713
 
3711
3714
  declare interface IStructureRunnable {
@@ -5020,6 +5023,7 @@ export declare enum ScopeType {
5020
5023
  FunctionModule = "function",
5021
5024
  Method = "method",
5022
5025
  MethodInstance = "method_instance",
5026
+ MethodDefinition = "method_definition",
5023
5027
  For = "for",
5024
5028
  Let = "let",
5025
5029
  OpenSQL = "open_sql"
@@ -6324,6 +6328,10 @@ declare class Translate implements IStatement {
6324
6328
  getMatcher(): IStatementRunnable;
6325
6329
  }
6326
6330
 
6331
+ declare class TransportingFields extends Expression {
6332
+ getRunnable(): IStatementRunnable;
6333
+ }
6334
+
6327
6335
  declare class TruncateDataset implements IStatement {
6328
6336
  getMatcher(): IStatementRunnable;
6329
6337
  }
@@ -43,7 +43,6 @@ __exportStar(require("./component_compare"), exports);
43
43
  __exportStar(require("./component_cond_sub"), exports);
44
44
  __exportStar(require("./component_cond"), exports);
45
45
  __exportStar(require("./component_name"), exports);
46
- __exportStar(require("./sql_fields"), exports);
47
46
  __exportStar(require("./concatenated_constant"), exports);
48
47
  __exportStar(require("./cond_body"), exports);
49
48
  __exportStar(require("./cond_sub"), exports);
@@ -168,8 +167,8 @@ __exportStar(require("./source_field"), exports);
168
167
  __exportStar(require("./source"), exports);
169
168
  __exportStar(require("./sql_aggregation"), exports);
170
169
  __exportStar(require("./sql_alias_field"), exports);
171
- __exportStar(require("./sql_arithmetics"), exports);
172
170
  __exportStar(require("./sql_arithmetic_operator"), exports);
171
+ __exportStar(require("./sql_arithmetics"), exports);
173
172
  __exportStar(require("./sql_as_name"), exports);
174
173
  __exportStar(require("./sql_case"), exports);
175
174
  __exportStar(require("./sql_cds_parameters"), exports);
@@ -182,6 +181,7 @@ __exportStar(require("./sql_field_list_loop"), exports);
182
181
  __exportStar(require("./sql_field_list"), exports);
183
182
  __exportStar(require("./sql_field_name"), exports);
184
183
  __exportStar(require("./sql_field"), exports);
184
+ __exportStar(require("./sql_fields"), exports);
185
185
  __exportStar(require("./sql_for_all_entries"), exports);
186
186
  __exportStar(require("./sql_from_source"), exports);
187
187
  __exportStar(require("./sql_from"), exports);
@@ -214,6 +214,7 @@ __exportStar(require("./text_element_key"), exports);
214
214
  __exportStar(require("./text_element_string"), exports);
215
215
  __exportStar(require("./text_element"), exports);
216
216
  __exportStar(require("./throw"), exports);
217
+ __exportStar(require("./transporting_fields"), exports);
217
218
  __exportStar(require("./type_name_or_infer"), exports);
218
219
  __exportStar(require("./type_name"), exports);
219
220
  __exportStar(require("./type_param"), exports);
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TransportingFields = void 0;
4
+ const combi_1 = require("../combi");
5
+ const dynamic_1 = require("./dynamic");
6
+ const field_sub_1 = require("./field_sub");
7
+ class TransportingFields extends combi_1.Expression {
8
+ getRunnable() {
9
+ const fields = (0, combi_1.plus)((0, combi_1.alt)((0, combi_1.seq)("INTO", (0, combi_1.failStar)()), field_sub_1.FieldSub));
10
+ return (0, combi_1.altPrio)(dynamic_1.Dynamic, fields);
11
+ }
12
+ }
13
+ exports.TransportingFields = TransportingFields;
14
+ //# sourceMappingURL=transporting_fields.js.map
@@ -4,6 +4,7 @@ exports.ReadTable = void 0;
4
4
  const combi_1 = require("../combi");
5
5
  const expressions_1 = require("../expressions");
6
6
  const version_1 = require("../../../version");
7
+ const transporting_fields_1 = require("../expressions/transporting_fields");
7
8
  class ReadTable {
8
9
  getMatcher() {
9
10
  const comparing = (0, combi_1.seq)("COMPARING", (0, combi_1.alt)((0, combi_1.plus)(expressions_1.FieldSub), expressions_1.Dynamic));
@@ -12,8 +13,7 @@ class ReadTable {
12
13
  const key = (0, combi_1.seq)((0, combi_1.altPrio)("WITH KEY", "WITH TABLE KEY"), (0, combi_1.alt)(expressions_1.ComponentCompareSimple, components, (0, combi_1.seq)((0, combi_1.optPrio)("="), expressions_1.Source)));
13
14
  const using = (0, combi_1.seq)("USING KEY", (0, combi_1.alt)(expressions_1.Field, expressions_1.Dynamic));
14
15
  const from = (0, combi_1.seq)("FROM", expressions_1.Source);
15
- const fields = (0, combi_1.plus)((0, combi_1.alt)((0, combi_1.seq)("INTO", (0, combi_1.failStar)()), expressions_1.FieldSub));
16
- const perm = (0, combi_1.per)((0, combi_1.alt)(index, key, from), expressions_1.ReadTableTarget, using, comparing, "CASTING", "TRANSPORTING ALL FIELDS", (0, combi_1.seq)("TRANSPORTING", (0, combi_1.altPrio)(expressions_1.Dynamic, fields)), "BINARY SEARCH");
16
+ const perm = (0, combi_1.per)((0, combi_1.alt)(index, key, from), expressions_1.ReadTableTarget, using, comparing, "CASTING", "TRANSPORTING ALL FIELDS", (0, combi_1.seq)("TRANSPORTING", transporting_fields_1.TransportingFields), "BINARY SEARCH");
17
17
  return (0, combi_1.seq)("READ TABLE", (0, combi_1.alt)(expressions_1.SimpleSource2, (0, combi_1.ver)(version_1.Version.v740sp02, expressions_1.Source)), (0, combi_1.opt)(perm));
18
18
  }
19
19
  }
@@ -16,6 +16,7 @@ var ScopeType;
16
16
  ScopeType["FunctionModule"] = "function";
17
17
  ScopeType["Method"] = "method";
18
18
  ScopeType["MethodInstance"] = "method_instance";
19
+ ScopeType["MethodDefinition"] = "method_definition";
19
20
  ScopeType["For"] = "for";
20
21
  ScopeType["Let"] = "let";
21
22
  ScopeType["OpenSQL"] = "open_sql";
@@ -5,22 +5,28 @@ const Expressions = require("../../2_statements/expressions");
5
5
  const source_1 = require("./source");
6
6
  class MessageSource {
7
7
  runSyntax(node, scope, filename) {
8
- var _a, _b, _c, _d;
8
+ var _a, _b, _c, _d, _e;
9
9
  for (const f of node.findDirectExpressions(Expressions.Source)) {
10
10
  new source_1.Source().runSyntax(f, scope, filename);
11
11
  }
12
12
  if (node.getFirstToken().getStr().toUpperCase() === "ID") {
13
13
  const id = (_a = node.findExpressionAfterToken("ID")) === null || _a === void 0 ? void 0 : _a.concatTokens();
14
- const number = (_b = node.findDirectExpression(Expressions.MessageNumber)) === null || _b === void 0 ? void 0 : _b.concatTokens();
14
+ let number = (_b = node.findDirectExpression(Expressions.MessageNumber)) === null || _b === void 0 ? void 0 : _b.concatTokens();
15
+ if (number === undefined) {
16
+ const num = (_c = node.findExpressionAfterToken("NUMBER")) === null || _c === void 0 ? void 0 : _c.concatTokens();
17
+ if (num === null || num === void 0 ? void 0 : num.startsWith("'")) {
18
+ number = num.substring(1, num.length - 1).toUpperCase();
19
+ }
20
+ }
15
21
  if ((id === null || id === void 0 ? void 0 : id.startsWith("'")) && number) {
16
22
  const messageClass = id.substring(1, id.length - 1).toUpperCase();
17
23
  scope.getMSAGReferences().addUsing(filename, node.getFirstToken(), messageClass, number);
18
24
  }
19
25
  }
20
26
  else {
21
- const typeAndNumber = (_c = node.findDirectExpression(Expressions.MessageTypeAndNumber)) === null || _c === void 0 ? void 0 : _c.concatTokens();
27
+ const typeAndNumber = (_d = node.findDirectExpression(Expressions.MessageTypeAndNumber)) === null || _d === void 0 ? void 0 : _d.concatTokens();
22
28
  const messageNumber = typeAndNumber === null || typeAndNumber === void 0 ? void 0 : typeAndNumber.substring(1);
23
- const messageClass = (_d = node.findDirectExpression(Expressions.MessageClass)) === null || _d === void 0 ? void 0 : _d.concatTokens().toUpperCase();
29
+ const messageClass = (_e = node.findDirectExpression(Expressions.MessageClass)) === null || _e === void 0 ? void 0 : _e.concatTokens().toUpperCase();
24
30
  if (messageNumber && messageClass) {
25
31
  scope.getMSAGReferences().addUsing(filename, node.getFirstToken(), messageClass, messageNumber);
26
32
  }
@@ -17,14 +17,18 @@ class IncludeType {
17
17
  let ityp = new basic_types_1.BasicTypes(filename, scope).parseType(iname);
18
18
  const as = (_a = node.findExpressionAfterToken("AS")) === null || _a === void 0 ? void 0 : _a.concatTokens();
19
19
  if (as && ityp instanceof basic_1.StructureType) {
20
- ityp = new basic_1.StructureType(ityp.getComponents().concat([{ name: as, type: ityp }]));
20
+ ityp = new basic_1.StructureType(ityp.getComponents().concat([{
21
+ name: as,
22
+ type: ityp,
23
+ asInclude: true,
24
+ }]));
21
25
  }
22
26
  const suffix = (_b = node.findExpressionAfterToken("SUFFIX")) === null || _b === void 0 ? void 0 : _b.concatTokens();
23
27
  if (suffix && ityp instanceof basic_1.StructureType) {
24
28
  const components = [];
25
29
  for (const c of ityp.getComponents()) {
26
30
  if (c.name === as) {
27
- components.push(c);
31
+ components.push(Object.assign(Object.assign({}, c), { suffix: suffix, asInclude: c.asInclude }));
28
32
  continue;
29
33
  }
30
34
  components.push({
@@ -19,11 +19,12 @@ class MethodImplementation {
19
19
  if (methodDefinition === undefined) {
20
20
  throw new Error("Method definition \"" + methodName + "\" not found");
21
21
  }
22
+ const start = node.getFirstToken().getStart();
22
23
  if (methodDefinition.isStatic() === false) {
23
- scope.push(_scope_type_1.ScopeType.MethodInstance, methodName, node.getFirstToken().getStart(), filename);
24
+ scope.push(_scope_type_1.ScopeType.MethodInstance, methodName, start, filename);
24
25
  scope.addList(classDefinition.getAttributes().getInstance());
25
26
  }
26
- scope.push(_scope_type_1.ScopeType.Method, methodName, node.getFirstToken().getStart(), filename);
27
+ scope.push(_scope_type_1.ScopeType.Method, methodName, start, filename);
27
28
  scope.addReference(methodToken, methodDefinition, _reference_1.ReferenceType.MethodImplementationReference, filename);
28
29
  scope.addList(methodDefinition.getParameters().getAll());
29
30
  for (const i of helper.findInterfaces(classDefinition)) {
@@ -13,7 +13,7 @@ const method_parameters_1 = require("../expressions/method_parameters");
13
13
  class Raise {
14
14
  runSyntax(node, scope, filename) {
15
15
  // todo
16
- var _a, _b, _c, _d, _e;
16
+ var _a, _b, _c, _d, _e, _f;
17
17
  const helper = new _object_oriented_1.ObjectOriented(scope);
18
18
  let method;
19
19
  const classTok = (_a = node.findDirectExpression(Expressions.ClassName)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
@@ -72,7 +72,13 @@ class Raise {
72
72
  new message_source_1.MessageSource().runSyntax(s, scope, filename);
73
73
  }
74
74
  const id = (_d = node.findExpressionAfterToken("ID")) === null || _d === void 0 ? void 0 : _d.concatTokens();
75
- const number = (_e = node.findDirectExpression(Expressions.MessageNumber)) === null || _e === void 0 ? void 0 : _e.concatTokens();
75
+ let number = (_e = node.findDirectExpression(Expressions.MessageNumber)) === null || _e === void 0 ? void 0 : _e.concatTokens();
76
+ if (number === undefined) {
77
+ const num = (_f = node.findExpressionAfterToken("NUMBER")) === null || _f === void 0 ? void 0 : _f.concatTokens();
78
+ if (num === null || num === void 0 ? void 0 : num.startsWith("'")) {
79
+ number = num.substring(1, num.length - 1).toUpperCase();
80
+ }
81
+ }
76
82
  if ((id === null || id === void 0 ? void 0 : id.startsWith("'")) && number) {
77
83
  const messageClass = id.substring(1, id.length - 1).toUpperCase();
78
84
  scope.getMSAGReferences().addUsing(filename, node.getFirstToken(), messageClass, number);
@@ -58,29 +58,19 @@ class ReadTable {
58
58
  rowType = new basic_1.DataReference(rowType);
59
59
  }
60
60
  const inline = target.findFirstExpression(Expressions.InlineData);
61
+ const fst = target.findDirectExpression(Expressions.FSTarget);
62
+ const t = target.findFirstExpression(Expressions.Target);
61
63
  if (inline) {
62
64
  new inline_data_1.InlineData().runSyntax(inline, scope, filename, rowType);
63
- return;
64
65
  }
65
- const fst = target.findDirectExpression(Expressions.FSTarget);
66
- if (fst) {
66
+ else if (fst) {
67
67
  new fstarget_1.FSTarget().runSyntax(fst, scope, filename, rowType);
68
- return;
69
68
  }
70
- /*
71
- const inlinefs = target.findFirstExpression(Expressions.InlineFS);
72
- if (inlinefs) {
73
- new InlineFS().runSyntax(inlinefs, scope, filename, sourceType);
74
- return;
75
- }
76
- */
77
- const t = target.findFirstExpression(Expressions.Target);
78
- if (t) {
69
+ else if (t) {
79
70
  const targetType = new target_1.Target().runSyntax(t, scope, filename);
80
71
  if (new _type_utils_1.TypeUtils(scope).isAssignable(rowType, targetType) === false) {
81
72
  throw new Error("Incompatible types");
82
73
  }
83
- return;
84
74
  }
85
75
  }
86
76
  if (target === undefined && concat.includes(" TRANSPORTING NO FIELDS ") === false) {
@@ -89,6 +79,25 @@ class ReadTable {
89
79
  throw new Error("READ TABLE, define INTO or TRANSPORTING NO FIELDS");
90
80
  }
91
81
  }
82
+ const transporting = node.findDirectExpression(Expressions.TransportingFields);
83
+ if (transporting
84
+ && !(rowType instanceof basic_1.VoidType)
85
+ && !(rowType instanceof basic_1.UnknownType)
86
+ && !(rowType instanceof basic_1.AnyType)) {
87
+ if (!(rowType instanceof basic_1.StructureType)) {
88
+ throw new Error("READ TABLE, source not structured");
89
+ }
90
+ for (const t of (transporting === null || transporting === void 0 ? void 0 : transporting.findDirectExpressions(Expressions.FieldSub)) || []) {
91
+ const field = t.concatTokens();
92
+ if (field.includes("-")) {
93
+ // todo
94
+ continue;
95
+ }
96
+ if (rowType.getComponentByName(field) === undefined) {
97
+ throw new Error("READ TABLE, field " + field + " not found in source");
98
+ }
99
+ }
100
+ }
92
101
  }
93
102
  }
94
103
  exports.ReadTable = ReadTable;
@@ -11,6 +11,7 @@ const method_param_1 = require("../5_syntax/expressions/method_param");
11
11
  const _object_oriented_1 = require("../5_syntax/_object_oriented");
12
12
  const _reference_1 = require("../5_syntax/_reference");
13
13
  const identifier_1 = require("../1_lexer/tokens/identifier");
14
+ const _scope_type_1 = require("../5_syntax/_scope_type");
14
15
  // todo:
15
16
  // this.exceptions = [];
16
17
  // also consider RAISING vs EXCEPTIONS
@@ -28,7 +29,11 @@ class MethodParameters {
28
29
  this.preferred = undefined;
29
30
  this.exceptions = [];
30
31
  this.filename = filename;
31
- this.parse(node, scope, filename);
32
+ // need the scope for LIKE typing inside method parameters
33
+ const parentName = scope.getName();
34
+ scope.push(_scope_type_1.ScopeType.MethodDefinition, "method definition", node.getStart(), filename);
35
+ this.parse(node, scope, filename, parentName);
36
+ scope.pop(node.getEnd());
32
37
  }
33
38
  getFilename() {
34
39
  return this.filename;
@@ -103,7 +108,7 @@ class MethodParameters {
103
108
  return this.defaults[parameter.toUpperCase()];
104
109
  }
105
110
  ///////////////////
106
- parse(node, scope, filename) {
111
+ parse(node, scope, filename, parentName) {
107
112
  var _a, _b;
108
113
  const handler = node.findFirstExpression(Expressions.EventHandler);
109
114
  if (handler) {
@@ -163,9 +168,9 @@ class MethodParameters {
163
168
  if (returning) {
164
169
  this.returning = new method_def_returning_1.MethodDefReturning().runSyntax(returning, scope, this.filename, ["returning" /* IdentifierMeta.MethodReturning */]);
165
170
  }
166
- this.workaroundRAP(node, scope, filename);
171
+ this.workaroundRAP(node, scope, filename, parentName);
167
172
  }
168
- workaroundRAP(node, scope, filename) {
173
+ workaroundRAP(node, _scope, filename, parentName) {
169
174
  const resultName = node.findExpressionAfterToken("RESULT");
170
175
  const isRap = node.findExpressionAfterToken("IMPORTING");
171
176
  if (isRap) {
@@ -190,7 +195,7 @@ class MethodParameters {
190
195
  this.importing.push(new _typed_identifier_1.TypedIdentifier(token, filename, new basic_1.VoidType("RapMethodParameter"), ["exporting" /* IdentifierMeta.MethodExporting */]));
191
196
  }
192
197
  // its some kind of magic
193
- if (scope.getName().toUpperCase() === "CL_ABAP_BEHAVIOR_SAVER") {
198
+ if (parentName.toUpperCase() === "CL_ABAP_BEHAVIOR_SAVER") {
194
199
  const tempChanging = this.changing.map(c => new _typed_identifier_1.TypedIdentifier(c.getToken(), filename, new basic_1.VoidType("RapMethodParameter"), c.getMeta()));
195
200
  while (this.changing.length > 0) {
196
201
  this.changing.shift();
@@ -217,7 +222,9 @@ class MethodParameters {
217
222
  else if (meta.includes("importing" /* IdentifierMeta.MethodImporting */)) {
218
223
  extraMeta.push("read_only" /* IdentifierMeta.ReadOnly */);
219
224
  }
220
- target.push(new method_param_1.MethodParam().runSyntax(p, scope, this.filename, [...meta, ...extraMeta]));
225
+ const id = new method_param_1.MethodParam().runSyntax(p, scope, this.filename, [...meta, ...extraMeta]);
226
+ scope.addIdentifier(id);
227
+ target.push(id);
221
228
  if (opt.getLastToken().getStr().toUpperCase() === "OPTIONAL") {
222
229
  const name = target[target.length - 1].getName().toUpperCase();
223
230
  this.optional.push(name);
@@ -55,7 +55,7 @@ class LSPLookup {
55
55
  const hover = "Type Definition, " + cursor.token.getStr() + "\n\n" + this.dumpType(type);
56
56
  return { hover, definition: found, definitionId: type, scope: bottomScope };
57
57
  }
58
- const method = this.findMethodDefinition(cursor, bottomScope);
58
+ const method = this.findMethodDefinition(cursor, bottomScope.getParent());
59
59
  if (method !== undefined && method.getStart().equals(cursor.token.getStart())) {
60
60
  const found = _lsp_utils_1.LSPUtils.identiferToLocation(method);
61
61
  const hover = "Method Definition \"" + method.getName() + "\"";
@@ -272,6 +272,9 @@ class LSPLookup {
272
272
  }
273
273
  static findMethodDefinition(found, scope) {
274
274
  var _a, _b, _c, _d;
275
+ if (scope === undefined) {
276
+ return undefined;
277
+ }
275
278
  if (scope.getIdentifier().stype !== _scope_type_1.ScopeType.ClassDefinition
276
279
  || !(found.snode.get() instanceof Statements.MethodDef)) {
277
280
  return undefined;
@@ -7,6 +7,9 @@ class MSAGReferences {
7
7
  this.filenameIndex = {};
8
8
  }
9
9
  addUsing(filename, token, messageClass, number) {
10
+ if (number.length !== 3) {
11
+ return;
12
+ }
10
13
  if (this.filenameIndex[filename] === undefined) {
11
14
  this.filenameIndex[filename] = [];
12
15
  }
@@ -13,6 +13,7 @@ class MessageClass extends _abstract_object_1.AbstractObject {
13
13
  return "MSAG";
14
14
  }
15
15
  getDescription() {
16
+ this.parseXML();
16
17
  // todo
17
18
  return undefined;
18
19
  }
@@ -32,6 +33,7 @@ class MessageClass extends _abstract_object_1.AbstractObject {
32
33
  return msg ? msg : [];
33
34
  }
34
35
  getByNumber(num) {
36
+ this.parseXML();
35
37
  // todo, optimize performance,
36
38
  for (const message of this.getMessages()) {
37
39
  if (message.getNumber() === num) {
@@ -65,7 +65,7 @@ class Registry {
65
65
  }
66
66
  static abaplintVersion() {
67
67
  // magic, see build script "version.sh"
68
- return "2.102.14";
68
+ return "2.102.16";
69
69
  }
70
70
  getDDICReferences() {
71
71
  return this.ddicReferences;
@@ -30,6 +30,7 @@ class FunctionModuleRecommendationsConf extends _basic_rule_config_1.BasicRuleCo
30
30
  { name: "POPUP_TO_CONFIRM_STEP", replace: "use POPUP_TO_CONFIRM" },
31
31
  { name: "POPUP_TO_DECIDE", replace: "use POPUP_TO_CONFIRM" },
32
32
  { name: "POPUP_TO_GET_VALUE", replace: "use POPUP_GET_VALUES" },
33
+ { name: "QF05_RANDOM_INTEGER", replace: "use CL_ABAP_RANDOM_INT" },
33
34
  { name: "REUSE_ALV_GRID_DISPLAY", replace: "use CL_SALV_TABLE=>FACTORY or CL_GUI_ALV_GRID" },
34
35
  { name: "ROUND", replace: "use built in function: round()" },
35
36
  { name: "SAPGUI_PROGRESS_INDICATOR", replace: "use CL_PROGRESS_INDICATOR" },
@@ -2,17 +2,23 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MessageExistsRule = exports.MessageExistsConf = void 0;
4
4
  const Expressions = require("../abap/2_statements/expressions");
5
+ const Statements = require("../abap/2_statements/statements");
5
6
  const issue_1 = require("../issue");
6
- const _abap_rule_1 = require("./_abap_rule");
7
7
  const _basic_rule_config_1 = require("./_basic_rule_config");
8
8
  const ddic_1 = require("../ddic");
9
9
  const _irule_1 = require("./_irule");
10
+ const nodes_1 = require("../abap/nodes");
11
+ const _abap_object_1 = require("../objects/_abap_object");
12
+ const syntax_1 = require("../abap/5_syntax/syntax");
10
13
  class MessageExistsConf extends _basic_rule_config_1.BasicRuleConfig {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.checkPlaceholders = true;
17
+ }
11
18
  }
12
19
  exports.MessageExistsConf = MessageExistsConf;
13
- class MessageExistsRule extends _abap_rule_1.ABAPRule {
20
+ class MessageExistsRule {
14
21
  constructor() {
15
- super(...arguments);
16
22
  this.conf = new MessageExistsConf();
17
23
  }
18
24
  getMetadata() {
@@ -29,58 +35,116 @@ class MessageExistsRule extends _abap_rule_1.ABAPRule {
29
35
  setConfig(conf) {
30
36
  this.conf = conf;
31
37
  }
32
- runParsed(file) {
38
+ initialize(reg) {
39
+ this.msagReferences = reg.getMSAGReferences();
40
+ this.reg = reg;
41
+ // the SyntaxLogic builds the references
42
+ for (const obj of reg.getObjects()) {
43
+ if (obj instanceof _abap_object_1.ABAPObject) {
44
+ new syntax_1.SyntaxLogic(reg, obj).run();
45
+ }
46
+ }
47
+ return this;
48
+ }
49
+ run(obj) {
33
50
  const issues = [];
34
- const struc = file.getStructure();
35
- if (struc === undefined) {
36
- return [];
51
+ if (obj instanceof _abap_object_1.ABAPObject) {
52
+ for (const file of obj.getABAPFiles()) {
53
+ const struc = file.getStructure();
54
+ if (struc === undefined) {
55
+ return [];
56
+ }
57
+ issues.push(...this.checkReportStatement(file));
58
+ issues.push(...this.checkSource(file));
59
+ }
37
60
  }
38
- const expressions = struc.findAllExpressionsMulti([Expressions.MessageClass, Expressions.MessageSource]);
39
- for (const node of expressions) {
40
- if (node.get() instanceof Expressions.MessageClass) {
41
- const token = node.getFirstToken();
42
- const name = token.getStr();
43
- if (this.reg.getObject("MSAG", name) === undefined
44
- && new ddic_1.DDIC(this.reg).inErrorNamespace(name) === true) {
45
- const message = "Message class \"" + name + "\" not found";
46
- const issue = issue_1.Issue.atToken(file, token, message, this.getMetadata().key, this.conf.severity);
61
+ return issues;
62
+ }
63
+ ////////////////////////////////
64
+ checkReportStatement(file) {
65
+ const issues = [];
66
+ for (const statement of file.getStatements()) {
67
+ if (!(statement.get() instanceof Statements.Report)) {
68
+ continue;
69
+ }
70
+ const expression = statement.findFirstExpression(Expressions.MessageClass);
71
+ if (expression) {
72
+ const issue = this.checkClass(expression, file);
73
+ if (issue) {
47
74
  issues.push(issue);
48
75
  }
49
76
  }
50
77
  }
51
- for (const node of expressions) {
52
- if (node.get() instanceof Expressions.MessageSource) {
53
- const clas = node.findFirstExpression(Expressions.MessageClass);
54
- if (clas === undefined) {
55
- // todo, handle case where message class is defined on header level instead of in the statement
56
- continue;
57
- }
58
- const token = clas.getFirstToken();
59
- const name = token.getStr();
60
- const msag = this.reg.getObject("MSAG", name);
61
- if (msag === undefined) {
62
- if (new ddic_1.DDIC(this.reg).inErrorNamespace(name) === true) {
63
- const message = "Message class \"" + token.getStr() + "\" not found";
64
- const issue = issue_1.Issue.atToken(file, token, message, this.getMetadata().key, this.conf.severity);
65
- issues.push(issue);
78
+ return issues;
79
+ }
80
+ checkClass(node, file) {
81
+ const token = node.getFirstToken();
82
+ const name = token.getStr();
83
+ if (this.reg.getObject("MSAG", name) === undefined
84
+ && new ddic_1.DDIC(this.reg).inErrorNamespace(name) === true) {
85
+ const message = "Message class \"" + name + "\" not found";
86
+ return issue_1.Issue.atToken(file, token, message, this.getMetadata().key, this.conf.severity);
87
+ }
88
+ return undefined;
89
+ }
90
+ checkSource(file) {
91
+ const issues = [];
92
+ const references = this.msagReferences.listByFilename(file.getFilename());
93
+ for (const statement of file.getStatements()) {
94
+ if (statement.get() instanceof Statements.Raise || statement.get() instanceof Statements.Message) {
95
+ for (const ref of references) {
96
+ // always max one message reference per statement? chained statements?
97
+ if (ref.token.getStart().isBetween(statement.getStart(), statement.getEnd())) {
98
+ const msag = this.reg.getObject("MSAG", ref.messageClass);
99
+ if (msag === undefined) {
100
+ if (new ddic_1.DDIC(this.reg).inErrorNamespace(ref.messageClass) === true) {
101
+ const message = "Message class \"" + ref.token.getStr() + "\" not found";
102
+ issues.push(issue_1.Issue.atToken(file, ref.token, message, this.getMetadata().key, this.conf.severity));
103
+ }
104
+ continue;
105
+ }
106
+ const text = msag.getByNumber(ref.number);
107
+ if (text === undefined) {
108
+ const message = "Message number \"" + ref.number + "\" not found in class \"" + ref.messageClass + "\"";
109
+ issues.push(issue_1.Issue.atToken(file, ref.token, message, this.getMetadata().key, this.conf.severity));
110
+ continue;
111
+ }
112
+ if (this.getConfig().checkPlaceholders === true) {
113
+ const count = this.countWith(statement);
114
+ const textCount = text.getPlaceholderCount();
115
+ if (count !== textCount) {
116
+ const message = `Message ${ref.number}, expected ${textCount} WITH parameters`;
117
+ issues.push(issue_1.Issue.atToken(file, ref.token, message, this.getMetadata().key, this.conf.severity));
118
+ }
119
+ }
66
120
  }
67
- continue;
68
121
  }
69
- const typeNumber = node.findFirstExpression(Expressions.MessageTypeAndNumber);
70
- if (typeNumber === undefined) {
71
- continue;
122
+ }
123
+ }
124
+ return issues;
125
+ }
126
+ countWith(statement) {
127
+ const raiseWith = statement.findDirectExpression(Expressions.RaiseWith);
128
+ if (raiseWith) {
129
+ return raiseWith.getChildren().length - 1;
130
+ }
131
+ let count = 0;
132
+ let afterWith = false;
133
+ for (const expression of statement.getChildren()) {
134
+ if (expression instanceof nodes_1.TokenNode && expression.concatTokens().toUpperCase() === "WITH") {
135
+ afterWith = true;
136
+ continue;
137
+ }
138
+ if (afterWith === true) {
139
+ if (expression instanceof nodes_1.ExpressionNode) {
140
+ count++;
72
141
  }
73
- const numberToken = typeNumber.getFirstToken();
74
- const num = numberToken.getStr().substr(1);
75
- if (msag.getByNumber(num) === undefined) {
76
- const message = "Message number \"" + num + "\" not found in class \"" + name + "\"";
77
- const issue = issue_1.Issue.atToken(file, numberToken, message, this.getMetadata().key, this.conf.severity);
78
- issues.push(issue);
142
+ else {
143
+ break;
79
144
  }
80
145
  }
81
146
  }
82
- // todo, check number of placeholders in message vs code matches
83
- return issues;
147
+ return count;
84
148
  }
85
149
  }
86
150
  exports.MessageExistsRule = MessageExistsRule;
@@ -49,15 +49,21 @@ This rule makes sure the spaces are consistently required across the language.`,
49
49
  return issues;
50
50
  }
51
51
  missingSpace(statement) {
52
- const found = statement.findAllExpressionsMulti([Expressions.CondSub, Expressions.SQLCond,
53
- Expressions.ValueBodyLine, Expressions.NewObject, Expressions.Cond,
54
- Expressions.ComponentCond, Expressions.ComponentCondSub, Expressions.MethodCallParam], true);
52
+ const found = statement.findAllExpressionsMulti([
53
+ Expressions.CondSub, Expressions.SQLCond, Expressions.ValueBodyLine,
54
+ Expressions.NewObject, Expressions.Cond, Expressions.ComponentCond,
55
+ Expressions.Source,
56
+ Expressions.ComponentCondSub, Expressions.MethodCallParam
57
+ ], true);
55
58
  let pos = undefined;
56
59
  for (const f of found) {
57
60
  const type = f.get();
58
61
  if (type instanceof Expressions.Cond) {
59
62
  pos = this.checkCond(f);
60
63
  }
64
+ else if (type instanceof Expressions.Source) {
65
+ pos = this.checkSource(f);
66
+ }
61
67
  else if (type instanceof Expressions.CondSub) {
62
68
  pos = this.checkCondSub(f);
63
69
  }
@@ -210,6 +216,20 @@ This rule makes sure the spaces are consistently required across the language.`,
210
216
  }
211
217
  return undefined;
212
218
  }
219
+ checkSource(cond) {
220
+ const children = cond.getAllTokens();
221
+ if (children.length < 2) {
222
+ return undefined;
223
+ }
224
+ const nextLast = children[children.length - 2];
225
+ const last = children[children.length - 1];
226
+ if (nextLast.getStr().startsWith("'")
227
+ && nextLast.getRow() === last.getRow()
228
+ && nextLast.getEnd().getCol() === last.getStart().getCol()) {
229
+ return last.getEnd();
230
+ }
231
+ return undefined;
232
+ }
213
233
  checkMethodCallParam(call) {
214
234
  const children = call.getChildren();
215
235
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.102.14",
3
+ "version": "2.102.16",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",
@@ -53,7 +53,7 @@
53
53
  "@microsoft/api-extractor": "^7.36.3",
54
54
  "@types/chai": "^4.3.5",
55
55
  "@types/mocha": "^10.0.1",
56
- "@types/node": "^20.4.5",
56
+ "@types/node": "^20.4.8",
57
57
  "chai": "^4.3.7",
58
58
  "eslint": "^8.46.0",
59
59
  "mocha": "^10.2.0",