@abaplint/core 2.99.11 → 2.100.0

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.
@@ -1300,6 +1300,7 @@ export declare class CurrentScope {
1300
1300
  findVariable(name: string | undefined): TypedIdentifier | undefined;
1301
1301
  getDDIC(): DDIC;
1302
1302
  getDDICReferences(): IDDICReferences;
1303
+ getMSAGReferences(): IMSAGReferences;
1303
1304
  getParentObj(): IObject;
1304
1305
  getName(): string;
1305
1306
  getType(): ScopeType;
@@ -3078,6 +3079,20 @@ declare class ImportNametab implements IStatement {
3078
3079
  getMatcher(): IStatementRunnable;
3079
3080
  }
3080
3081
 
3082
+ declare interface IMSAGReferences {
3083
+ clear(obj: IObject): void;
3084
+ addUsing(filename: string, token: Token, messageClass: string, number: string): void;
3085
+ listByFilename(filename: string): {
3086
+ token: Token;
3087
+ messageClass: string;
3088
+ number: string;
3089
+ }[];
3090
+ listByMessage(messageClass: string, number: string): {
3091
+ filename: string;
3092
+ token: Token;
3093
+ }[];
3094
+ }
3095
+
3081
3096
  declare class InboundService extends AbstractObject {
3082
3097
  getType(): string;
3083
3098
  getAllowedNaming(): {
@@ -3424,6 +3439,7 @@ export declare interface IRegistry {
3424
3439
  findIssuesObject(iobj: IObject): readonly Issue[];
3425
3440
  inErrorNamespace(name: string): boolean;
3426
3441
  getDDICReferences(): IDDICReferences;
3442
+ getMSAGReferences(): IMSAGReferences;
3427
3443
  getConfig(): IConfiguration;
3428
3444
  setConfig(conf: IConfiguration): IRegistry;
3429
3445
  /** Get all objects, including dependencies */
@@ -3842,9 +3858,9 @@ export declare class MemoryFile extends AbstractFile {
3842
3858
  }
3843
3859
 
3844
3860
  declare class Message {
3845
- private readonly num;
3846
- private readonly msg;
3847
- constructor(num: string, msg: string);
3861
+ private readonly number;
3862
+ private readonly message;
3863
+ constructor(number: string, message: string);
3848
3864
  getNumber(): string;
3849
3865
  getMessage(): string;
3850
3866
  getPlaceholderCount(): number;
@@ -4721,11 +4737,13 @@ export declare class Registry implements IRegistry {
4721
4737
  private readonly objects;
4722
4738
  private readonly objectsByType;
4723
4739
  private readonly dependencies;
4724
- private readonly references;
4740
+ private readonly ddicReferences;
4741
+ private readonly msagReferences;
4725
4742
  private conf;
4726
4743
  constructor(conf?: IConfiguration);
4727
4744
  static abaplintVersion(): string;
4728
4745
  getDDICReferences(): IDDICReferences;
4746
+ getMSAGReferences(): IMSAGReferences;
4729
4747
  getObjects(): Generator<IObject, void, undefined>;
4730
4748
  getObjectsByType(type: string): Generator<IObject, void, undefined>;
4731
4749
  getFiles(): Generator<IFile, void, undefined>;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=_imsag_references.js.map
@@ -324,6 +324,9 @@ class CurrentScope {
324
324
  getDDICReferences() {
325
325
  return this.reg.getDDICReferences();
326
326
  }
327
+ getMSAGReferences() {
328
+ return this.reg.getMSAGReferences();
329
+ }
327
330
  getParentObj() {
328
331
  return this.parentObj;
329
332
  }
@@ -5,9 +5,26 @@ 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
9
  for (const f of node.findDirectExpressions(Expressions.Source)) {
9
10
  new source_1.Source().runSyntax(f, scope, filename);
10
11
  }
12
+ if (node.getFirstToken().getStr().toUpperCase() === "ID") {
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();
15
+ if ((id === null || id === void 0 ? void 0 : id.startsWith("'")) && number) {
16
+ const messageClass = id.substring(1, id.length - 1).toUpperCase();
17
+ scope.getMSAGReferences().addUsing(filename, node.getFirstToken(), messageClass, number);
18
+ }
19
+ }
20
+ else {
21
+ const typeAndNumber = (_c = node.findDirectExpression(Expressions.MessageTypeAndNumber)) === null || _c === void 0 ? void 0 : _c.concatTokens();
22
+ 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();
24
+ if (messageNumber && messageClass) {
25
+ scope.getMSAGReferences().addUsing(filename, node.getFirstToken(), messageClass, messageNumber);
26
+ }
27
+ }
11
28
  }
12
29
  }
13
30
  exports.MessageSource = MessageSource;
@@ -285,6 +285,7 @@ class SyntaxLogic {
285
285
  }
286
286
  this.issues = [];
287
287
  this.reg.getDDICReferences().clear(this.object);
288
+ this.reg.getMSAGReferences().clear(this.object);
288
289
  if (this.object instanceof objects_1.Program && this.object.isInclude()) {
289
290
  // todo, show some kind of error?
290
291
  return { issues: [], spaghetti: this.scope.pop(new position_1.Position(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER)) };
@@ -2,21 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Message = void 0;
4
4
  class Message {
5
- constructor(num, msg) {
6
- this.num = num;
7
- if (this.num === undefined) {
8
- this.num = "";
5
+ constructor(number, message) {
6
+ this.number = number;
7
+ if (this.number === undefined) {
8
+ this.number = "";
9
9
  }
10
- this.msg = msg;
11
- if (this.msg === undefined) {
12
- this.msg = "";
10
+ this.message = message;
11
+ if (this.message === undefined) {
12
+ this.message = "";
13
13
  }
14
14
  }
15
15
  getNumber() {
16
- return this.num;
16
+ return this.number;
17
17
  }
18
18
  getMessage() {
19
- return this.msg;
19
+ return this.message;
20
20
  }
21
21
  getPlaceholderCount() {
22
22
  return (this.getMessage().match(/&/g) || []).length;
@@ -48,10 +48,11 @@ class DDICReferences {
48
48
  }
49
49
  }
50
50
  }
51
- const newName = obj.getName().toUpperCase();
52
- const newType = obj.getType();
53
- if ((_b = this.nameTypeIndex[newName]) === null || _b === void 0 ? void 0 : _b[newType]) {
54
- this.nameTypeIndex[newName][newType] = [];
51
+ // remove from name + type index
52
+ const name = obj.getName().toUpperCase();
53
+ const type = obj.getType();
54
+ if ((_b = this.nameTypeIndex[name]) === null || _b === void 0 ? void 0 : _b[type]) {
55
+ this.nameTypeIndex[name][type] = [];
55
56
  }
56
57
  }
57
58
  listByFilename(filename, line) {
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MSAGReferences = void 0;
4
+ class MSAGReferences {
5
+ constructor() {
6
+ this.nameNumberIndex = {};
7
+ this.filenameIndex = {};
8
+ }
9
+ addUsing(filename, token, messageClass, number) {
10
+ if (this.filenameIndex[filename] === undefined) {
11
+ this.filenameIndex[filename] = [];
12
+ }
13
+ this.filenameIndex[filename].push({
14
+ token: token,
15
+ messageClass: messageClass,
16
+ number: number,
17
+ });
18
+ if (this.nameNumberIndex[messageClass] === undefined) {
19
+ this.nameNumberIndex[messageClass] = {};
20
+ }
21
+ if (this.nameNumberIndex[messageClass][number] === undefined) {
22
+ this.nameNumberIndex[messageClass][number] = [];
23
+ }
24
+ this.nameNumberIndex[messageClass][number].push({
25
+ filename: filename,
26
+ token: token,
27
+ });
28
+ }
29
+ clear(obj) {
30
+ for (const file of obj.getFiles()) {
31
+ const filename = file.getFilename();
32
+ for (const fIndex of this.filenameIndex[filename] || []) {
33
+ // this should be okay for performance, each message should be referenced less than 10 times typically
34
+ this.nameNumberIndex[fIndex.messageClass][fIndex.number] =
35
+ this.nameNumberIndex[fIndex.messageClass][fIndex.number].filter(i => i.filename !== filename);
36
+ }
37
+ delete this.filenameIndex[filename];
38
+ }
39
+ }
40
+ listByFilename(filename) {
41
+ return this.filenameIndex[filename] || [];
42
+ }
43
+ listByMessage(messageClass, number) {
44
+ var _a;
45
+ return ((_a = this.nameNumberIndex[messageClass]) === null || _a === void 0 ? void 0 : _a[number]) || [];
46
+ }
47
+ }
48
+ exports.MSAGReferences = MSAGReferences;
49
+ //# sourceMappingURL=msag_references.js.map
@@ -32,6 +32,7 @@ class MessageClass extends _abstract_object_1.AbstractObject {
32
32
  return msg ? msg : [];
33
33
  }
34
34
  getByNumber(num) {
35
+ // todo, optimize performance,
35
36
  for (const message of this.getMessages()) {
36
37
  if (message.getNumber() === num) {
37
38
  return message;
@@ -7,6 +7,7 @@ const find_global_definitions_1 = require("./abap/5_syntax/global_definitions/fi
7
7
  const excludeHelper_1 = require("./utils/excludeHelper");
8
8
  const ddic_references_1 = require("./ddic_references");
9
9
  const rules_runner_1 = require("./rules_runner");
10
+ const msag_references_1 = require("./msag_references");
10
11
  // todo, this should really be an instance in case there are multiple Registry'ies
11
12
  class ParsingPerformance {
12
13
  static clear() {
@@ -59,14 +60,18 @@ class Registry {
59
60
  this.objectsByType = {};
60
61
  this.dependencies = {};
61
62
  this.conf = conf ? conf : config_1.Config.getDefault();
62
- this.references = new ddic_references_1.DDICReferences();
63
+ this.ddicReferences = new ddic_references_1.DDICReferences();
64
+ this.msagReferences = new msag_references_1.MSAGReferences();
63
65
  }
64
66
  static abaplintVersion() {
65
67
  // magic, see build script "version.sh"
66
- return "2.99.11";
68
+ return "2.100.0";
67
69
  }
68
70
  getDDICReferences() {
69
- return this.references;
71
+ return this.ddicReferences;
72
+ }
73
+ getMSAGReferences() {
74
+ return this.msagReferences;
70
75
  }
71
76
  *getObjects() {
72
77
  for (const name in this.objects) {
@@ -154,7 +159,8 @@ class Registry {
154
159
  const obj = this.find(file.getObjectName(), file.getObjectType());
155
160
  obj.removeFile(file);
156
161
  if (obj.getFiles().length === 0) {
157
- this.references.clear(obj);
162
+ this.ddicReferences.clear(obj);
163
+ this.msagReferences.clear(obj);
158
164
  this.removeObject(obj);
159
165
  }
160
166
  return this;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EasyToFindMessages = exports.EasyToFindMessagesConf = void 0;
4
+ const issue_1 = require("../issue");
5
+ const _irule_1 = require("./_irule");
6
+ const _basic_rule_config_1 = require("./_basic_rule_config");
7
+ const position_1 = require("../position");
8
+ const syntax_1 = require("../abap/5_syntax/syntax");
9
+ const _abap_object_1 = require("../objects/_abap_object");
10
+ class EasyToFindMessagesConf extends _basic_rule_config_1.BasicRuleConfig {
11
+ }
12
+ exports.EasyToFindMessagesConf = EasyToFindMessagesConf;
13
+ class EasyToFindMessages {
14
+ constructor() {
15
+ this.conf = new EasyToFindMessagesConf();
16
+ }
17
+ getMetadata() {
18
+ return {
19
+ key: "easy_to_find_messages",
20
+ title: "Easy to find messages",
21
+ shortDescription: `Make messages easy to find`,
22
+ extendedInformation: `All messages must be statically referenced exactly once
23
+
24
+ Only MESSAGE and RAISE statments are counted as static references
25
+
26
+ Also see rule "message_exists"
27
+
28
+ https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#make-messages-easy-to-find`,
29
+ tags: [_irule_1.RuleTag.Styleguide],
30
+ };
31
+ }
32
+ getConfig() {
33
+ return this.conf;
34
+ }
35
+ setConfig(conf) {
36
+ this.conf = conf;
37
+ }
38
+ initialize(reg) {
39
+ this.msagReferences = reg.getMSAGReferences();
40
+ // the SyntaxLogic builds the references
41
+ for (const obj of reg.getObjects()) {
42
+ if (obj instanceof _abap_object_1.ABAPObject) {
43
+ new syntax_1.SyntaxLogic(reg, obj).run();
44
+ }
45
+ }
46
+ return this;
47
+ }
48
+ run(object) {
49
+ const issues = [];
50
+ if (object.getType() === "MSAG") {
51
+ const msag = object;
52
+ for (const message of msag.getMessages()) {
53
+ const where = this.msagReferences.listByMessage(msag.getName().toUpperCase(), message.getNumber());
54
+ if (where.length === 0) {
55
+ const text = `Message ${message.getNumber()} not statically referenced`;
56
+ const position = new position_1.Position(1, 1);
57
+ const issue = issue_1.Issue.atPosition(object.getFiles()[0], position, text, this.getMetadata().key, this.conf.severity);
58
+ issues.push(issue);
59
+ }
60
+ else if (where.length >= 2) {
61
+ const text = `Message ${message.getNumber()} referenced more than once`;
62
+ const position = new position_1.Position(1, 1);
63
+ const issue = issue_1.Issue.atPosition(object.getFiles()[0], position, text, this.getMetadata().key, this.conf.severity);
64
+ issues.push(issue);
65
+ }
66
+ }
67
+ }
68
+ return issues;
69
+ }
70
+ }
71
+ exports.EasyToFindMessages = EasyToFindMessages;
72
+ //# sourceMappingURL=easy_to_find_messages.js.map
@@ -53,6 +53,7 @@ __exportStar(require("./definitions_top"), exports);
53
53
  __exportStar(require("./description_empty"), exports);
54
54
  __exportStar(require("./double_space"), exports);
55
55
  __exportStar(require("./downport"), exports);
56
+ __exportStar(require("./easy_to_find_messages"), exports);
56
57
  __exportStar(require("./empty_line_in_statement"), exports);
57
58
  __exportStar(require("./empty_statement"), exports);
58
59
  __exportStar(require("./empty_structure"), exports);
@@ -17,6 +17,7 @@ class MSAGConsistency {
17
17
  key: "msag_consistency",
18
18
  title: "MSAG consistency check",
19
19
  shortDescription: `Checks the validity of messages in message classes`,
20
+ extendedInformation: `Message numbers must be 3 digits, and message text must not be empty`,
20
21
  };
21
22
  }
22
23
  getDescription(reason) {
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.99.11",
3
+ "version": "2.100.0",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",
7
+ "funding": "https://github.com/sponsors/larshp",
7
8
  "scripts": {
8
9
  "lint": "eslint src/**/*.ts test/**/*.ts --format unix",
9
10
  "lint:fix": "eslint src/**/*.ts test/**/*.ts --format unix --fix",
@@ -47,10 +48,10 @@
47
48
  },
48
49
  "homepage": "https://abaplint.org",
49
50
  "devDependencies": {
50
- "@microsoft/api-extractor": "^7.34.8",
51
+ "@microsoft/api-extractor": "^7.34.9",
51
52
  "@types/chai": "^4.3.5",
52
53
  "@types/mocha": "^10.0.1",
53
- "@types/node": "^20.1.2",
54
+ "@types/node": "^20.1.3",
54
55
  "chai": "^4.3.7",
55
56
  "eslint": "^8.40.0",
56
57
  "mocha": "^10.2.0",