@abaplint/cli 2.120.26 → 2.120.27

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.
Files changed (2) hide show
  1. package/build/cli.js +333 -1
  2. package/package.json +2 -2
package/build/cli.js CHANGED
@@ -62507,6 +62507,7 @@ const _abstract_object_1 = __webpack_require__(/*! ./_abstract_object */ "../cor
62507
62507
  const ddic_1 = __webpack_require__(/*! ../ddic */ "../core/build/src/ddic.js");
62508
62508
  const Types = __importStar(__webpack_require__(/*! ../abap/types/basic */ "../core/build/src/abap/types/basic/index.js"));
62509
62509
  const xml_utils_1 = __webpack_require__(/*! ../xml_utils */ "../core/build/src/xml_utils.js");
62510
+ const domain_1 = __webpack_require__(/*! ./domain */ "../core/build/src/objects/domain.js");
62510
62511
  class DataElement extends _abstract_object_1.AbstractObject {
62511
62512
  constructor() {
62512
62513
  super(...arguments);
@@ -62537,6 +62538,22 @@ class DataElement extends _abstract_object_1.AbstractObject {
62537
62538
  this.parse();
62538
62539
  return (_a = this.parsedXML) === null || _a === void 0 ? void 0 : _a.domname;
62539
62540
  }
62541
+ /** parseType() cannot distinguish DEC, CURR, and QUAN because they all become PackedType.
62542
+ * Return the original DDIC datatype instead, resolving domain-backed data elements when possible. */
62543
+ getDataType(reg) {
62544
+ var _a, _b;
62545
+ this.parse();
62546
+ if ((_a = this.parsedXML) === null || _a === void 0 ? void 0 : _a.datatype) {
62547
+ return this.parsedXML.datatype;
62548
+ }
62549
+ if (reg && ((_b = this.parsedXML) === null || _b === void 0 ? void 0 : _b.refkind) === "D" && this.parsedXML.domname) {
62550
+ const domain = reg.getObject("DOMA", this.parsedXML.domname);
62551
+ if (domain instanceof domain_1.Domain) {
62552
+ return domain.getDataType();
62553
+ }
62554
+ }
62555
+ return undefined;
62556
+ }
62540
62557
  getTexts() {
62541
62558
  var _a;
62542
62559
  this.parse();
@@ -62820,6 +62837,11 @@ class Domain extends _abstract_object_1.AbstractObject {
62820
62837
  var _a;
62821
62838
  return (_a = this.parsedXML) === null || _a === void 0 ? void 0 : _a.conversionExit;
62822
62839
  }
62840
+ getDataType() {
62841
+ var _a;
62842
+ this.parse();
62843
+ return (_a = this.parsedXML) === null || _a === void 0 ? void 0 : _a.datatype;
62844
+ }
62823
62845
  getAllowedNaming() {
62824
62846
  return {
62825
62847
  maxLength: 30,
@@ -69751,7 +69773,7 @@ class Registry {
69751
69773
  }
69752
69774
  static abaplintVersion() {
69753
69775
  // magic, see build script "version.js"
69754
- return "2.120.26";
69776
+ return "2.120.27";
69755
69777
  }
69756
69778
  getDDICReferences() {
69757
69779
  return this.ddicReferences;
@@ -72415,6 +72437,315 @@ exports.CDSAssociationName = CDSAssociationName;
72415
72437
 
72416
72438
  /***/ },
72417
72439
 
72440
+ /***/ "../core/build/src/rules/cds_check_syntax.js"
72441
+ /*!***************************************************!*\
72442
+ !*** ../core/build/src/rules/cds_check_syntax.js ***!
72443
+ \***************************************************/
72444
+ (__unused_webpack_module, exports, __webpack_require__) {
72445
+
72446
+ "use strict";
72447
+
72448
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
72449
+ exports.CDSCheckSyntax = exports.CDSCheckSyntaxConf = void 0;
72450
+ const nodes_1 = __webpack_require__(/*! ../abap/nodes */ "../core/build/src/abap/nodes/index.js");
72451
+ const basic_1 = __webpack_require__(/*! ../abap/types/basic */ "../core/build/src/abap/types/basic/index.js");
72452
+ const expressions_1 = __webpack_require__(/*! ../cds/expressions */ "../core/build/src/cds/expressions/index.js");
72453
+ const ddic_1 = __webpack_require__(/*! ../ddic */ "../core/build/src/ddic.js");
72454
+ const issue_1 = __webpack_require__(/*! ../issue */ "../core/build/src/issue.js");
72455
+ const objects_1 = __webpack_require__(/*! ../objects */ "../core/build/src/objects/index.js");
72456
+ const _basic_rule_config_1 = __webpack_require__(/*! ./_basic_rule_config */ "../core/build/src/rules/_basic_rule_config.js");
72457
+ const _irule_1 = __webpack_require__(/*! ./_irule */ "../core/build/src/rules/_irule.js");
72458
+ class CDSCheckSyntaxConf extends _basic_rule_config_1.BasicRuleConfig {
72459
+ }
72460
+ exports.CDSCheckSyntaxConf = CDSCheckSyntaxConf;
72461
+ const KNOWN_QUAN_DATA_ELEMENTS = ["MENGE_D"];
72462
+ const KNOWN_CURR_DATA_ELEMENTS = ["BWERT", "DZWERT"];
72463
+ class CDSCheckSyntax {
72464
+ constructor() {
72465
+ this.conf = new CDSCheckSyntaxConf();
72466
+ }
72467
+ getMetadata() {
72468
+ return {
72469
+ key: "cds_check_syntax",
72470
+ title: "CDS Check Syntax",
72471
+ shortDescription: "Checks CDS source, field, type, and semantic annotation references",
72472
+ extendedInformation: `Performs semantic checks which require other DDIC objects to be available.
72473
+
72474
+ Missing objects are only reported when their names match the configured errorNamespace.`,
72475
+ tags: [_irule_1.RuleTag.Syntax],
72476
+ };
72477
+ }
72478
+ getConfig() {
72479
+ return this.conf;
72480
+ }
72481
+ setConfig(conf) {
72482
+ this.conf = conf;
72483
+ }
72484
+ initialize(reg) {
72485
+ this.reg = reg;
72486
+ return this;
72487
+ }
72488
+ run(object) {
72489
+ if (!(object instanceof objects_1.DataDefinition)) {
72490
+ return [];
72491
+ }
72492
+ const tree = object.getTree();
72493
+ const file = object.findSourceFile();
72494
+ if (tree === undefined || file === undefined) {
72495
+ return [];
72496
+ }
72497
+ const issues = [];
72498
+ const ddic = new ddic_1.DDIC(this.reg);
72499
+ const sources = this.findSources(tree, ddic, issues, file);
72500
+ this.checkRelations(tree, ddic, issues, file);
72501
+ this.checkTypes(tree, ddic, issues, file);
72502
+ this.checkRootProjection(tree, ddic, issues, file);
72503
+ this.checkFieldAnnotations(tree, ddic, issues, file);
72504
+ this.checkFields(tree, sources, object, issues, file);
72505
+ return issues;
72506
+ }
72507
+ findSources(tree, ddic, issues, file) {
72508
+ var _a;
72509
+ const sources = [];
72510
+ for (const node of tree.findAllExpressionsRecursive(expressions_1.CDSSource)) {
72511
+ const prefixed = node.findDirectExpression(expressions_1.CDSPrefixedName);
72512
+ const entityNode = prefixed === null || prefixed === void 0 ? void 0 : prefixed.findDirectExpression(expressions_1.CDSName);
72513
+ if (entityNode === undefined) {
72514
+ continue;
72515
+ }
72516
+ const entity = this.name(entityNode);
72517
+ const asName = (_a = node.findDirectExpression(expressions_1.CDSAs)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(expressions_1.CDSName);
72518
+ const bareAlias = node.findDirectExpression(expressions_1.CDSName);
72519
+ const alias = asName === undefined ? bareAlias : asName;
72520
+ const lookup = ddic.lookupTableOrView(entity);
72521
+ if (lookup.type instanceof basic_1.UnknownType && lookup.object === undefined && this.reg.inErrorNamespace(entity)) {
72522
+ issues.push(issue_1.Issue.atToken(file, entityNode.getFirstToken(), `CDS source "${entity}" not found`, this.getMetadata().key, this.conf.severity));
72523
+ }
72524
+ if (lookup.type instanceof basic_1.StructureType || lookup.type instanceof basic_1.UnknownType || lookup.type instanceof basic_1.VoidType) {
72525
+ const names = [entity.toUpperCase()];
72526
+ if (alias) {
72527
+ names.push(this.name(alias).toUpperCase());
72528
+ }
72529
+ sources.push({ entity, names, type: lookup.type });
72530
+ }
72531
+ }
72532
+ return sources;
72533
+ }
72534
+ checkRelations(tree, ddic, issues, file) {
72535
+ for (const relation of tree.findAllExpressionsRecursive(expressions_1.CDSRelation)) {
72536
+ const directTokens = relation.getDirectTokens();
72537
+ if (directTokens.length === 0) {
72538
+ continue;
72539
+ }
72540
+ const entity = directTokens.map(t => t.getStr()).join("");
72541
+ const lookup = ddic.lookupTableOrView(entity);
72542
+ if (lookup.type instanceof basic_1.UnknownType && lookup.object === undefined && this.reg.inErrorNamespace(entity)) {
72543
+ issues.push(issue_1.Issue.atToken(file, directTokens[0], `CDS source "${entity}" not found`, this.getMetadata().key, this.conf.severity));
72544
+ }
72545
+ }
72546
+ }
72547
+ checkTypes(tree, ddic, issues, file) {
72548
+ for (const typeNode of tree.findAllExpressionsRecursive(expressions_1.CDSType)) {
72549
+ const typeName = typeNode.findDirectExpressions(expressions_1.CDSName).map(n => this.name(n)).join(".");
72550
+ if (typeName === "" || typeName.toUpperCase().startsWith("ABAP.")) {
72551
+ continue;
72552
+ }
72553
+ const lookup = ddic.lookup(typeName);
72554
+ if (lookup.type instanceof basic_1.UnknownType && lookup.object === undefined && this.reg.inErrorNamespace(typeName)) {
72555
+ issues.push(issue_1.Issue.atToken(file, typeNode.getFirstToken(), `CDS type "${typeName}" not found`, this.getMetadata().key, this.conf.severity));
72556
+ }
72557
+ }
72558
+ }
72559
+ checkRootProjection(tree, ddic, issues, file) {
72560
+ var _a;
72561
+ const projection = tree.findFirstExpression(expressions_1.CDSDefineProjection);
72562
+ const rootToken = projection === null || projection === void 0 ? void 0 : projection.findDirectTokenByText("ROOT");
72563
+ if (projection === undefined || rootToken === undefined) {
72564
+ return;
72565
+ }
72566
+ const names = projection.findDirectExpressions(expressions_1.CDSName);
72567
+ const projectedEntityNode = names[1];
72568
+ if (projectedEntityNode === undefined) {
72569
+ return;
72570
+ }
72571
+ const projectedEntityName = this.name(projectedEntityNode);
72572
+ const projectedEntity = ddic.lookupTableOrView(projectedEntityName).object;
72573
+ if (projectedEntity instanceof objects_1.DataDefinition
72574
+ && ((_a = projectedEntity.getTree()) === null || _a === void 0 ? void 0 : _a.findDirectTokenByText("ROOT")) === undefined) {
72575
+ const message = `ROOT keyword not valid since ${projectedEntityName} is not a root property`;
72576
+ issues.push(issue_1.Issue.atToken(file, rootToken, message, this.getMetadata().key, this.conf.severity));
72577
+ }
72578
+ }
72579
+ checkFieldAnnotations(tree, ddic, issues, file) {
72580
+ const definition = tree.findFirstExpression(expressions_1.CDSDefineAbstract);
72581
+ if (definition === undefined) {
72582
+ return;
72583
+ }
72584
+ let definitionNameSeen = false;
72585
+ let fieldName;
72586
+ let annotations = [];
72587
+ const fields = [];
72588
+ for (const child of definition.getChildren()) {
72589
+ if (child instanceof nodes_1.TokenNode) {
72590
+ if (child.get().getStr() === ";") {
72591
+ fieldName = undefined;
72592
+ annotations = [];
72593
+ }
72594
+ continue;
72595
+ }
72596
+ else if (child.get() instanceof expressions_1.CDSAnnotation) {
72597
+ annotations.push(child);
72598
+ }
72599
+ else if (child.get() instanceof expressions_1.CDSName) {
72600
+ if (definitionNameSeen === false) {
72601
+ definitionNameSeen = true;
72602
+ annotations = [];
72603
+ }
72604
+ else {
72605
+ fieldName = child;
72606
+ }
72607
+ }
72608
+ else if (child.get() instanceof expressions_1.CDSType && fieldName !== undefined) {
72609
+ const semanticType = this.determineSemanticType(child, ddic);
72610
+ const normalizedAnnotations = annotations.map(a => a.concatTokens().replace(/\s/g, ""));
72611
+ fields.push({ name: this.name(fieldName), node: fieldName, annotations: normalizedAnnotations, semanticType });
72612
+ fieldName = undefined;
72613
+ annotations = [];
72614
+ }
72615
+ }
72616
+ const reportedCompanionFields = new Set();
72617
+ for (const field of fields) {
72618
+ if (field.semanticType === undefined) {
72619
+ continue;
72620
+ }
72621
+ const quantity = field.semanticType === "QUAN";
72622
+ const referenceAnnotation = quantity ? "@Semantics.quantity.unitOfMeasure" : "@Semantics.amount.currencyCode";
72623
+ const companionAnnotation = quantity ? "@Semantics.unitOfMeasure: true" : "@Semantics.currencyCode: true";
72624
+ const reference = this.findSemanticReference(field.annotations, field.semanticType);
72625
+ if (reference === undefined) {
72626
+ const kind = quantity ? "quantity" : "amount";
72627
+ const message = `CDS ${kind} field "${field.name}" requires ${referenceAnnotation}`;
72628
+ issues.push(issue_1.Issue.atToken(file, field.node.getFirstToken(), message, this.getMetadata().key, this.conf.severity));
72629
+ continue;
72630
+ }
72631
+ const companion = fields.find(f => f.name.toUpperCase() === reference.toUpperCase());
72632
+ if (companion === undefined) {
72633
+ const kind = quantity ? "unit" : "currency";
72634
+ const message = `CDS field "${field.name}" references missing ${kind} field "${reference}"`;
72635
+ issues.push(issue_1.Issue.atToken(file, field.node.getFirstToken(), message, this.getMetadata().key, this.conf.severity));
72636
+ continue;
72637
+ }
72638
+ const companionKey = field.semanticType + ":" + companion.name.toUpperCase();
72639
+ if (reportedCompanionFields.has(companionKey) === false
72640
+ && this.hasCompanionAnnotation(companion.annotations, field.semanticType) === false) {
72641
+ const message = `CDS field "${companion.name}" requires ${companionAnnotation}`;
72642
+ issues.push(issue_1.Issue.atToken(file, companion.node.getFirstToken(), message, this.getMetadata().key, this.conf.severity));
72643
+ reportedCompanionFields.add(companionKey);
72644
+ }
72645
+ }
72646
+ }
72647
+ determineSemanticType(typeNode, ddic) {
72648
+ var _a;
72649
+ const typeName = typeNode.findDirectExpressions(expressions_1.CDSName).map(n => this.name(n)).join(".");
72650
+ const upper = typeName.toUpperCase();
72651
+ if (upper === "ABAP.QUAN" || KNOWN_QUAN_DATA_ELEMENTS.includes(upper)) {
72652
+ return "QUAN";
72653
+ }
72654
+ else if (upper === "ABAP.CURR" || KNOWN_CURR_DATA_ELEMENTS.includes(upper)) {
72655
+ return "CURR";
72656
+ }
72657
+ const object = ddic.lookup(typeName).object;
72658
+ if (object instanceof objects_1.DataElement) {
72659
+ const dataType = (_a = object.getDataType(this.reg)) === null || _a === void 0 ? void 0 : _a.toUpperCase();
72660
+ if (dataType === "QUAN" || dataType === "CURR") {
72661
+ return dataType;
72662
+ }
72663
+ }
72664
+ return undefined;
72665
+ }
72666
+ findSemanticReference(annotations, type) {
72667
+ const expression = type === "QUAN"
72668
+ ? /(?:SEMANTICS\.QUANTITY|SEMANTICS:\{QUANTITY:\{)\.?UNITOFMEASURE:'([^']+)'/i
72669
+ : /(?:SEMANTICS\.AMOUNT|SEMANTICS:\{AMOUNT:\{)\.?CURRENCYCODE:'([^']+)'/i;
72670
+ for (const annotation of annotations) {
72671
+ const match = annotation.match(expression);
72672
+ if (match) {
72673
+ return match[1];
72674
+ }
72675
+ }
72676
+ return undefined;
72677
+ }
72678
+ hasCompanionAnnotation(annotations, type) {
72679
+ if (type === "QUAN") {
72680
+ return annotations.some(a => a.toUpperCase().includes("@SEMANTICS.UNITOFMEASURE:TRUE")
72681
+ || a.toUpperCase().includes("@SEMANTICS:{UNITOFMEASURE:TRUE"));
72682
+ }
72683
+ else {
72684
+ return annotations.some(a => a.toUpperCase().includes("@SEMANTICS.CURRENCYCODE:TRUE")
72685
+ || a.toUpperCase().includes("@SEMANTICS:{CURRENCYCODE:TRUE"));
72686
+ }
72687
+ }
72688
+ checkFields(tree, sources, object, issues, file) {
72689
+ var _a;
72690
+ const associationNames = new Set();
72691
+ for (const association of ((_a = object.getParsedData()) === null || _a === void 0 ? void 0 : _a.associations) || []) {
72692
+ associationNames.add(association.name.toUpperCase());
72693
+ if (association.as) {
72694
+ associationNames.add(association.as.toUpperCase());
72695
+ }
72696
+ }
72697
+ for (const element of tree.findAllExpressionsRecursive(expressions_1.CDSElement)) {
72698
+ const prefixed = element.findDirectExpression(expressions_1.CDSPrefixedName);
72699
+ if (prefixed === undefined) {
72700
+ continue;
72701
+ }
72702
+ const names = prefixed.findDirectExpressions(expressions_1.CDSName);
72703
+ if (names.length === 0) {
72704
+ continue;
72705
+ }
72706
+ const first = this.name(names[0]);
72707
+ if (first === "*" || first.startsWith("$") || first.startsWith("#") || associationNames.has(first.toUpperCase())) {
72708
+ continue;
72709
+ }
72710
+ if (names.length > 1) {
72711
+ const matchingSources = sources.filter(s => s.names.includes(first.toUpperCase()));
72712
+ if (matchingSources.length !== 1) {
72713
+ continue;
72714
+ }
72715
+ const source = matchingSources[0];
72716
+ const sourceType = source.type;
72717
+ if (!(sourceType instanceof basic_1.StructureType)) {
72718
+ continue;
72719
+ }
72720
+ const field = this.name(names[1]);
72721
+ if (field !== "*" && sourceType.getComponentByName(field) === undefined) {
72722
+ issues.push(issue_1.Issue.atToken(file, names[1].getFirstToken(), `CDS field "${field}" not found in "${source.entity}"`, this.getMetadata().key, this.conf.severity));
72723
+ }
72724
+ continue;
72725
+ }
72726
+ // Do not guess across joins or when a dependency is absent.
72727
+ if (sources.length !== 1) {
72728
+ continue;
72729
+ }
72730
+ const source = sources[0];
72731
+ const sourceType = source.type;
72732
+ if (!(sourceType instanceof basic_1.StructureType)) {
72733
+ continue;
72734
+ }
72735
+ if (sourceType.getComponentByName(first) === undefined) {
72736
+ issues.push(issue_1.Issue.atToken(file, names[0].getFirstToken(), `CDS field "${first}" not found in "${source.entity}"`, this.getMetadata().key, this.conf.severity));
72737
+ }
72738
+ }
72739
+ }
72740
+ name(node) {
72741
+ return node.concatTokens().replace(/ /g, "");
72742
+ }
72743
+ }
72744
+ exports.CDSCheckSyntax = CDSCheckSyntax;
72745
+ //# sourceMappingURL=cds_check_syntax.js.map
72746
+
72747
+ /***/ },
72748
+
72418
72749
  /***/ "../core/build/src/rules/cds_comment_style.js"
72419
72750
  /*!****************************************************!*\
72420
72751
  !*** ../core/build/src/rules/cds_comment_style.js ***!
@@ -83509,6 +83840,7 @@ __exportStar(__webpack_require__(/*! ./begin_single_include */ "../core/build/sr
83509
83840
  __exportStar(__webpack_require__(/*! ./call_transaction_authority_check */ "../core/build/src/rules/call_transaction_authority_check.js"), exports);
83510
83841
  __exportStar(__webpack_require__(/*! ./catch_and_raise */ "../core/build/src/rules/catch_and_raise.js"), exports);
83511
83842
  __exportStar(__webpack_require__(/*! ./cds_association_name */ "../core/build/src/rules/cds_association_name.js"), exports);
83843
+ __exportStar(__webpack_require__(/*! ./cds_check_syntax */ "../core/build/src/rules/cds_check_syntax.js"), exports);
83512
83844
  __exportStar(__webpack_require__(/*! ./cds_comment_style */ "../core/build/src/rules/cds_comment_style.js"), exports);
83513
83845
  __exportStar(__webpack_require__(/*! ./cds_field_order */ "../core/build/src/rules/cds_field_order.js"), exports);
83514
83846
  __exportStar(__webpack_require__(/*! ./cds_legacy_view */ "../core/build/src/rules/cds_legacy_view.js"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/cli",
3
- "version": "2.120.26",
3
+ "version": "2.120.27",
4
4
  "description": "abaplint - Command Line Interface",
5
5
  "funding": "https://github.com/sponsors/larshp",
6
6
  "bin": {
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "homepage": "https://abaplint.org",
41
41
  "devDependencies": {
42
- "@abaplint/core": "^2.120.26",
42
+ "@abaplint/core": "^2.120.27",
43
43
  "@types/chai": "^4.3.20",
44
44
  "@types/minimist": "^1.2.5",
45
45
  "@types/mocha": "^10.0.10",