@abaplint/core 2.120.27 → 2.120.29

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.
@@ -9,7 +9,7 @@ const sql_field_1 = require("./sql_field");
9
9
  const sql_grouping_sets_1 = require("./sql_grouping_sets");
10
10
  class SQLGroupBy extends combi_1.Expression {
11
11
  getRunnable() {
12
- const fieldName = (0, combi_1.alt)(sql_field_name_1.SQLFieldName, dynamic_1.Dynamic);
12
+ const fieldName = (0, combi_1.seq)((0, combi_1.stopBefore)("ORDER", "BY"), (0, combi_1.alt)(sql_field_name_1.SQLFieldName, dynamic_1.Dynamic));
13
13
  const expr = (0, combi_1.ver)(version_1.Release.v740sp02, sql_field_1.SQLField, { also: combi_1.AlsoIn.OpenABAP });
14
14
  const newGroup = (0, combi_1.ver)(version_1.Release.v740sp02, (0, combi_1.seq)("GROUP BY", expr, (0, combi_1.altPrio)((0, combi_1.plus)((0, combi_1.seq)(",", expr)), (0, combi_1.optPrio)((0, combi_1.plus)(fieldName)))), { also: combi_1.AlsoIn.OpenABAP });
15
15
  const old = (0, combi_1.seq)((0, combi_1.plus)((0, combi_1.seq)(fieldName, ",")), fieldName);
@@ -171,6 +171,31 @@ class ObjectOriented {
171
171
  }
172
172
  return ret;
173
173
  }
174
+ // does the supplied class have friendship with "friendName"?
175
+ // note that interfaces of the friend, and subclasses of the friend also have friendship
176
+ hasFriendship(cd, friendName) {
177
+ var _a;
178
+ const friends = cd.getFriends().map(f => f.toUpperCase());
179
+ const isFriend = (name) => friends.includes(name.toUpperCase()) || this.scope.isLocalFriend(cd.getName(), name);
180
+ if (isFriend(friendName)) {
181
+ return true;
182
+ }
183
+ const friendDefinition = this.scope.findClassDefinition(friendName);
184
+ if (friendDefinition === undefined) {
185
+ return false;
186
+ }
187
+ if (this.findInterfaces(friendDefinition).some(i => friends.includes(i.name.toUpperCase()))) {
188
+ return true;
189
+ }
190
+ let sup = friendDefinition.getSuperClass();
191
+ while (sup !== undefined) {
192
+ if (isFriend(sup)) {
193
+ return true;
194
+ }
195
+ sup = (_a = this.scope.findClassDefinition(sup)) === null || _a === void 0 ? void 0 : _a.getSuperClass();
196
+ }
197
+ return false;
198
+ }
174
199
  searchEvent(def, name) {
175
200
  if (def === undefined || name === undefined) {
176
201
  return undefined;
@@ -567,6 +567,9 @@ class TypeUtils {
567
567
  console.dir(source);
568
568
  console.dir(target);
569
569
  */
570
+ if (source instanceof basic_1.UTCLongType || target instanceof basic_1.UTCLongType) {
571
+ return this.isUTCLongAssignable(source, target);
572
+ }
570
573
  if (target instanceof basic_1.TableType) {
571
574
  if (target.isWithHeader()) {
572
575
  return this.isAssignable(source, target.getRowType());
@@ -718,6 +721,33 @@ class TypeUtils {
718
721
  }
719
722
  return true;
720
723
  }
724
+ // utclong can only be converted to and from character like types
725
+ isUTCLongAssignable(source, target) {
726
+ if (this.isGenericOrVoid(source) || this.isGenericOrVoid(target)) {
727
+ return true;
728
+ }
729
+ else if (source instanceof basic_1.UTCLongType && target instanceof basic_1.UTCLongType) {
730
+ return true;
731
+ }
732
+ else if (source instanceof basic_1.UTCLongType) {
733
+ return this.isTextLike(target);
734
+ }
735
+ return this.isTextLike(source);
736
+ }
737
+ isGenericOrVoid(type) {
738
+ return type instanceof basic_1.VoidType
739
+ || type instanceof basic_1.AnyType
740
+ || type instanceof basic_1.DataType
741
+ || type instanceof basic_1.UnknownType;
742
+ }
743
+ isTextLike(type) {
744
+ return type instanceof basic_1.CharacterType
745
+ || type instanceof basic_1.StringType
746
+ || type instanceof basic_1.CLikeType
747
+ || type instanceof basic_1.CSequenceType
748
+ || type instanceof cgeneric_type_1.CGenericType
749
+ || type instanceof basic_1.SimpleType;
750
+ }
721
751
  }
722
752
  exports.TypeUtils = TypeUtils;
723
753
  //# sourceMappingURL=_type_utils.js.map
@@ -25,7 +25,7 @@ function checkDatabaseFields(fields, dbSources, input, token) {
25
25
  if (field === "*") {
26
26
  continue;
27
27
  }
28
- if (/^\w+$/.test(field) && type.getComponentByName(field) === undefined) {
28
+ if (/^[A-Z_]\w*$/i.test(field) && type.getComponentByName(field) === undefined) {
29
29
  const message = `checkFields, field ${field} not found`;
30
30
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, token, message));
31
31
  return;
@@ -47,6 +47,8 @@ const _reference_1 = require("../_reference");
47
47
  const component_name_1 = require("./component_name");
48
48
  const attribute_name_1 = require("./attribute_name");
49
49
  const _syntax_input_1 = require("../_syntax_input");
50
+ const visibility_1 = require("../../4_file_information/visibility");
51
+ const class_definition_1 = require("../../types/class_definition");
50
52
  class MethodCallChain {
51
53
  static runSyntax(node, input, targetType) {
52
54
  var _a, _b;
@@ -95,6 +97,12 @@ class MethodCallChain {
95
97
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, methodToken, message));
96
98
  return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
97
99
  }
100
+ const notVisible = method ? this.checkVisibility(method, def, foundDef, input) : undefined;
101
+ if (notVisible !== undefined) {
102
+ const message = `Method "${methodName}" is ${notVisible} and cannot be accessed`;
103
+ input.issues.push((0, _syntax_input_1.syntaxIssue)(input, methodToken, message));
104
+ return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
105
+ }
98
106
  const voidedName = context instanceof basic_1.VoidType ? context.getVoided() : undefined;
99
107
  const extra = helper.methodReferenceExtras(foundDef, className || voidedName);
100
108
  input.scope.addReference(methodToken, method, _reference_1.ReferenceType.MethodReference, input.filename, extra);
@@ -137,6 +145,47 @@ class MethodCallChain {
137
145
  return context;
138
146
  }
139
147
  //////////////////////////////////////
148
+ // returns the visibility as text if the method cannot be accessed from the current scope
149
+ static checkVisibility(method, def, foundDef, input) {
150
+ var _a, _b, _c;
151
+ const visibility = method.getVisibility();
152
+ if (visibility === visibility_1.Visibility.Public || foundDef === undefined) {
153
+ return undefined;
154
+ }
155
+ else if (!(foundDef instanceof class_definition_1.ClassDefinition)) {
156
+ // interface members are always public
157
+ return undefined;
158
+ }
159
+ const name = foundDef.getName().toUpperCase();
160
+ const enclosing = (_a = input.scope.getEnclosingClassName()) === null || _a === void 0 ? void 0 : _a.toUpperCase();
161
+ if (enclosing === undefined || enclosing === name) {
162
+ return undefined;
163
+ }
164
+ // friendship with the class used at the call site also gives access to inherited members
165
+ const helper = new _object_oriented_1.ObjectOriented(input.scope);
166
+ const visited = [];
167
+ let current = def instanceof class_definition_1.ClassDefinition ? def : foundDef;
168
+ while (current !== undefined && visited.includes(current.getName().toUpperCase()) === false) {
169
+ visited.push(current.getName().toUpperCase());
170
+ if (helper.hasFriendship(current, enclosing)) {
171
+ return undefined;
172
+ }
173
+ const sup = current.getSuperClass();
174
+ current = sup === undefined ? undefined : input.scope.findClassDefinition(sup);
175
+ }
176
+ if (visibility === visibility_1.Visibility.Protected) {
177
+ // subclasses can access protected members
178
+ let sup = (_b = input.scope.findClassDefinition(enclosing)) === null || _b === void 0 ? void 0 : _b.getSuperClass();
179
+ while (sup !== undefined) {
180
+ if (sup.toUpperCase() === name) {
181
+ return undefined;
182
+ }
183
+ sup = (_c = input.scope.findClassDefinition(sup)) === null || _c === void 0 ? void 0 : _c.getSuperClass();
184
+ }
185
+ return "protected";
186
+ }
187
+ return "private";
188
+ }
140
189
  static findTop(first, input, targetType) {
141
190
  var _a;
142
191
  if (first.get() instanceof Expressions.ClassName) {
@@ -74,7 +74,7 @@ class Select {
74
74
  return;
75
75
  }
76
76
  (0, _check_database_fields_1.checkDatabaseFields)(fields.map(field => field.code), dbSources, input, node.getFirstToken());
77
- const intoExpression = this.handleInto(node, input, fields, dbSources);
77
+ const intoExpression = this.handleInto(node, input, fields, dbSources, from);
78
78
  const fae = node.findDirectExpression(Expressions.SQLForAllEntries);
79
79
  if (fae) {
80
80
  input.scope.push(_scope_type_1.ScopeType.OpenSQL, "SELECT", token.getStart(), input.filename);
@@ -235,12 +235,12 @@ class Select {
235
235
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
236
236
  }
237
237
  }
238
- static handleInto(node, input, fields, dbSources) {
238
+ static handleInto(node, input, fields, dbSources, from) {
239
239
  const intoTable = node.findDirectExpression(Expressions.SQLIntoTable);
240
240
  if (intoTable) {
241
241
  const inline = intoTable.findFirstExpression(Expressions.InlineData);
242
242
  if (inline) {
243
- inline_data_1.InlineData.runSyntax(inline, input, this.buildTableType(fields, dbSources, input.scope));
243
+ inline_data_1.InlineData.runSyntax(inline, input, this.buildTableType(fields, dbSources, input.scope, from));
244
244
  }
245
245
  return intoTable;
246
246
  }
@@ -388,11 +388,36 @@ class Select {
388
388
  return basic_1.VoidType.get("SELECT_todo13");
389
389
  }
390
390
  }
391
- static buildTableType(fields, dbSources, scope) {
392
- if (dbSources.length !== 1) {
391
+ static buildTableType(fields, dbSources, scope, from) {
392
+ var _a;
393
+ const tableOptions = { withHeader: false, keyType: basic_1.TableKeyType.empty };
394
+ if (dbSources.length > 1) {
395
+ const sourceInfos = this.buildDatabaseSourceInfos(from, dbSources);
396
+ if (fields.length === 1 && fields[0].code === "*") {
397
+ const components = [];
398
+ for (const info of sourceInfos) {
399
+ const type = (_a = info.source) === null || _a === void 0 ? void 0 : _a.parseType(scope.getRegistry());
400
+ if (!(type instanceof basic_1.StructureType)) {
401
+ return basic_1.VoidType.get("SELECT_todo3");
402
+ }
403
+ components.push({ name: info.alias || info.name, type });
404
+ }
405
+ return new basic_1.TableType(new basic_1.StructureType(components), tableOptions, undefined);
406
+ }
407
+ const components = [];
408
+ for (const field of fields) {
409
+ const type = this.findFieldType(field.code, sourceInfos, scope);
410
+ if (type === undefined) {
411
+ return basic_1.VoidType.get("SELECT_todo3");
412
+ }
413
+ const name = field.as || field.code.split("~").pop();
414
+ components.push({ name, type });
415
+ }
416
+ return new basic_1.TableType(new basic_1.StructureType(components), tableOptions, undefined);
417
+ }
418
+ else if (dbSources.length !== 1) {
393
419
  return basic_1.VoidType.get("SELECT_todo3");
394
420
  }
395
- const tableOptions = { withHeader: false, keyType: basic_1.TableKeyType.empty };
396
421
  if (dbSources[0] === undefined) {
397
422
  // then its a voided table
398
423
  return new basic_1.TableType(basic_1.VoidType.get("SELECT_todo4"), tableOptions, undefined);
@@ -418,6 +443,39 @@ class Select {
418
443
  }
419
444
  return basic_1.VoidType.get("SELECT_todo7");
420
445
  }
446
+ static buildDatabaseSourceInfos(from, dbSources) {
447
+ const fromSources = from.findAllExpressions(Expressions.SQLFromSource);
448
+ return dbSources.map((source, index) => {
449
+ var _a, _b;
450
+ const fromSource = fromSources[index];
451
+ const name = ((_a = fromSource === null || fromSource === void 0 ? void 0 : fromSource.findFirstExpression(Expressions.DatabaseTable)) === null || _a === void 0 ? void 0 : _a.getFirstToken().getStr().replace(/^\*/, "").toUpperCase()) || "";
452
+ const alias = ((_b = fromSource === null || fromSource === void 0 ? void 0 : fromSource.findDirectExpression(Expressions.SQLAsName)) === null || _b === void 0 ? void 0 : _b.concatTokens().toUpperCase()) || "";
453
+ return { source, name, alias };
454
+ });
455
+ }
456
+ static findFieldType(code, sourceInfos, scope) {
457
+ var _a;
458
+ const separator = code.indexOf("~");
459
+ const qualifier = separator >= 0 ? code.substring(0, separator) : undefined;
460
+ const fieldName = separator >= 0 ? code.substring(separator + 1) : code;
461
+ const candidates = qualifier === undefined
462
+ ? sourceInfos
463
+ : sourceInfos.filter(info => info.alias === qualifier || info.name === qualifier);
464
+ for (const candidate of candidates) {
465
+ let type = (_a = candidate.source) === null || _a === void 0 ? void 0 : _a.parseType(scope.getRegistry());
466
+ for (const component of fieldName.split("-")) {
467
+ if (!(type instanceof basic_1.StructureType)) {
468
+ type = undefined;
469
+ break;
470
+ }
471
+ type = type.getComponentByName(component);
472
+ }
473
+ if (type) {
474
+ return type;
475
+ }
476
+ }
477
+ return undefined;
478
+ }
421
479
  static findFields(node, input) {
422
480
  var _a, _b;
423
481
  let expr = undefined;
@@ -431,7 +489,7 @@ class Select {
431
489
  let code = field.concatTokens().toUpperCase();
432
490
  const as = ((_b = field.findDirectExpression(Expressions.SQLAsName)) === null || _b === void 0 ? void 0 : _b.concatTokens()) || "";
433
491
  if (as !== "") {
434
- code = code.replace(" AS " + as, "");
492
+ code = code.replace(" AS " + as.toUpperCase(), "");
435
493
  }
436
494
  ret.push({ code, as, expression: field });
437
495
  }
@@ -39,6 +39,7 @@ const basic_1 = require("../../types/basic");
39
39
  const _syntax_input_1 = require("../_syntax_input");
40
40
  const sql_set_op_group_1 = require("./sql_set_op_group");
41
41
  const sql_source_1 = require("./sql_source");
42
+ const RANGE_COMPONENTS = ["SIGN", "OPTION", "LOW", "HIGH"];
42
43
  class SQLIn {
43
44
  static runSyntax(node, input) {
44
45
  const setop = node.findDirectExpression(Expressions.SQLSetOpGroup);
@@ -58,6 +59,12 @@ class SQLIn {
58
59
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
59
60
  return;
60
61
  }
62
+ if (intype instanceof basic_1.TableType && this.isRangeRow(intype.getRowType()) === false) {
63
+ const name = insource.concatTokens().replace(/^@/, "").replace(/\[\]$/, "");
64
+ const message = `row structure of ${name} is not correct`;
65
+ input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
66
+ return;
67
+ }
61
68
  }
62
69
  return;
63
70
  }
@@ -68,6 +75,16 @@ class SQLIn {
68
75
  sql_source_1.SQLSource.runSyntax(s, input);
69
76
  }
70
77
  }
78
+ // "IN itab" expects a ranges table, ie. the row must have SIGN, OPTION, LOW and HIGH
79
+ static isRangeRow(rowType) {
80
+ if (rowType instanceof basic_1.VoidType || rowType instanceof basic_1.UnknownType || rowType instanceof basic_1.AnyType) {
81
+ return true;
82
+ }
83
+ else if (!(rowType instanceof basic_1.StructureType)) {
84
+ return false;
85
+ }
86
+ return RANGE_COMPONENTS.every(c => rowType.getComponentByName(c) !== undefined);
87
+ }
71
88
  }
72
89
  exports.SQLIn = SQLIn;
73
90
  //# sourceMappingURL=sql_in.js.map
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.ValueBody = void 0;
37
+ const nodes_1 = require("../../nodes");
37
38
  const Expressions = __importStar(require("../../2_statements/expressions"));
38
39
  const for_1 = require("./for");
39
40
  const source_1 = require("./source");
@@ -43,7 +44,7 @@ const basic_1 = require("../../types/basic");
43
44
  const _syntax_input_1 = require("../_syntax_input");
44
45
  class ValueBody {
45
46
  static runSyntax(node, input, targetType) {
46
- var _a, _b;
47
+ var _a, _b, _c;
47
48
  if (node === undefined) {
48
49
  return targetType;
49
50
  }
@@ -60,26 +61,45 @@ class ValueBody {
60
61
  }
61
62
  }
62
63
  const fields = new Set();
63
- for (const s of node.findDirectExpressions(Expressions.FieldAssignment)) {
64
- field_assignment_1.FieldAssignment.runSyntax(s, input, targetType);
65
- if (node.findDirectExpression(Expressions.ValueBodyLine) === undefined) {
66
- // todo: refine, still needs to be checked when there are table lines
67
- const fieldname = (_a = s.findDirectExpression(Expressions.FieldSub)) === null || _a === void 0 ? void 0 : _a.concatTokens().toUpperCase();
64
+ const hasTableLines = node.findDirectExpression(Expressions.ValueBodyLine) !== undefined;
65
+ for (const child of node.getChildren()) {
66
+ if (!(child instanceof nodes_1.ExpressionNode)) {
67
+ continue;
68
+ }
69
+ else if (child.get() instanceof Expressions.FieldAssignment) {
70
+ const fieldname = (_a = child.findDirectExpression(Expressions.FieldSub)) === null || _a === void 0 ? void 0 : _a.concatTokens().toUpperCase();
71
+ if (fieldname && hasTableLines === false && fields.has(fieldname)) {
72
+ const message = "Duplicate field assignment";
73
+ input.issues.push((0, _syntax_input_1.syntaxIssue)(input, child.getFirstToken(), message));
74
+ return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
75
+ }
68
76
  if (fieldname) {
69
- if (fields.has(fieldname)) {
77
+ fields.add(fieldname);
78
+ }
79
+ }
80
+ else if (child.get() instanceof Expressions.ValueBodyLine) {
81
+ const rowFields = new Set(fields);
82
+ for (const assignment of child.findDirectExpressions(Expressions.FieldAssignment)) {
83
+ const fieldname = (_b = assignment.findDirectExpression(Expressions.FieldSub)) === null || _b === void 0 ? void 0 : _b.concatTokens().toUpperCase();
84
+ if (fieldname && rowFields.has(fieldname)) {
70
85
  const message = "Duplicate field assignment";
71
- input.issues.push((0, _syntax_input_1.syntaxIssue)(input, s.getFirstToken(), message));
86
+ input.issues.push((0, _syntax_input_1.syntaxIssue)(input, assignment.getFirstToken(), message));
72
87
  return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
73
88
  }
74
- fields.add(fieldname);
89
+ if (fieldname) {
90
+ rowFields.add(fieldname);
91
+ }
75
92
  }
76
93
  }
77
94
  }
95
+ for (const s of node.findDirectExpressions(Expressions.FieldAssignment)) {
96
+ field_assignment_1.FieldAssignment.runSyntax(s, input, targetType);
97
+ }
78
98
  let type = undefined; // todo, this is only correct if there is a single source in the body
79
99
  for (const s of node.findDirectExpressions(Expressions.Source)) {
80
100
  type = source_1.Source.runSyntax(s, input, type);
81
101
  }
82
- for (const s of ((_b = node.findDirectExpression(Expressions.ValueBase)) === null || _b === void 0 ? void 0 : _b.findDirectExpressions(Expressions.Source)) || []) {
102
+ for (const s of ((_c = node.findDirectExpression(Expressions.ValueBase)) === null || _c === void 0 ? void 0 : _c.findDirectExpressions(Expressions.Source)) || []) {
83
103
  type = source_1.Source.runSyntax(s, input, type);
84
104
  }
85
105
  for (const foo of node.findDirectExpressions(Expressions.ValueBodyLine)) {
@@ -151,7 +151,7 @@ class CreateObject {
151
151
  this.validateParameters(cdef, node, input);
152
152
  }
153
153
  static checkInstantiationAllowed(cdef, input) {
154
- var _a, _b, _c;
154
+ var _a, _b;
155
155
  const createVis = cdef.getCreateVisibility();
156
156
  if (createVis === visibility_1.Visibility.Public) {
157
157
  return undefined;
@@ -164,34 +164,17 @@ class CreateObject {
164
164
  if (enclosingClass.toUpperCase() === cdef.getName().toUpperCase()) {
165
165
  return undefined;
166
166
  }
167
- if (cdef.getFriends().some(f => f.toUpperCase() === enclosingClass.toUpperCase()) ||
168
- input.scope.isLocalFriend(cdef.getName(), enclosingClass)) {
167
+ if (new _object_oriented_1.ObjectOriented(input.scope).hasFriendship(cdef, enclosingClass)) {
169
168
  return undefined;
170
169
  }
171
- const enclosingDefinition = input.scope.findClassDefinition(enclosingClass);
172
- if (enclosingDefinition !== undefined) {
173
- const implementedInterfaces = new _object_oriented_1.ObjectOriented(input.scope).findInterfaces(enclosingDefinition);
174
- if (cdef.getFriends().some(friend => implementedInterfaces.some(implemented => implemented.name.toUpperCase() === friend.toUpperCase()))) {
175
- return undefined;
176
- }
177
- }
178
- // subclasses of friends also have friendship
179
- let enclosingSup = enclosingDefinition === null || enclosingDefinition === void 0 ? void 0 : enclosingDefinition.getSuperClass();
180
- while (enclosingSup !== undefined) {
181
- if (cdef.getFriends().some(f => f.toUpperCase() === enclosingSup.toUpperCase()) ||
182
- input.scope.isLocalFriend(cdef.getName(), enclosingSup)) {
183
- return undefined;
184
- }
185
- enclosingSup = (_a = input.scope.findClassDefinition(enclosingSup)) === null || _a === void 0 ? void 0 : _a.getSuperClass();
186
- }
187
170
  if (createVis === visibility_1.Visibility.Protected) {
188
171
  // subclasses are also allowed
189
- let sup = (_b = input.scope.findClassDefinition(enclosingClass)) === null || _b === void 0 ? void 0 : _b.getSuperClass();
172
+ let sup = (_a = input.scope.findClassDefinition(enclosingClass)) === null || _a === void 0 ? void 0 : _a.getSuperClass();
190
173
  while (sup !== undefined) {
191
174
  if (sup.toUpperCase() === cdef.getName().toUpperCase()) {
192
175
  return undefined;
193
176
  }
194
- sup = (_c = input.scope.findClassDefinition(sup)) === null || _c === void 0 ? void 0 : _c.getSuperClass();
177
+ sup = (_b = input.scope.findClassDefinition(sup)) === null || _b === void 0 ? void 0 : _b.getSuperClass();
195
178
  }
196
179
  }
197
180
  return cdef.getName() + " cannot be instantiated, class is defined as " +
@@ -39,6 +39,7 @@ const source_1 = require("../expressions/source");
39
39
  const dynamic_1 = require("../expressions/dynamic");
40
40
  const database_table_1 = require("../expressions/database_table");
41
41
  const _check_database_fields_1 = require("../expressions/_check_database_fields");
42
+ const sql_in_1 = require("../expressions/sql_in");
42
43
  class DeleteDatabase {
43
44
  runSyntax(node, input) {
44
45
  for (const s of node.findAllExpressions(Expressions.Source)) {
@@ -50,6 +51,12 @@ class DeleteDatabase {
50
51
  for (const d of node.findAllExpressions(Expressions.Dynamic)) {
51
52
  dynamic_1.Dynamic.runSyntax(d, input);
52
53
  }
54
+ for (const compare of node.findAllExpressions(Expressions.SQLCompare)) {
55
+ const sqlin = compare.findDirectExpression(Expressions.SQLIn);
56
+ if (sqlin !== undefined) {
57
+ sql_in_1.SQLIn.runSyntax(sqlin, input);
58
+ }
59
+ }
53
60
  const dbtab = node.findFirstExpression(Expressions.DatabaseTable);
54
61
  if (dbtab !== undefined) {
55
62
  const dbSource = database_table_1.DatabaseTable.runSyntax(dbtab, 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.27";
78
+ return "2.120.29";
79
79
  }
80
80
  getDDICReferences() {
81
81
  return this.ddicReferences;
@@ -14,6 +14,11 @@ class CDSCheckSyntaxConf extends _basic_rule_config_1.BasicRuleConfig {
14
14
  exports.CDSCheckSyntaxConf = CDSCheckSyntaxConf;
15
15
  const KNOWN_QUAN_DATA_ELEMENTS = ["MENGE_D"];
16
16
  const KNOWN_CURR_DATA_ELEMENTS = ["BWERT", "DZWERT"];
17
+ // Reserved names, cannot be used as element names, see DDIC table TRESE
18
+ const RESERVED_ELEMENT_NAMES = ["BEGIN", "NUMBER", "POSITION"];
19
+ const MAX_LABEL_LENGTH = 40;
20
+ // Annotations which are not supported in CDS view entities
21
+ const VIEW_ENTITY_FORBIDDEN_ANNOTATIONS = ["Semantics.unitOfMeasure"];
17
22
  class CDSCheckSyntax {
18
23
  constructor() {
19
24
  this.conf = new CDSCheckSyntaxConf();
@@ -55,6 +60,9 @@ Missing objects are only reported when their names match the configured errorNam
55
60
  this.checkTypes(tree, ddic, issues, file);
56
61
  this.checkRootProjection(tree, ddic, issues, file);
57
62
  this.checkFieldAnnotations(tree, ddic, issues, file);
63
+ this.checkLabels(tree, issues, file);
64
+ this.checkViewEntityAnnotations(tree, issues, file);
65
+ this.checkReservedNames(tree, issues, file);
58
66
  this.checkFields(tree, sources, object, issues, file);
59
67
  return issues;
60
68
  }
@@ -239,6 +247,62 @@ Missing objects are only reported when their names match the configured errorNam
239
247
  || a.toUpperCase().includes("@SEMANTICS:{CURRENCYCODE:TRUE"));
240
248
  }
241
249
  }
250
+ checkLabels(tree, issues, file) {
251
+ for (const annotation of tree.findAllExpressionsRecursive(expressions_1.CDSAnnotation)) {
252
+ const concatenated = annotation.concatTokens();
253
+ if (/@\s*<?\s*EndUserText/i.test(concatenated) === false) {
254
+ continue;
255
+ }
256
+ const expression = /label\s*:\s*'((?:[^']|'')*)'/gi;
257
+ let match = expression.exec(concatenated);
258
+ while (match !== null) {
259
+ const label = match[1].replace(/''/g, "'");
260
+ if (label.length > MAX_LABEL_LENGTH) {
261
+ const message = `CDS label is ${label.length} characters, maximum is ${MAX_LABEL_LENGTH}`;
262
+ issues.push(issue_1.Issue.atToken(file, annotation.getFirstToken(), message, this.getMetadata().key, this.conf.severity));
263
+ }
264
+ match = expression.exec(concatenated);
265
+ }
266
+ }
267
+ }
268
+ checkViewEntityAnnotations(tree, issues, file) {
269
+ const view = tree.findFirstExpression(expressions_1.CDSDefineView);
270
+ if (view === undefined || view.findDirectTokenByText("ENTITY") === undefined) {
271
+ return;
272
+ }
273
+ for (const annotation of tree.findAllExpressionsRecursive(expressions_1.CDSAnnotation)) {
274
+ const normalized = annotation.concatTokens().replace(/\s/g, "").toUpperCase().replace(/^@</, "@");
275
+ for (const forbidden of VIEW_ENTITY_FORBIDDEN_ANNOTATIONS) {
276
+ const upper = forbidden.toUpperCase();
277
+ if (normalized.startsWith("@" + upper) === false
278
+ && normalized.startsWith("@" + upper.replace(".", ":{")) === false) {
279
+ continue;
280
+ }
281
+ const message = `Annotation ${forbidden} is not allowed in view entities`;
282
+ issues.push(issue_1.Issue.atToken(file, annotation.getFirstToken(), message, this.getMetadata().key, this.conf.severity));
283
+ }
284
+ }
285
+ }
286
+ checkReservedNames(tree, issues, file) {
287
+ var _a, _b;
288
+ for (const element of tree.findAllExpressionsRecursive(expressions_1.CDSElement)) {
289
+ if (element.findDirectTokenByText("INCLUDE") !== undefined) {
290
+ continue;
291
+ }
292
+ let nameNode = (_a = element.findDirectExpression(expressions_1.CDSAs)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(expressions_1.CDSName);
293
+ if (nameNode === undefined) {
294
+ const names = ((_b = element.findDirectExpression(expressions_1.CDSPrefixedName)) === null || _b === void 0 ? void 0 : _b.findDirectExpressions(expressions_1.CDSName)) || [];
295
+ nameNode = names[names.length - 1];
296
+ }
297
+ if (nameNode === undefined) {
298
+ continue;
299
+ }
300
+ const name = this.name(nameNode);
301
+ if (RESERVED_ELEMENT_NAMES.includes(name.toUpperCase())) {
302
+ issues.push(issue_1.Issue.atToken(file, nameNode.getFirstToken(), `CDS element name "${name}" is reserved`, this.getMetadata().key, this.conf.severity));
303
+ }
304
+ }
305
+ }
242
306
  checkFields(tree, sources, object, issues, file) {
243
307
  var _a;
244
308
  const associationNames = new Set();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.120.27",
3
+ "version": "2.120.29",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",