@abaplint/core 2.120.44 → 2.120.46

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.
@@ -7966,6 +7966,8 @@ declare class Table extends AbstractObject {
7966
7966
  listKeys(reg: IRegistry): string[];
7967
7967
  parseType(reg: IRegistry): AbstractType;
7968
7968
  getTableCategory(): TableCategory | undefined;
7969
+ getDeliveryClass(): string | undefined;
7970
+ getSizeCategory(): string | undefined;
7969
7971
  getEnhancementCategory(): EnhancementCategory;
7970
7972
  private keyFieldsNotFirst;
7971
7973
  private parseXML;
@@ -131,6 +131,19 @@ class ValueBody {
131
131
  const message = "VALUE, source type CharacterType not compatible with StringType";
132
132
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, s.getFirstToken(), message));
133
133
  }
134
+ else if (this.insertsUsingTableKey(targetType)) {
135
+ // only STANDARD tables append the rows, and thus allow padding of shorter c and x values
136
+ const row = this.characterOrHexLength(rowType);
137
+ const source = this.characterOrHexLength(sourceType);
138
+ if (row !== undefined
139
+ && source !== undefined
140
+ && row.abap === source.abap
141
+ && row.length !== source.length) {
142
+ const message = `VALUE, source type ${source.abap} LENGTH ${source.length} not compatible ` +
143
+ `with row type ${row.abap} LENGTH ${row.length}`;
144
+ input.issues.push((0, _syntax_input_1.syntaxIssue)(input, s.getFirstToken(), message));
145
+ }
146
+ }
134
147
  }
135
148
  }
136
149
  if (letScoped === true) {
@@ -144,6 +157,25 @@ class ValueBody {
144
157
  }
145
158
  return targetType ? targetType : type;
146
159
  }
160
+ /** SORTED and HASHED tables insert the constructed rows using the table key, which
161
+ * requires the rows to be compatible with the row type. STANDARD tables only append
162
+ * the rows, so shorter c and x values are padded on the right instead. */
163
+ static insertsUsingTableKey(targetType) {
164
+ if (!(targetType instanceof basic_1.TableType)) {
165
+ return false;
166
+ }
167
+ const accessType = targetType.getAccessType();
168
+ return accessType === basic_1.TableAccessType.sorted || accessType === basic_1.TableAccessType.hashed;
169
+ }
170
+ static characterOrHexLength(type) {
171
+ if (type instanceof basic_1.CharacterType) {
172
+ return { abap: "c", length: type.getLength() };
173
+ }
174
+ else if (type instanceof basic_1.HexType) {
175
+ return { abap: "x", length: type.getLength() };
176
+ }
177
+ return undefined;
178
+ }
147
179
  }
148
180
  exports.ValueBody = ValueBody;
149
181
  //# sourceMappingURL=value_body.js.map
@@ -310,6 +310,20 @@ class Table extends _abstract_object_1.AbstractObject {
310
310
  }
311
311
  return (_a = this.parsedData) === null || _a === void 0 ? void 0 : _a.tableCategory;
312
312
  }
