@abaplint/core 2.102.15 → 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,7 +3707,8 @@ declare interface IStructure {
3706
3707
  declare interface IStructureComponent {
3707
3708
  name: string;
3708
3709
  type: AbstractType;
3709
- renamingSuffix?: string;
3710
+ asInclude?: boolean;
3711
+ suffix?: string;
3710
3712
  }
3711
3713
 
3712
3714
  declare interface IStructureRunnable {
@@ -6326,6 +6328,10 @@ declare class Translate implements IStatement {
6326
6328
  getMatcher(): IStatementRunnable;
6327
6329
  }
6328
6330
 
6331
+ declare class TransportingFields extends Expression {
6332
+ getRunnable(): IStatementRunnable;
6333
+ }
6334
+
6329
6335
  declare class TruncateDataset implements IStatement {
6330
6336
  getMatcher(): IStatementRunnable;
6331
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
  }
@@ -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(Object.assign(Object.assign({}, c), { renamingSuffix: suffix }));
31
+ components.push(Object.assign(Object.assign({}, c), { suffix: suffix, asInclude: c.asInclude }));
28
32
  continue;
29
33
  }
30
34
  components.push({
@@ -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;
@@ -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.15";
68
+ return "2.102.16";
69
69
  }
70
70
  getDDICReferences() {
71
71
  return this.ddicReferences;
@@ -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.15",
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",