@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.
- package/build/abaplint.d.ts +22 -4
- package/build/src/_imsag_references.js +3 -0
- package/build/src/abap/5_syntax/_current_scope.js +3 -0
- package/build/src/abap/5_syntax/expressions/message_source.js +17 -0
- package/build/src/abap/5_syntax/syntax.js +1 -0
- package/build/src/abap/types/message.js +9 -9
- package/build/src/ddic_references.js +5 -4
- package/build/src/msag_references.js +49 -0
- package/build/src/objects/message_class.js +1 -0
- package/build/src/registry.js +10 -4
- package/build/src/rules/easy_to_find_messages.js +72 -0
- package/build/src/rules/index.js +1 -0
- package/build/src/rules/msag_consistency.js +1 -0
- package/package.json +4 -3
package/build/abaplint.d.ts
CHANGED
|
@@ -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
|
|
3846
|
-
private readonly
|
|
3847
|
-
constructor(
|
|
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
|
|
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>;
|
|
@@ -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(
|
|
6
|
-
this.
|
|
7
|
-
if (this.
|
|
8
|
-
this.
|
|
5
|
+
constructor(number, message) {
|
|
6
|
+
this.number = number;
|
|
7
|
+
if (this.number === undefined) {
|
|
8
|
+
this.number = "";
|
|
9
9
|
}
|
|
10
|
-
this.
|
|
11
|
-
if (this.
|
|
12
|
-
this.
|
|
10
|
+
this.message = message;
|
|
11
|
+
if (this.message === undefined) {
|
|
12
|
+
this.message = "";
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
getNumber() {
|
|
16
|
-
return this.
|
|
16
|
+
return this.number;
|
|
17
17
|
}
|
|
18
18
|
getMessage() {
|
|
19
|
-
return this.
|
|
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
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
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
|
package/build/src/registry.js
CHANGED
|
@@ -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.
|
|
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.
|
|
68
|
+
return "2.100.0";
|
|
67
69
|
}
|
|
68
70
|
getDDICReferences() {
|
|
69
|
-
return this.
|
|
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.
|
|
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
|
package/build/src/rules/index.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
54
|
+
"@types/node": "^20.1.3",
|
|
54
55
|
"chai": "^4.3.7",
|
|
55
56
|
"eslint": "^8.40.0",
|
|
56
57
|
"mocha": "^10.2.0",
|