313
+ getDeliveryClass() {
314
+ var _a;
315
+ if (this.parsedData === undefined) {
316
+ this.parseXML();
317
+ }
318
+ return (_a = this.parsedData) === null || _a === void 0 ? void 0 : _a.deliveryClass;
319
+ }
320
+ getSizeCategory() {
321
+ var _a;
322
+ if (this.parsedData === undefined) {
323
+ this.parseXML();
324
+ }
325
+ return (_a = this.parsedData) === null || _a === void 0 ? void 0 : _a.sizeCategory;
326
+ }
313
327
  getEnhancementCategory() {
314
328
  var _a;
315
329
  if (this.parsedData === undefined) {
@@ -337,7 +351,7 @@ class Table extends _abstract_object_1.AbstractObject {
337
351
  return false;
338
352
  }
339
353
  parseXML() {
340
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4;
354
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12;
341
355
  const parsed = super.parseRaw2();
342
356
  if (parsed === undefined) {
343
357
  return;
@@ -357,8 +371,10 @@ class Table extends _abstract_object_1.AbstractObject {
357
371
  this.parsedData.tableCategory = (_m = (_l = (_k = (_j = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _j === void 0 ? void 0 : _j["asx:abap"]) === null || _k === void 0 ? void 0 : _k["asx:values"]) === null || _l === void 0 ? void 0 : _l.DD02V) === null || _m === void 0 ? void 0 : _m.TABCLASS;
358
372
  this.parsedData.description = (_r = (_q = (_p = (_o = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _o === void 0 ? void 0 : _o["asx:abap"]) === null || _p === void 0 ? void 0 : _p["asx:values"]) === null || _q === void 0 ? void 0 : _q.DD02V) === null || _r === void 0 ? void 0 : _r.DDTEXT;
359
373
  this.parsedData.dataClass = (_v = (_u = (_t = (_s = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _s === void 0 ? void 0 : _s["asx:abap"]) === null || _t === void 0 ? void 0 : _t["asx:values"]) === null || _u === void 0 ? void 0 : _u.DD09L) === null || _v === void 0 ? void 0 : _v.TABART;
374
+ this.parsedData.deliveryClass = (_z = (_y = (_x = (_w = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _w === void 0 ? void 0 : _w["asx:abap"]) === null || _x === void 0 ? void 0 : _x["asx:values"]) === null || _y === void 0 ? void 0 : _y.DD02V) === null || _z === void 0 ? void 0 : _z.CONTFLAG;
375
+ this.parsedData.sizeCategory = (_3 = (_2 = (_1 = (_0 = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _0 === void 0 ? void 0 : _0["asx:abap"]) === null || _1 === void 0 ? void 0 : _1["asx:values"]) === null || _2 === void 0 ? void 0 : _2.DD09L) === null || _3 === void 0 ? void 0 : _3.TABKAT;
360
376
  // fields
361
- const fields = (_y = (_x = (_w = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _w === void 0 ? void 0 : _w["asx:abap"]) === null || _x === void 0 ? void 0 : _x["asx:values"]) === null || _y === void 0 ? void 0 : _y.DD03P_TABLE;
377
+ const fields = (_6 = (_5 = (_4 = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _4 === void 0 ? void 0 : _4["asx:abap"]) === null || _5 === void 0 ? void 0 : _5["asx:values"]) === null || _6 === void 0 ? void 0 : _6.DD03P_TABLE;
362
378
  for (const field of (0, xml_utils_1.xmlToArray)(fields === null || fields === void 0 ? void 0 : fields.DD03P)) {
363
379
  if (typeof (field === null || field === void 0 ? void 0 : field.FIELDNAME) !== "string") {
364
380
  continue;
@@ -382,13 +398,13 @@ class Table extends _abstract_object_1.AbstractObject {
382
398
  });
383
399
  }
384
400
  // secondary indexes
385
- const indexes = (_1 = (_0 = (_z = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _z === void 0 ? void 0 : _z["asx:abap"]) === null || _0 === void 0 ? void 0 : _0["asx:values"]) === null || _1 === void 0 ? void 0 : _1.DD12V;
401
+ const indexes = (_9 = (_8 = (_7 = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _7 === void 0 ? void 0 : _7["asx:abap"]) === null || _8 === void 0 ? void 0 : _8["asx:values"]) === null || _9 === void 0 ? void 0 : _9.DD12V;
386
402
  ;
387
403
  this.parsedData.secondaryIndexes = [];
388
404
  for (const index of (0, xml_utils_1.xmlToArray)(indexes === null || indexes === void 0 ? void 0 : indexes.DD12V)) {
389
405
  const indexName = index.INDEXNAME;
390
406
  const indexFields = [];
391
- const indexFieldsXml = (_4 = (_3 = (_2 = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _2 === void 0 ? void 0 : _2["asx:abap"]) === null || _3 === void 0 ? void 0 : _3["asx:values"]) === null || _4 === void 0 ? void 0 : _4.DD17V;
407
+ const indexFieldsXml = (_12 = (_11 = (_10 = parsed === null || parsed === void 0 ? void 0 : parsed.abapGit) === null || _10 === void 0 ? void 0 : _10["asx:abap"]) === null || _11 === void 0 ? void 0 : _11["asx:values"]) === null || _12 === void 0 ? void 0 : _12.DD17V;
392
408
  for (const indexField of (0, xml_utils_1.xmlToArray)(indexFieldsXml === null || indexFieldsXml === void 0 ? void 0 : indexFieldsXml.DD17V)) {
393
409
  if (indexField.INDEXNAME === indexName) {
394
410
  // assumption: fields are listed by POSITION in the xml
@@ -75,7 +75,7 @@ class Registry {
75
75
  }
76
76
  static abaplintVersion() {
77
77
  // magic, see build script "version.js"
78
- return "2.120.44";
78
+ return "2.120.46";
79
79
  }
80
80
  getDDICReferences() {
81
81
  return this.ddicReferences;
@@ -17,7 +17,7 @@ class AllowedObjectNaming {
17
17
  key: "allowed_object_naming",
18
18
  title: "Allowed object naming",
19
19
  shortDescription: `Enforces basic name length and namespace restrictions, see note SAP 104010`,
20
- tags: [_irule_1.RuleTag.Naming, _irule_1.RuleTag.SingleFile],
20
+ tags: [_irule_1.RuleTag.Naming, _irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Syntax],
21
21
  };
22
22
  }
23
23
  initialize(_reg) {
@@ -409,6 +409,10 @@ Make sure to test the downported code, it might not always be completely correct
409
409
  if (found) {
410
410
  return found;
411
411
  }
412
+ found = this.outlineTableExpressionKeyBuiltin(low, high, lowFile, highSyntax);
413
+ if (found) {
414
+ return found;
415
+ }
412
416
  found = this.assignWithTable(low, high, lowFile);
413
417
  if (found) {
414
418
  return found;
@@ -1106,6 +1110,69 @@ ${indentation}`);
1106
1110
  }
1107
1111
  return condition;
1108
1112
  }
1113
+ /** A built-in function is only recognized as one in an operand position that admits an
1114
+ * expression, and "WITH KEY comp = ..." does not become such a position until v740. The
1115
+ * table expression rewrites carry the key operand over verbatim, so
1116
+ * "tab[ comp = to_upper( x ) ]" downports to a READ TABLE that a 702 or 731 compiler can
1117
+ * only read as a functional method call - "method TO_UPPER is unknown", reported as a
1118
+ * SYNTAX_ERROR of the whole class pool. Hoist the call into a variable first, an
1119
+ * assignment IS an expression position at 702.
1120
+ *
1121
+ * Built-ins that predate 702 (lines, strlen, numofchar, ...) carry no release in
1122
+ * BuiltIn.methods; they are accepted in the operand as they are and stay untouched. So do
1123
+ * functional method calls, which are exactly the reading that makes the built-in fail, and
1124
+ * the INDEX operand of "tab[ 1 ]", which is not a key operand. */
1125
+ outlineTableExpressionKeyBuiltin(low, high, lowFile, highSyntax) {
1126
+ if (!(low.get() instanceof _statement_1.Unknown)) {
1127
+ return undefined;
1128
+ }
1129
+ for (const tableExpression of high.findAllExpressionsRecursive(Expressions.TableExpression)) {
1130
+ let afterComponent = false;
1131
+ for (const child of tableExpression.getChildren()) {
1132
+ if (child.get() instanceof Expressions.ComponentChainSimple) {
1133
+ afterComponent = true;
1134
+ continue;
1135
+ }
1136
+ else if (afterComponent === false
1137
+ || !(child.get() instanceof Expressions.Source)
1138
+ || !(child instanceof nodes_1.ExpressionNode)) {
1139
+ continue;
1140
+ }
1141
+ const type = this.builtinReturnType(child, lowFile, highSyntax);
1142
+ if (type === undefined) {
1143
+ continue;
1144
+ }
1145
+ const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
1146
+ const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
1147
+ const code = `DATA ${uniqueName} TYPE ${type}.\n` +
1148
+ indentation + `${uniqueName} = ${child.concatTokens()}.\n` +
1149
+ indentation;
1150
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getFirstToken().getStart(), code);
1151
+ const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, child.getFirstToken().getStart(), child.getLastToken().getEnd(), uniqueName);
1152
+ const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
1153
+ return issue_1.Issue.atToken(lowFile, child.getFirstToken(), "Outline built-in function in table expression key", this.getMetadata().key, this.conf.severity, fix);
1154
+ }
1155
+ }
1156
+ return undefined;
1157
+ }
1158
+ /** the ABAP type returned by the built-in function this Source starts with, or undefined
1159
+ * if it does not start with one, or with one that predates 702 */
1160
+ builtinReturnType(source, lowFile, highSyntax) {
1161
+ var _a, _b, _c;
1162
+ const spag = highSyntax.spaghetti.lookupPosition(source.getFirstToken().getStart(), lowFile.getFilename());
1163
+ for (const r of (spag === null || spag === void 0 ? void 0 : spag.getData().references) || []) {
1164
+ if (r.referenceType !== _reference_1.ReferenceType.BuiltinMethodReference
1165
+ || r.position.getStart().equals(source.getFirstToken().getStart()) === false) {
1166
+ continue;
1167
+ }
1168
+ const name = r.position.getName().toUpperCase();
1169
+ if (((_a = _builtin_1.BuiltIn.methods[name]) === null || _a === void 0 ? void 0 : _a.release) === undefined) {
1170
+ return undefined;
1171
+ }
1172
+ return (_c = (_b = _builtin_1.BuiltIn.searchBuiltin(name)) === null || _b === void 0 ? void 0 : _b.getParameters().getReturning()) === null || _c === void 0 ? void 0 : _c.getType().toABAP();
1173
+ }
1174
+ return undefined;
1175
+ }
1109
1176
  outlineCatchSimple(node, lowFile) {
1110
1177
  // outlines "CATCH cx_bcs INTO DATA(lx_bcs_excep).", note that this does not need to look at types
1111
1178
  var _a, _b;
@@ -2716,9 +2783,13 @@ ${indentation} output = ${uniqueName}.\n`;
2716
2783
  continue;
2717
2784
  }
2718
2785
  const uniqueName = this.uniqueName(i.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
2719
- const type = this.findType(i, lowFile, highSyntax);
2720
2786
  const indent = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
2721
- const abap = `DATA ${uniqueName} TYPE ${type}.\n` +
2787
+ // the CONV type might just be a component of the row type, so prefer the row type
2788
+ const rowOf = this.findRowTypeOf(high, i);
2789
+ const declaration = rowOf === undefined
2790
+ ? `DATA ${uniqueName} TYPE ${this.findType(i, lowFile, highSyntax)}.`
2791
+ : `DATA ${uniqueName} LIKE LINE OF ${rowOf}.`;
2792
+ const abap = declaration + "\n" +
2722
2793
  indent + `${uniqueName} = ${body}.\n` +
2723
2794
  indent;
2724
2795
  const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getFirstToken().getStart(), abap);
@@ -2728,6 +2799,19 @@ ${indentation} output = ${uniqueName}.\n`;
2728
2799
  }
2729
2800
  return undefined;
2730
2801
  }
2802
+ /** if the source expression is the complete source of an INSERT or APPEND statement, then the
2803
+ * outlined variable must be typed via the row type of the table, returns the table name */
2804
+ findRowTypeOf(high, source) {
2805
+ var _a;
2806
+ if (!(high.get() instanceof Statements.InsertInternal)
2807
+ && !(high.get() instanceof Statements.Append)) {
2808
+ return undefined;
2809
+ }
2810
+ else if (high.getChildren()[1] !== source) {
2811
+ return undefined;
2812
+ }
2813
+ return (_a = high.findDirectExpression(Expressions.Target)) === null || _a === void 0 ? void 0 : _a.concatTokens();
2814
+ }
2731
2815
  // "CAST" to "?="
2732
2816
  outlineCast(low, high, lowFile, highSyntax) {
2733
2817
  var _a;
@@ -4,6 +4,7 @@ exports.LineBreakStyle = exports.LineBreakStyleConf = void 0;
4
4
  const issue_1 = require("../issue");
5
5
  const _basic_rule_config_1 = require("./_basic_rule_config");
6
6
  const _irule_1 = require("./_irule");
7
+ const objects_1 = require("../objects");
7
8
  class LineBreakStyleConf extends _basic_rule_config_1.BasicRuleConfig {
8
9
  }
9
10
  exports.LineBreakStyleConf = LineBreakStyleConf;
@@ -14,10 +15,11 @@ class LineBreakStyle {
14
15
  getMetadata() {
15
16
  return {
16
17
  key: "line_break_style",
17
- title: "Makes sure line breaks are consistent in the ABAP code",
18
- shortDescription: `Enforces LF as newlines in ABAP files
18
+ title: "Makes sure line breaks are consistent",
19
+ shortDescription: `Enforces LF as newlines in ABAP and XML files
19
20
 
20
21
  abapGit does not work with CRLF`,
22
+ extendedInformation: `SMIM and W3MI files are not checked.`,
21
23
  tags: [_irule_1.RuleTag.Whitespace, _irule_1.RuleTag.SingleFile],
22
24
  };
23
25
  }
@@ -32,8 +34,15 @@ abapGit does not work with CRLF`,
32
34
  }
33
35
  run(obj) {
34
36
  const output = [];
37
+ if (obj instanceof objects_1.MIMEObject || obj instanceof objects_1.WebMIME) {
38
+ return output;
39
+ }
35
40
  for (const file of obj.getFiles()) {
36
- if (file.getFilename().endsWith(".abap")) {
41
+ const filename = file.getFilename();
42
+ if (filename.endsWith(".abap") || filename.endsWith(".xml")) {
43
+ if (filename.endsWith(".xslt.source.xml")) {
44
+ continue;
45
+ }
37
46
  const rows = file.getRawRows();
38
47
  for (let i = 0; i < rows.length; i++) {
39
48
  if (rows[i].endsWith("\r") === true) {
@@ -64,6 +64,7 @@ class XMLConsistency {
64
64
  * XML is well-formed and parseable
65
65
  * Naming for CLAS and INTF objects
66
66
  * QUAN fields in TABL objects have reference table and field values
67
+ * Transparent TABL objects have delivery class and size category set
67
68
  * Lock parameter names in ENQU objects are max 16 characters
68
69
  * Texts and translations do not exceed maximum allowed length.`,
69
70
  tags: [_irule_1.RuleTag.Naming, _irule_1.RuleTag.Syntax],
@@ -322,15 +323,25 @@ class XMLConsistency {
322
323
  return issues;
323
324
  }
324
325
  runTable(obj, file) {
325
- var _a, _b;
326
- var _c;
326
+ var _a, _b, _c, _d;
327
+ var _e;
327
328
  const issues = [];
328
- for (const field of (_c = obj.getFields()) !== null && _c !== void 0 ? _c : []) {
329
+ for (const field of (_e = obj.getFields()) !== null && _e !== void 0 ? _e : []) {
329
330
  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()))) {
330
331
  const message = `QUAN field ${field.FIELDNAME} must have REFTABLE and REFFIELD set`;
331
332
  issues.push(issue_1.Issue.atRow(file, 1, message, this.getMetadata().key, this.conf.severity));
332
333
  }
333
334
  }
335
+ if (obj.getTableCategory() === Objects.TableCategory.Transparent) {
336
+ if (!((_c = obj.getDeliveryClass()) === null || _c === void 0 ? void 0 : _c.trim())) {
337
+ const message = `Transparent table must have delivery class(CONTFLAG in DD02V) set`;
338
+ issues.push(issue_1.Issue.atRow(file, 1, message, this.getMetadata().key, this.conf.severity));
339
+ }
340
+ if (!((_d = obj.getSizeCategory()) === null || _d === void 0 ? void 0 : _d.trim())) {
341
+ const message = `Transparent table must have size category(TABKAT in DD09L) set`;
342
+ issues.push(issue_1.Issue.atRow(file, 1, message, this.getMetadata().key, this.conf.severity));
343
+ }
344
+ }
334
345
  return issues;
335
346
  }
336
347
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.120.44",
3
+ "version": "2.120.46",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",