@abaplint/core 2.120.10 → 2.120.11

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.
@@ -1257,6 +1257,7 @@ declare class ClassDefinition_3 extends Identifier implements IClassDefinition {
1257
1257
  isAbstract(): boolean;
1258
1258
  isSharedMemory(): boolean;
1259
1259
  getCreateVisibility(): Visibility;
1260
+ private checkClassNameLength;
1260
1261
  private findSuper;
1261
1262
  private checkMethodNameLength;
1262
1263
  private checkClassConstructorStatic;
@@ -7891,12 +7892,30 @@ declare class Table extends AbstractObject {
7891
7892
  getType(): string;
7892
7893
  getDescription(): string | undefined;
7893
7894
  getSecondaryIndexes(): SecondaryIndex[] | undefined;
7895
+ getFields(): {
7896
+ FIELDNAME: string;
7897
+ ROLLNAME?: string;
7898
+ COMPTYPE?: string;
7899
+ PRECFIELD?: string;
7900
+ LENG?: string;
7901
+ INTLEN?: string;
7902
+ DATATYPE?: string;
7903
+ DECIMALS?: string;
7904
+ KEYFLAG?: string;
7905
+ GROUPNAME?: string;
7906
+ CHECKTABLE?: string;
7907
+ REFTABLE?: string;
7908
+ REFFIELD?: string;
7909
+ REFTYPE?: string;
7910
+ DDTEXT?: string;
7911
+ }[] | undefined;
7894
7912
  getAllowedNaming(): IAllowedNaming;
7895
7913
  setDirty(): void;
7896
7914
  listKeys(reg: IRegistry): string[];
7897
7915
  parseType(reg: IRegistry): AbstractType;
7898
7916
  getTableCategory(): TableCategory | undefined;
7899
7917
  getEnhancementCategory(): EnhancementCategory;
7918
+ private keyFieldsNotFirst;
7900
7919
  private parseXML;
7901
7920
  }
7902
7921
 
@@ -16,7 +16,7 @@ class For extends combi_1.Expression {
16
16
  const itera = (0, combi_1.seq)(_1.InlineFieldDefinition, (0, combi_1.opt)(then), whil);
17
17
  const groupBy = (0, combi_1.seq)("GROUP BY", (0, combi_1.alt)(field_chain_1.FieldChain, (0, combi_1.seq)("(", (0, combi_1.plus)(_1.LoopGroupByComponent), ")")), (0, combi_1.opt)((0, combi_1.seq)((0, combi_1.alt)("ASCENDING", "DESCENDING"), (0, combi_1.opt)("AS TEXT"))), (0, combi_1.opt)("WITHOUT MEMBERS"));
18
18
  const t = (0, combi_1.alt)(_1.TargetField, _1.TargetFieldSymbol);
19
- const groups = (0, combi_1.ver)(version_1.Release.v740sp08, (0, combi_1.seq)("GROUPS", t, "OF", t, "IN", _1.Source, (0, combi_1.optPrio)(groupBy)));
19
+ const groups = (0, combi_1.ver)(version_1.Release.v740sp08, (0, combi_1.seq)("GROUPS", t, "OF", t, "IN", _1.Source, (0, combi_1.optPrio)(groupBy)), { also: combi_1.AlsoIn.OpenABAP });
20
20
  const f = (0, combi_1.seq)("FOR", (0, combi_1.alt)(itera, inn, groups), (0, combi_1.optPrio)(_1.Let));
21
21
  return (0, combi_1.ver)(version_1.Release.v740sp05, f, { also: combi_1.AlsoIn.OpenABAP });
22
22
  }
@@ -5,7 +5,8 @@ const combi_1 = require("../combi");
5
5
  const _1 = require(".");
6
6
  class InlineFieldDefinition extends combi_1.Expression {
7
7
  getRunnable() {
8
- return (0, combi_1.altPrio)((0, combi_1.seq)(_1.Field, "=", _1.Source), (0, combi_1.seq)(_1.Field, "TYPE", _1.TypeName));
8
+ const field = (0, combi_1.altPrio)(_1.Field, _1.FieldSymbol);
9
+ return (0, combi_1.altPrio)((0, combi_1.seq)(field, "=", _1.Source), (0, combi_1.seq)(_1.Field, "TYPE", _1.TypeName));
9
10
  }
10
11
  }
11
12
  exports.InlineFieldDefinition = InlineFieldDefinition;
@@ -7,7 +7,7 @@ const tokens_1 = require("../../1_lexer/tokens");
7
7
  const version_1 = require("../../../version");
8
8
  class ReduceNext extends combi_1.Expression {
9
9
  getRunnable() {
10
- const calcAssign = (0, combi_1.ver)(version_1.Release.v754, (0, combi_1.alt)((0, combi_1.seq)((0, combi_1.tok)(tokens_1.WPlus), "="), (0, combi_1.seq)((0, combi_1.tok)(tokens_1.WDash), "="), "/=", "*=", "&&="));
10
+ const calcAssign = (0, combi_1.ver)(version_1.Release.v754, (0, combi_1.alt)((0, combi_1.seq)((0, combi_1.tok)(tokens_1.WPlus), "="), (0, combi_1.seq)((0, combi_1.tok)(tokens_1.WDash), "="), "/=", "*=", "&&="), { also: combi_1.AlsoIn.OpenABAP });
11
11
  const fields = (0, combi_1.seq)(_1.SimpleTarget, (0, combi_1.altPrio)("=", calcAssign), _1.Source);
12
12
  return (0, combi_1.seq)("NEXT", (0, combi_1.plus)(fields));
13
13
  }
@@ -37,18 +37,30 @@ exports.ComponentCompare = void 0;
37
37
  const Expressions = __importStar(require("../../2_statements/expressions"));
38
38
  const _syntax_input_1 = require("../_syntax_input");
39
39
  const component_chain_1 = require("./component_chain");
40
+ const component_name_1 = require("./component_name");
40
41
  const source_1 = require("./source");
41
42
  class ComponentCompare {
42
- static runSyntax(node, input, type) {
43
+ static runSyntax(node, input, leftType, rightType) {
43
44
  const chain = node.findDirectExpression(Expressions.ComponentChainSimple);
44
45
  if (chain === undefined) {
45
46
  const message = "ComponentCompare, chain not found";
46
47
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
47
48
  return;
48
49
  }
49
- const fieldType = component_chain_1.ComponentChain.runSyntax(type, chain, input);
50
+ const fieldType = component_chain_1.ComponentChain.runSyntax(leftType, chain, input);
50
51
  for (const s of node.findDirectExpressions(Expressions.Source)) {
51
- source_1.Source.runSyntax(s, input, fieldType);
52
+ const fieldChain = s.findDirectExpression(Expressions.FieldChain);
53
+ const first = fieldChain === null || fieldChain === void 0 ? void 0 : fieldChain.getFirstChild();
54
+ if (rightType && fieldChain && first) {
55
+ let sourceType = rightType;
56
+ if (first.concatTokens().toUpperCase() !== "TABLE_LINE") {
57
+ sourceType = component_name_1.ComponentName.runSyntax(sourceType, first, input);
58
+ }
59
+ component_chain_1.ComponentChain.runSyntax(sourceType, fieldChain, input);
60
+ }
61
+ else {
62
+ source_1.Source.runSyntax(s, input, fieldType);
63
+ }
52
64
  }
53
65
  }
54
66
  }
@@ -37,15 +37,15 @@ exports.ComponentCond = void 0;
37
37
  const Expressions = __importStar(require("../../2_statements/expressions"));
38
38
  const component_compare_1 = require("./component_compare");
39
39
  class ComponentCond {
40
- static runSyntax(node, input, type) {
40
+ static runSyntax(node, input, leftType, rightType) {
41
41
  for (const t of node.findDirectExpressions(Expressions.ComponentCondSub)) {
42
42
  const c = t.findDirectExpression(Expressions.ComponentCond);
43
43
  if (c) {
44
- ComponentCond.runSyntax(c, input, type);
44
+ ComponentCond.runSyntax(c, input, leftType, rightType);
45
45
  }
46
46
  }
47
47
  for (const t of node.findDirectExpressions(Expressions.ComponentCompare)) {
48
- component_compare_1.ComponentCompare.runSyntax(t, input, type);
48
+ component_compare_1.ComponentCompare.runSyntax(t, input, leftType, rightType);
49
49
  }
50
50
  }
51
51
  }
@@ -43,21 +43,14 @@ class FilterBody {
43
43
  if (node === undefined) {
44
44
  return targetType;
45
45
  }
46
- let type = undefined;
46
+ const types = [];
47
47
  for (const s of node.findDirectExpressions(Expressions.Source)) {
48
- if (type === undefined) {
49
- type = source_1.Source.runSyntax(s, input);
50
- }
51
- else {
52
- source_1.Source.runSyntax(s, input);
53
- }
48
+ types.push(source_1.Source.runSyntax(s, input));
54
49
  }
55
- // todo
56
- if (node.findDirectTokenByText("EXCEPT") === undefined) {
57
- const rowType = type instanceof basic_1.TableType ? type.getRowType() : undefined;
58
- component_cond_1.ComponentCond.runSyntax(node.findDirectExpression(Expressions.ComponentCond), input, rowType);
59
- }
60
- return type ? type : targetType;
50
+ const inputRowType = types[0] instanceof basic_1.TableType ? types[0].getRowType() : undefined;
51
+ const filterRowType = types[1] instanceof basic_1.TableType ? types[1].getRowType() : undefined;
52
+ component_cond_1.ComponentCond.runSyntax(node.findDirectExpression(Expressions.ComponentCond), input, inputRowType, filterRowType);
53
+ return types[0] ? types[0] : targetType;
61
54
  }
62
55
  }
63
56
  exports.FilterBody = FilterBody;
@@ -46,7 +46,8 @@ class InlineFieldDefinition {
46
46
  static runSyntax(node, input, targetType) {
47
47
  var _a;
48
48
  let type = undefined;
49
- const field = (_a = node.findDirectExpression(Expressions.Field)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
49
+ const field = (_a = (node.findDirectExpression(Expressions.Field)
50
+ || node.findDirectExpression(Expressions.FieldSymbol))) === null || _a === void 0 ? void 0 : _a.getFirstToken();
50
51
  if (field === undefined) {
51
52
  return undefined;
52
53
  }
@@ -97,6 +97,7 @@ class ClassDefinition extends _identifier_1.Identifier {
97
97
  // perform checks after everything has been initialized
98
98
  this.checkInterfaceVisibility(input, node);
99
99
  this.checkMethodsFromSuperClasses(input);
100
+ this.checkClassNameLength(input);
100
101
  this.checkMethodNameLength(input);
101
102
  this.checkClassConstructorStatic(input);
102
103
  }
@@ -147,6 +148,12 @@ class ClassDefinition extends _identifier_1.Identifier {
147
148
  }
148
149
  */
149
150
  ///////////////////
151
+ checkClassNameLength(input) {
152
+ if (this.getName().length > 30) {
153
+ const message = `Class name "${this.getName()}" is too long, maximum length is 30 characters`;
154
+ input.issues.push((0, _syntax_input_1.syntaxIssue)(input, this.getToken(), message));
155
+ }
156
+ }
150
157
  findSuper(def, input) {
151
158
  var _a;
152
159
  const token = (_a = def === null || def === void 0 ? void 0 : def.findDirectExpression(expressions_1.SuperClassName)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DDLValueHelp = exports.DDLForeignKey = exports.DDLForeignKeyTarget = void 0;
3
+ exports.DDLReference = exports.DDLValueHelp = exports.DDLForeignKey = exports.DDLForeignKeyTarget = void 0;
4
4
  const combi_1 = require("../../abap/2_statements/combi");
5
5
  const ddl_literal_1 = require("./ddl_literal");
6
6
  const ddl_name_1 = require("./ddl_name");
@@ -24,4 +24,10 @@ class DDLValueHelp extends combi_1.Expression {
24
24
  }
25
25
  }
26
26
  exports.DDLValueHelp = DDLValueHelp;
27
+ class DDLReference extends combi_1.Expression {
28
+ getRunnable() {
29
+ return (0, combi_1.seq)("WITH", "REFERENCE", "TABLE", ddl_name_1.DDLName, "AND", "REFERENCE", "FIELD", ddl_name_1.DDLName);
30
+ }
31
+ }
32
+ exports.DDLReference = DDLReference;
27
33
  //# sourceMappingURL=ddl_clauses.js.map
@@ -8,7 +8,7 @@ const ddl_name_1 = require("./ddl_name");
8
8
  const ddl_type_1 = require("./ddl_type");
9
9
  class DDLTableField extends combi_1.Expression {
10
10
  getRunnable() {
11
- const trailingClause = (0, combi_1.alt)(ddl_clauses_1.DDLForeignKey, ddl_clauses_1.DDLValueHelp);
11
+ const trailingClause = (0, combi_1.alt)(ddl_clauses_1.DDLForeignKey, ddl_clauses_1.DDLReference, ddl_clauses_1.DDLValueHelp);
12
12
  return (0, combi_1.seq)((0, combi_1.star)(expressions_1.CDSAnnotation), (0, combi_1.optPrio)("KEY"), ddl_name_1.DDLName, ":", ddl_type_1.DDLType, (0, combi_1.optPrio)("NOT NULL"), (0, combi_1.star)(trailingClause), ";");
13
13
  }
14
14
  }
@@ -80,6 +80,13 @@ class Table extends _abstract_object_1.AbstractObject {
80
80
  }
81
81
  return (_a = this.parsedData) === null || _a === void 0 ? void 0 : _a.secondaryIndexes;
82
82
  }
83
+ getFields() {
84
+ var _a;
85
+ if (this.parsedData === undefined) {
86
+ this.parseXML();
87
+ }
88
+ return (_a = this.parsedData) === null || _a === void 0 ? void 0 : _a.fields;
89
+ }
83
90
  getAllowedNaming() {
84
91
  let length = 30;
85
92
  const regex = /^((\/[A-Z_\d]{3,8}\/)|[a-zA-Z0-9]{3}|CI_)\w+$/;
@@ -132,9 +139,13 @@ class Table extends _abstract_object_1.AbstractObject {
132
139
  && this.parsedData.dataClass === "USER3") {
133
140
  return new Types.UnknownType("Data class = USER3 not allowed in cloud");
134
141
  }
135
- if (this.getTableCategory() === TableCategory.Transparent
136
- && this.listKeys(reg).length === 0) {
137
- return new Types.UnknownType("Table " + this.getName() + " has no key fields");
142
+ if (this.getTableCategory() === TableCategory.Transparent) {
143
+ if (this.listKeys(reg).length === 0) {
144
+ return new Types.UnknownType("Table " + this.getName() + " has no key fields");
145
+ }
146
+ else if (this.keyFieldsNotFirst()) {
147
+ return new Types.UnknownType("Table " + this.getName() + " key fields must be first");
148
+ }
138
149
  }
139
150
  if (this.parsedType) {
140
151
  return this.parsedType;
@@ -310,6 +321,21 @@ class Table extends _abstract_object_1.AbstractObject {
310
321
  return this.parsedData.enhancementCategory;
311
322
  }
312
323
  ///////////////
324
+ keyFieldsNotFirst() {
325
+ var _a;
326
+ let seenNonKey = false;
327
+ for (const field of ((_a = this.parsedData) === null || _a === void 0 ? void 0 : _a.fields) || []) {
328
+ if (field.KEYFLAG === "X") {
329
+ if (seenNonKey === true) {
330
+ return true;
331
+ }
332
+ }
333
+ else {
334
+ seenNonKey = true;
335
+ }
336
+ }
337
+ return false;
338
+ }
313
339
  parseXML() {
314
340
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
315
341
  const parsed = super.parseRaw2();
@@ -346,6 +372,8 @@ class Table extends _abstract_object_1.AbstractObject {
346
372
  KEYFLAG: field.KEYFLAG,
347
373
  GROUPNAME: field.GROUPNAME,
348
374
  CHECKTABLE: field.CHECKTABLE,
375
+ REFTABLE: field.REFTABLE,
376
+ REFFIELD: field.REFFIELD,
349
377
  REFTYPE: field.REFTYPE,
350
378
  DDTEXT: field.DDTEXT,
351
379
  });
@@ -75,7 +75,7 @@ class Registry {
75
75
  }
76
76
  static abaplintVersion() {
77
77
  // magic, see build script "version.js"
78
- return "2.120.10";
78
+ return "2.120.11";
79
79
  }
80
80
  getDDICReferences() {
81
81
  return this.ddicReferences;
@@ -126,6 +126,7 @@ __exportStar(require("./no_comments_between_methods"), exports);
126
126
  __exportStar(require("./no_external_form_calls"), exports);
127
127
  __exportStar(require("./no_inline_in_optional_branches"), exports);
128
128
  __exportStar(require("./no_macros"), exports);
129
+ __exportStar(require("./no_mandt_in_database_operations"), exports);
129
130
  __exportStar(require("./no_prefixes"), exports);
130
131
  __exportStar(require("./no_public_attributes"), exports);
131
132
  __exportStar(require("./no_yoda_conditions"), exports);
@@ -0,0 +1,133 @@
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.NoMandtInDatabaseOperations = exports.NoMandtInDatabaseOperationsConf = void 0;
37
+ const Expressions = __importStar(require("../abap/2_statements/expressions"));
38
+ const Statements = __importStar(require("../abap/2_statements/statements"));
39
+ const issue_1 = require("../issue");
40
+ const _abap_rule_1 = require("./_abap_rule");
41
+ const _basic_rule_config_1 = require("./_basic_rule_config");
42
+ const _irule_1 = require("./_irule");
43
+ class NoMandtInDatabaseOperationsConf extends _basic_rule_config_1.BasicRuleConfig {
44
+ }
45
+ exports.NoMandtInDatabaseOperationsConf = NoMandtInDatabaseOperationsConf;
46
+ class NoMandtInDatabaseOperations extends _abap_rule_1.ABAPRule {
47
+ constructor() {
48
+ super(...arguments);
49
+ this.conf = new NoMandtInDatabaseOperationsConf();
50
+ }
51
+ getMetadata() {
52
+ return {
53
+ key: "no_mandt_in_database_operations",
54
+ title: "No MANDT in database operations",
55
+ shortDescription: "Do not specify the client in database operations; the ABAP runtime handles it automatically.",
56
+ extendedInformation: "Only check for the name MANDT, not for the field type. The rule does not check for dynamic SQL.",
57
+ tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Syntax],
58
+ badExample: `SELECT * FROM zcustomers
59
+ CLIENT SPECIFIED
60
+ WHERE mandt = @sy-mandt
61
+ INTO TABLE @DATA(customers).`,
62
+ goodExample: `SELECT * FROM zcustomers
63
+ INTO TABLE @DATA(customers).`,
64
+ };
65
+ }
66
+ getConfig() {
67
+ return this.conf;
68
+ }
69
+ setConfig(conf) {
70
+ this.conf = conf;
71
+ }
72
+ runParsed(file) {
73
+ const issues = [];
74
+ for (const statement of file.getStatements()) {
75
+ if (this.isDatabaseOperation(statement) === false) {
76
+ continue;
77
+ }
78
+ const mandt = this.findMandtInCondition(statement);
79
+ if (mandt !== undefined) {
80
+ const issue = issue_1.Issue.atToken(file, mandt.getFirstToken(), this.getMetadata().title, this.getMetadata().key, this.conf.severity);
81
+ issues.push(issue);
82
+ continue;
83
+ }
84
+ const explicitClient = this.findExplicitClient(statement);
85
+ if (explicitClient !== undefined) {
86
+ const issue = issue_1.Issue.atToken(file, explicitClient, this.getMetadata().title, this.getMetadata().key, this.conf.severity);
87
+ issues.push(issue);
88
+ }
89
+ }
90
+ return issues;
91
+ }
92
+ isDatabaseOperation(statement) {
93
+ const type = statement.get();
94
+ return type instanceof Statements.DeleteDatabase
95
+ || type instanceof Statements.InsertDatabase
96
+ || type instanceof Statements.MergeDatabase
97
+ || type instanceof Statements.ModifyDatabase
98
+ || type instanceof Statements.OpenCursor
99
+ || type instanceof Statements.Select
100
+ || type instanceof Statements.SelectLoop
101
+ || type instanceof Statements.UpdateDatabase
102
+ || type instanceof Statements.With
103
+ || type instanceof Statements.WithLoop;
104
+ }
105
+ findMandtInCondition(statement) {
106
+ for (const condition of statement.findAllExpressions(Expressions.SQLCond)) {
107
+ for (const field of condition.findAllExpressions(Expressions.SQLFieldName)) {
108
+ const name = field.concatTokens().toUpperCase();
109
+ if (name === "MANDT" || name.endsWith("~MANDT")) {
110
+ return field;
111
+ }
112
+ }
113
+ }
114
+ return undefined;
115
+ }
116
+ findExplicitClient(statement) {
117
+ var _a, _b;
118
+ const tokens = statement.getTokens();
119
+ for (let index = 0; index < tokens.length; index++) {
120
+ const current = tokens[index].getStr().toUpperCase();
121
+ const next = (_a = tokens[index + 1]) === null || _a === void 0 ? void 0 : _a.getStr().toUpperCase();
122
+ const afterNext = (_b = tokens[index + 2]) === null || _b === void 0 ? void 0 : _b.getStr().toUpperCase();
123
+ if ((current === "CLIENT" && next === "SPECIFIED")
124
+ || (current === "USING" && (next === "CLIENT" || next === "CLIENTS"))
125
+ || (current === "USING" && next === "ALL" && afterNext === "CLIENTS")) {
126
+ return current === "CLIENT" ? tokens[index] : tokens[index + (next === "ALL" ? 2 : 1)];
127
+ }
128
+ }
129
+ return undefined;
130
+ }
131
+ }
132
+ exports.NoMandtInDatabaseOperations = NoMandtInDatabaseOperations;
133
+ //# sourceMappingURL=no_mandt_in_database_operations.js.map
@@ -61,6 +61,7 @@ class XMLConsistency {
61
61
  extendedInformation: `Checks:
62
62
  * XML is well-formed and parseable
63
63
  * Naming for CLAS and INTF objects
64
+ * QUAN fields in TABL objects have reference table and field values
64
65
  * Texts and translations do not exceed maximum allowed length.`,
65
66
  tags: [_irule_1.RuleTag.Naming, _irule_1.RuleTag.Syntax],
66
67
  };
@@ -75,6 +76,7 @@ class XMLConsistency {
75
76
  return this;
76
77
  }
77
78
  run(obj) {
79
+ var _a;
78
80
  const issues = [];
79
81
  const file = obj.getXMLFile();
80
82
  if (file === undefined) {
@@ -86,6 +88,14 @@ class XMLConsistency {
86
88
  if (res !== true) {
87
89
  issues.push(issue_1.Issue.atRow(file, 1, "XML parser error: " + res.err.msg, this.getMetadata().key, this.conf.severity));
88
90
  }
91
+ else {
92
+ for (const attribute of ["version", "serializer_version"]) {
93
+ const value = (_a = xml.match(new RegExp(`<abapGit\\b[^>]*\\b${attribute}="([^"]+)"`))) === null || _a === void 0 ? void 0 : _a[1];
94
+ if (value !== undefined && value.match(/^v\d\.\d\.\d$/) === null) {
95
+ issues.push(issue_1.Issue.atRow(file, 1, `Unexpected abapGit ${attribute} "${value}"`, this.getMetadata().key, this.conf.severity));
96
+ }
97
+ }
98
+ }
89
99
  }
90
100
  // todo, have some XML validation in each object?
91
101
  if (obj instanceof Objects.Class) {
@@ -106,6 +116,9 @@ class XMLConsistency {
106
116
  else if (obj instanceof Objects.MessageClass) {
107
117
  issues.push(...this.runMessageClass(obj, file));
108
118
  }
119
+ else if (obj instanceof Objects.Table) {
120
+ issues.push(...this.runTable(obj, file));
121
+ }
109
122
  if (obj instanceof _abap_object_1.ABAPObject) {
110
123
  issues.push(...this.runTextPool(obj, file));
111
124
  }
@@ -249,6 +262,18 @@ class XMLConsistency {
249
262
  }
250
263
  return issues;
251
264
  }
265
+ runTable(obj, file) {
266
+ var _a, _b;
267
+ var _c;
268
+ const issues = [];
269
+ for (const field of (_c = obj.getFields()) !== null && _c !== void 0 ? _c : []) {
270
+ if (field.DATATYPE === "QUAN" && (!((_a = field.REFTABLE) === null || _a === void 0 ? void 0 : _a.trim()) || !((_b = field.REFFIELD) === null || _b === void 0 ? void 0 : _b.trim()))) {
271
+ const message = `QUAN field ${field.FIELDNAME} must have REFTABLE and REFFIELD set`;
272
+ issues.push(issue_1.Issue.atRow(file, 1, message, this.getMetadata().key, this.conf.severity));
273
+ }
274
+ }
275
+ return issues;
276
+ }
252
277
  }
253
278
  exports.XMLConsistency = XMLConsistency;
254
279
  //# sourceMappingURL=xml_consistency.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.120.10",
3
+ "version": "2.120.11",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",