@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.
- package/build/abaplint.d.ts +19 -0
- package/build/src/abap/2_statements/expressions/for.js +1 -1
- package/build/src/abap/2_statements/expressions/inline_field_definition.js +2 -1
- package/build/src/abap/2_statements/expressions/reduce_next.js +1 -1
- package/build/src/abap/5_syntax/expressions/component_compare.js +15 -3
- package/build/src/abap/5_syntax/expressions/component_cond.js +3 -3
- package/build/src/abap/5_syntax/expressions/filter_body.js +6 -13
- package/build/src/abap/5_syntax/expressions/inline_field_definition.js +2 -1
- package/build/src/abap/types/class_definition.js +7 -0
- package/build/src/ddl/expressions/ddl_clauses.js +7 -1
- package/build/src/ddl/expressions/ddl_table_field.js +1 -1
- package/build/src/objects/table.js +31 -3
- package/build/src/registry.js +1 -1
- package/build/src/rules/index.js +1 -0
- package/build/src/rules/no_mandt_in_database_operations.js +133 -0
- package/build/src/rules/xml_consistency.js +25 -0
- package/package.json +1 -1
package/build/abaplint.d.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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(
|
|
50
|
+
const fieldType = component_chain_1.ComponentChain.runSyntax(leftType, chain, input);
|
|
50
51
|
for (const s of node.findDirectExpressions(Expressions.Source)) {
|
|
51
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
46
|
+
const types = [];
|
|
47
47
|
for (const s of node.findDirectExpressions(Expressions.Source)) {
|
|
48
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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)
|
|
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
|
-
|
|
137
|
-
|
|
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
|
});
|
package/build/src/registry.js
CHANGED
package/build/src/rules/index.js
CHANGED
|
@@ -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
|