@abaplint/core 2.79.18 → 2.79.22
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/README.md +6 -6
- package/build/abaplint.d.ts +1 -0
- package/build/src/abap/5_syntax/_builtin.js +50 -50
- package/build/src/abap/5_syntax/basic_types.js +7 -1
- package/build/src/abap/5_syntax/expressions/method_source.js +8 -2
- package/build/src/abap/5_syntax/statements/tables.js +1 -1
- package/build/src/ddic.js +6 -6
- package/build/src/ddic_references.js +44 -16
- package/build/src/lsp/_lookup.js +14 -5
- package/build/src/lsp/help.js +7 -7
- package/build/src/objects/assignment_service_to_authorization_group.js +1 -1
- package/build/src/registry.js +1 -1
- package/build/src/rules/abapdoc.js +1 -1
- package/build/src/rules/ambiguous_statement.js +5 -5
- package/build/src/rules/avoid_use.js +6 -6
- package/build/src/rules/begin_end_names.js +4 -4
- package/build/src/rules/begin_single_include.js +12 -12
- package/build/src/rules/chain_mainly_declarations.js +4 -4
- package/build/src/rules/check_abstract.js +2 -2
- package/build/src/rules/check_comments.js +3 -3
- package/build/src/rules/check_include.js +3 -3
- package/build/src/rules/check_no_handler_pragma.js +8 -8
- package/build/src/rules/check_subrc.js +8 -8
- package/build/src/rules/commented_code.js +1 -1
- package/build/src/rules/constructor_visibility_public.js +4 -4
- package/build/src/rules/contains_tab.js +2 -2
- package/build/src/rules/downport.js +103 -43
- package/build/src/rules/exit_or_check.js +3 -3
- package/build/src/rules/exporting.js +1 -1
- package/build/src/rules/forbidden_identifier.js +1 -1
- package/build/src/rules/forbidden_void_type.js +2 -2
- package/build/src/rules/functional_writing.js +17 -17
- package/build/src/rules/global_class.js +4 -4
- package/build/src/rules/identical_conditions.js +2 -2
- package/build/src/rules/identical_contents.js +15 -15
- package/build/src/rules/identical_descriptions.js +4 -4
- package/build/src/rules/if_in_if.js +7 -7
- package/build/src/rules/implement_methods.js +3 -3
- package/build/src/rules/in_statement_indentation.js +11 -11
- package/build/src/rules/intf_referencing_clas.js +3 -3
- package/build/src/rules/line_break_style.js +2 -2
- package/build/src/rules/line_length.js +1 -1
- package/build/src/rules/line_only_punc.js +1 -1
- package/build/src/rules/local_variable_names.js +2 -2
- package/build/src/rules/many_parentheses.js +10 -10
- package/build/src/rules/max_one_method_parameter_per_line.js +7 -7
- package/build/src/rules/max_one_statement.js +3 -3
- package/build/src/rules/nesting.js +1 -1
- package/build/src/rules/no_public_attributes.js +1 -1
- package/build/src/rules/no_yoda_conditions.js +4 -4
- package/build/src/rules/obsolete_statement.js +36 -36
- package/build/src/rules/omit_parameter_name.js +3 -3
- package/build/src/rules/omit_receiving.js +13 -13
- package/build/src/rules/parser_702_chaining.js +2 -2
- package/build/src/rules/parser_error.js +2 -2
- package/build/src/rules/parser_missing_space.js +1 -1
- package/build/src/rules/prefer_inline.js +16 -16
- package/build/src/rules/prefer_is_not.js +7 -7
- package/build/src/rules/prefer_raise_exception_new.js +3 -3
- package/build/src/rules/prefer_returning_to_exporting.js +1 -1
- package/build/src/rules/prefer_xsdbool.js +2 -2
- package/build/src/rules/remove_descriptions.js +4 -4
- package/build/src/rules/rfc_error_handling.js +9 -9
- package/build/src/rules/select_add_order_by.js +5 -5
- package/build/src/rules/select_performance.js +2 -2
- package/build/src/rules/sicf_consistency.js +4 -4
- package/build/src/rules/space_before_dot.js +2 -2
- package/build/src/rules/start_at_tab.js +1 -1
- package/build/src/rules/sy_modification.js +2 -2
- package/build/src/rules/tabl_enhancement_category.js +2 -2
- package/build/src/rules/unused_methods.js +9 -9
- package/build/src/rules/unused_variables.js +6 -6
- package/build/src/rules/use_bool_expression.js +8 -8
- package/build/src/rules/use_line_exists.js +6 -6
- package/build/src/rules/use_new.js +2 -2
- package/build/src/rules/when_others_last.js +6 -6
- package/package.json +76 -76
|
@@ -34,14 +34,14 @@ class CheckSubrc extends _abap_rule_1.ABAPRule {
|
|
|
34
34
|
key: "check_subrc",
|
|
35
35
|
title: "Check sy-subrc",
|
|
36
36
|
shortDescription: `Check sy-subrc`,
|
|
37
|
-
extendedInformation: `Pseudo comment "#EC CI_SUBRC can be added to suppress findings
|
|
38
|
-
|
|
39
|
-
If sy-dbcnt is checked after database statements, it is considered okay.
|
|
40
|
-
|
|
41
|
-
"SELECT SINGLE @abap_true FROM " is considered as an existence check
|
|
42
|
-
|
|
43
|
-
If IS ASSIGNED is checked after assigning, it is considered okay.
|
|
44
|
-
|
|
37
|
+
extendedInformation: `Pseudo comment "#EC CI_SUBRC can be added to suppress findings
|
|
38
|
+
|
|
39
|
+
If sy-dbcnt is checked after database statements, it is considered okay.
|
|
40
|
+
|
|
41
|
+
"SELECT SINGLE @abap_true FROM " is considered as an existence check
|
|
42
|
+
|
|
43
|
+
If IS ASSIGNED is checked after assigning, it is considered okay.
|
|
44
|
+
|
|
45
45
|
FIND statement with MATCH COUNT is considered okay if subrc is not checked`,
|
|
46
46
|
tags: [_irule_1.RuleTag.SingleFile],
|
|
47
47
|
pseudoComment: "EC CI_SUBRC",
|
|
@@ -30,7 +30,7 @@ class CommentedCode extends _abap_rule_1.ABAPRule {
|
|
|
30
30
|
key: "commented_code",
|
|
31
31
|
title: "Find commented code",
|
|
32
32
|
shortDescription: `Detects usage of commented out code.`,
|
|
33
|
-
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#delete-code-instead-of-commenting-it
|
|
33
|
+
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#delete-code-instead-of-commenting-it
|
|
34
34
|
https://docs.abapopenchecks.org/checks/14/`,
|
|
35
35
|
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.SingleFile],
|
|
36
36
|
};
|
|
@@ -18,10 +18,10 @@ class ConstructorVisibilityPublic {
|
|
|
18
18
|
key: "constructor_visibility_public",
|
|
19
19
|
title: "Check constructor visibility is public",
|
|
20
20
|
shortDescription: `Constructor must be placed in the public section, even if the class is not CREATE PUBLIC.`,
|
|
21
|
-
extendedInformation: `
|
|
22
|
-
This only applies to global classes.
|
|
23
|
-
|
|
24
|
-
https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#if-your-global-class-is-create-private-leave-the-constructor-public
|
|
21
|
+
extendedInformation: `
|
|
22
|
+
This only applies to global classes.
|
|
23
|
+
|
|
24
|
+
https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#if-your-global-class-is-create-private-leave-the-constructor-public
|
|
25
25
|
https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abeninstance_constructor_guidl.htm`,
|
|
26
26
|
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile],
|
|
27
27
|
};
|
|
@@ -25,8 +25,8 @@ class ContainsTab extends _abap_rule_1.ABAPRule {
|
|
|
25
25
|
key: "contains_tab",
|
|
26
26
|
title: "Code contains tab",
|
|
27
27
|
shortDescription: `Checks for usage of tabs (enable to enforce spaces)`,
|
|
28
|
-
extendedInformation: `
|
|
29
|
-
https://docs.abapopenchecks.org/checks/09/
|
|
28
|
+
extendedInformation: `
|
|
29
|
+
https://docs.abapopenchecks.org/checks/09/
|
|
30
30
|
https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#indent-and-snap-to-tab`,
|
|
31
31
|
tags: [_irule_1.RuleTag.Whitespace, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile],
|
|
32
32
|
};
|
|
@@ -5,6 +5,7 @@ const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
|
5
5
|
const issue_1 = require("../issue");
|
|
6
6
|
const _irule_1 = require("./_irule");
|
|
7
7
|
const _statement_1 = require("../abap/2_statements/statements/_statement");
|
|
8
|
+
const nodes_1 = require("../abap/nodes");
|
|
8
9
|
const Statements = require("../abap/2_statements/statements");
|
|
9
10
|
const Expressions = require("../abap/2_statements/expressions");
|
|
10
11
|
const edit_helper_1 = require("../edit_helper");
|
|
@@ -30,25 +31,26 @@ class Downport {
|
|
|
30
31
|
key: "downport",
|
|
31
32
|
title: "Downport statement",
|
|
32
33
|
shortDescription: `Experimental downport functionality`,
|
|
33
|
-
extendedInformation: `Much like the 'commented_code' rule this rule loops through unknown statements and tries parsing with
|
|
34
|
-
a higher level language version. If successful, various rules are applied to downport the statement.
|
|
35
|
-
Target downport version is always v702, thus rule is only enabled if target version is v702.
|
|
36
|
-
|
|
37
|
-
Current rules:
|
|
38
|
-
* NEW transformed to CREATE OBJECT, opposite of https://rules.abaplint.org/use_new/
|
|
39
|
-
* DATA() definitions are outlined, opposite of https://rules.abaplint.org/prefer_inline/
|
|
40
|
-
* FIELD-SYMBOL() definitions are outlined
|
|
41
|
-
* CONV is outlined
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* VALUE # with
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
|
|
34
|
+
extendedInformation: `Much like the 'commented_code' rule this rule loops through unknown statements and tries parsing with
|
|
35
|
+
a higher level language version. If successful, various rules are applied to downport the statement.
|
|
36
|
+
Target downport version is always v702, thus rule is only enabled if target version is v702.
|
|
37
|
+
|
|
38
|
+
Current rules:
|
|
39
|
+
* NEW transformed to CREATE OBJECT, opposite of https://rules.abaplint.org/use_new/
|
|
40
|
+
* DATA() definitions are outlined, opposite of https://rules.abaplint.org/prefer_inline/
|
|
41
|
+
* FIELD-SYMBOL() definitions are outlined
|
|
42
|
+
* CONV is outlined
|
|
43
|
+
* COND is outlined
|
|
44
|
+
* EMPTY KEY is changed to DEFAULT KEY, opposite of DEFAULT KEY in https://rules.abaplint.org/avoid_use/
|
|
45
|
+
* CAST changed to ?=
|
|
46
|
+
* LOOP AT method_call( ) is outlined
|
|
47
|
+
* VALUE # with structure fields
|
|
48
|
+
* VALUE # with internal table lines
|
|
49
|
+
* Table Expressions[ index ] are outlined
|
|
50
|
+
* SELECT INTO @DATA definitions are outlined
|
|
51
|
+
* Some occurrences of string template formatting option ALPHA changed to function module call
|
|
52
|
+
* SELECT/INSERT/MODIFY/DELETE/UPDATE "," in field list removed, "@" in source/targets removed
|
|
53
|
+
|
|
52
54
|
Only one transformation is applied to a statement at a time, so multiple steps might be required to do the full downport.`,
|
|
53
55
|
tags: [_irule_1.RuleTag.Experimental, _irule_1.RuleTag.Downport, _irule_1.RuleTag.Quickfix],
|
|
54
56
|
};
|
|
@@ -175,6 +177,10 @@ Only one transformation is applied to a statement at a time, so multiple steps m
|
|
|
175
177
|
if (found) {
|
|
176
178
|
return found;
|
|
177
179
|
}
|
|
180
|
+
found = this.outlineCond(high, lowFile, highSyntax);
|
|
181
|
+
if (found) {
|
|
182
|
+
return found;
|
|
183
|
+
}
|
|
178
184
|
found = this.outlineDataSimple(high, lowFile);
|
|
179
185
|
if (found) {
|
|
180
186
|
return found;
|
|
@@ -294,10 +300,10 @@ Only one transformation is applied to a statement at a time, so multiple steps m
|
|
|
294
300
|
const fieldName = f.concatTokens();
|
|
295
301
|
fieldDefinition += indentation + " " + fieldName + " TYPE " + tableName + "-" + fieldName + ",\n";
|
|
296
302
|
}
|
|
297
|
-
fieldDefinition = `DATA: BEGIN OF ${name},
|
|
303
|
+
fieldDefinition = `DATA: BEGIN OF ${name},
|
|
298
304
|
${fieldDefinition}${indentation} END OF ${name}.`;
|
|
299
305
|
}
|
|
300
|
-
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getStart(), `${fieldDefinition}
|
|
306
|
+
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getStart(), `${fieldDefinition}
|
|
301
307
|
${indentation}`);
|
|
302
308
|
const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, inlineData.getFirstToken().getStart(), inlineData.getLastToken().getEnd(), name);
|
|
303
309
|
const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
|
|
@@ -333,9 +339,9 @@ ${indentation}`);
|
|
|
333
339
|
}
|
|
334
340
|
const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
|
|
335
341
|
const name = ((_c = inlineData.findFirstExpression(Expressions.TargetField)) === null || _c === void 0 ? void 0 : _c.concatTokens()) || "error";
|
|
336
|
-
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getStart(), `TYPES: BEGIN OF ${uniqueName},
|
|
337
|
-
${fieldDefinitions}${indentation} END OF ${uniqueName}.
|
|
338
|
-
${indentation}DATA ${name} TYPE STANDARD TABLE OF ${uniqueName} WITH DEFAULT KEY.
|
|
342
|
+
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getStart(), `TYPES: BEGIN OF ${uniqueName},
|
|
343
|
+
${fieldDefinitions}${indentation} END OF ${uniqueName}.
|
|
344
|
+
${indentation}DATA ${name} TYPE STANDARD TABLE OF ${uniqueName} WITH DEFAULT KEY.
|
|
339
345
|
${indentation}`);
|
|
340
346
|
const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, inlineData.getFirstToken().getStart(), inlineData.getLastToken().getEnd(), name);
|
|
341
347
|
const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
|
|
@@ -369,11 +375,11 @@ ${indentation}`);
|
|
|
369
375
|
const uniqueName = this.uniqueName(node.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
|
|
370
376
|
const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
371
377
|
const firstToken = node.getFirstToken();
|
|
372
|
-
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, firstToken.getStart(), `DATA ${uniqueName} LIKE LINE OF ${pre}.
|
|
373
|
-
${indentation}READ TABLE ${pre} INDEX ${(_a = tableExpression.findFirstExpression(Expressions.Source)) === null || _a === void 0 ? void 0 : _a.concatTokens()} INTO ${uniqueName}.
|
|
374
|
-
${indentation}IF sy-subrc <> 0.
|
|
375
|
-
${indentation} RAISE EXCEPTION TYPE cx_sy_itab_line_not_found.
|
|
376
|
-
${indentation}ENDIF.
|
|
378
|
+
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, firstToken.getStart(), `DATA ${uniqueName} LIKE LINE OF ${pre}.
|
|
379
|
+
${indentation}READ TABLE ${pre} INDEX ${(_a = tableExpression.findFirstExpression(Expressions.Source)) === null || _a === void 0 ? void 0 : _a.concatTokens()} INTO ${uniqueName}.
|
|
380
|
+
${indentation}IF sy-subrc <> 0.
|
|
381
|
+
${indentation} RAISE EXCEPTION TYPE cx_sy_itab_line_not_found.
|
|
382
|
+
${indentation}ENDIF.
|
|
377
383
|
${indentation}`);
|
|
378
384
|
const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, startToken.getStart(), tableExpression.getLastToken().getEnd(), uniqueName);
|
|
379
385
|
const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
|
|
@@ -424,7 +430,7 @@ ${indentation}`);
|
|
|
424
430
|
}
|
|
425
431
|
i = key;
|
|
426
432
|
const concat = i.concatTokens();
|
|
427
|
-
if (concat.includes("WITH EMPTY KEY") === false) {
|
|
433
|
+
if (concat.toUpperCase().includes("WITH EMPTY KEY") === false) {
|
|
428
434
|
continue;
|
|
429
435
|
}
|
|
430
436
|
const token = i.findDirectTokenByText("EMPTY");
|
|
@@ -472,10 +478,10 @@ ${indentation}`);
|
|
|
472
478
|
const indentation = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
473
479
|
const source = (_b = templateSource === null || templateSource === void 0 ? void 0 : templateSource.findDirectExpression(Expressions.Source)) === null || _b === void 0 ? void 0 : _b.concatTokens();
|
|
474
480
|
const topTarget = (_c = node.findDirectExpression(Expressions.Target)) === null || _c === void 0 ? void 0 : _c.concatTokens();
|
|
475
|
-
const code = `CALL FUNCTION '${functionName}'
|
|
476
|
-
${indentation} EXPORTING
|
|
477
|
-
${indentation} input = ${source}
|
|
478
|
-
${indentation} IMPORTING
|
|
481
|
+
const code = `CALL FUNCTION '${functionName}'
|
|
482
|
+
${indentation} EXPORTING
|
|
483
|
+
${indentation} input = ${source}
|
|
484
|
+
${indentation} IMPORTING
|
|
479
485
|
${indentation} output = ${topTarget}.`;
|
|
480
486
|
const fix = edit_helper_1.EditHelper.replaceRange(lowFile, node.getFirstToken().getStart(), node.getLastToken().getEnd(), code);
|
|
481
487
|
return issue_1.Issue.atToken(lowFile, node.getFirstToken(), "Downport ALPHA", this.getMetadata().key, this.conf.severity, fix);
|
|
@@ -582,12 +588,13 @@ ${indentation} output = ${topTarget}.`;
|
|
|
582
588
|
return undefined;
|
|
583
589
|
}
|
|
584
590
|
findType(i, lowFile, highSyntax) {
|
|
591
|
+
var _a;
|
|
585
592
|
const expr = i.findDirectExpression(Expressions.TypeNameOrInfer);
|
|
586
593
|
if (expr === undefined) {
|
|
587
594
|
return undefined;
|
|
588
595
|
}
|
|
589
596
|
const firstToken = expr.getFirstToken();
|
|
590
|
-
const concat = expr.concatTokens();
|
|
597
|
+
const concat = expr.concatTokens().toLowerCase();
|
|
591
598
|
if (concat !== "#") {
|
|
592
599
|
return concat;
|
|
593
600
|
}
|
|
@@ -608,10 +615,10 @@ ${indentation} output = ${topTarget}.`;
|
|
|
608
615
|
if (inferred === undefined) {
|
|
609
616
|
return undefined;
|
|
610
617
|
}
|
|
611
|
-
return inferred.getType().getQualifiedName();
|
|
618
|
+
return (_a = inferred.getType().getQualifiedName()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
612
619
|
}
|
|
613
620
|
outlineFS(node, lowFile, highSyntax) {
|
|
614
|
-
var _a;
|
|
621
|
+
var _a, _b;
|
|
615
622
|
for (const i of node.findAllExpressionsRecursive(Expressions.InlineFS)) {
|
|
616
623
|
const nameToken = (_a = i.findDirectExpression(Expressions.TargetFieldSymbol)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
|
|
617
624
|
if (nameToken === undefined) {
|
|
@@ -629,7 +636,7 @@ ${indentation} output = ${topTarget}.`;
|
|
|
629
636
|
else if (found.getType() instanceof basic_1.VoidType) {
|
|
630
637
|
return issue_1.Issue.atToken(lowFile, i.getFirstToken(), "Error outlining voided type", this.getMetadata().key, this.conf.severity);
|
|
631
638
|
}
|
|
632
|
-
const type = found.getType().getQualifiedName() ? found.getType().getQualifiedName() : found.getType().toABAP();
|
|
639
|
+
const type = found.getType().getQualifiedName() ? (_b = found.getType().getQualifiedName()) === null || _b === void 0 ? void 0 : _b.toLowerCase() : found.getType().toABAP();
|
|
633
640
|
const code = `FIELD-SYMBOLS ${name} TYPE ${type}.\n` +
|
|
634
641
|
" ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
635
642
|
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), code);
|
|
@@ -640,7 +647,7 @@ ${indentation} output = ${topTarget}.`;
|
|
|
640
647
|
return undefined;
|
|
641
648
|
}
|
|
642
649
|
outlineData(node, lowFile, highSyntax) {
|
|
643
|
-
var _a;
|
|
650
|
+
var _a, _b;
|
|
644
651
|
for (const i of node.findAllExpressionsRecursive(Expressions.InlineData)) {
|
|
645
652
|
const nameToken = (_a = i.findDirectExpression(Expressions.TargetField)) === null || _a === void 0 ? void 0 : _a.getFirstToken();
|
|
646
653
|
if (nameToken === undefined) {
|
|
@@ -658,7 +665,7 @@ ${indentation} output = ${topTarget}.`;
|
|
|
658
665
|
else if (found.getType() instanceof basic_1.VoidType) {
|
|
659
666
|
return issue_1.Issue.atToken(lowFile, i.getFirstToken(), "Error outlining voided type", this.getMetadata().key, this.conf.severity);
|
|
660
667
|
}
|
|
661
|
-
const type = found.getType().getQualifiedName() ? found.getType().getQualifiedName() : found.getType().toABAP();
|
|
668
|
+
const type = found.getType().getQualifiedName() ? (_b = found.getType().getQualifiedName()) === null || _b === void 0 ? void 0 : _b.toLowerCase() : found.getType().toABAP();
|
|
662
669
|
const code = `DATA ${name} TYPE ${type}.\n` +
|
|
663
670
|
" ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
664
671
|
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), code);
|
|
@@ -668,6 +675,59 @@ ${indentation} output = ${topTarget}.`;
|
|
|
668
675
|
}
|
|
669
676
|
return undefined;
|
|
670
677
|
}
|
|
678
|
+
outlineCond(node, lowFile, highSyntax) {
|
|
679
|
+
for (const i of node.findAllExpressionsRecursive(Expressions.Source)) {
|
|
680
|
+
if (i.getFirstToken().getStr().toUpperCase() !== "COND") {
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
const body = i.findDirectExpression(Expressions.CondBody);
|
|
684
|
+
if (body === undefined) {
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
const uniqueName = this.uniqueName(i.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
|
|
688
|
+
const type = this.findType(i, lowFile, highSyntax);
|
|
689
|
+
const indent = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
690
|
+
const bodyCode = this.buildCondBody(body, uniqueName, indent);
|
|
691
|
+
const abap = `DATA ${uniqueName} TYPE ${type}.\n` + bodyCode;
|
|
692
|
+
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), abap);
|
|
693
|
+
const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, i.getFirstToken().getStart(), i.getLastToken().getEnd(), uniqueName);
|
|
694
|
+
const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
|
|
695
|
+
return issue_1.Issue.atToken(lowFile, i.getFirstToken(), "Downport COND", this.getMetadata().key, this.conf.severity, fix);
|
|
696
|
+
}
|
|
697
|
+
return undefined;
|
|
698
|
+
}
|
|
699
|
+
buildCondBody(body, uniqueName, indent) {
|
|
700
|
+
let code = indent;
|
|
701
|
+
for (const c of body.getChildren()) {
|
|
702
|
+
if (c instanceof nodes_1.TokenNode) {
|
|
703
|
+
switch (c.getFirstToken().getStr().toUpperCase()) {
|
|
704
|
+
case "WHEN":
|
|
705
|
+
code += "IF ";
|
|
706
|
+
break;
|
|
707
|
+
case "THEN":
|
|
708
|
+
code += ".\n";
|
|
709
|
+
break;
|
|
710
|
+
case "ELSE":
|
|
711
|
+
code += indent + "ELSE.\n";
|
|
712
|
+
break;
|
|
713
|
+
default:
|
|
714
|
+
throw "buildCondBody, unexpected token";
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
else if (c.get() instanceof Expressions.Cond) {
|
|
718
|
+
code += c.concatTokens();
|
|
719
|
+
}
|
|
720
|
+
else if (c.get() instanceof Expressions.Source) {
|
|
721
|
+
code += indent + " " + uniqueName + " = " + c.concatTokens() + ".\n";
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
throw "buildCondBody, unexpected expression";
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
code += indent + "ENDIF.\n";
|
|
728
|
+
code += indent;
|
|
729
|
+
return code;
|
|
730
|
+
}
|
|
671
731
|
outlineConv(node, lowFile, highSyntax) {
|
|
672
732
|
var _a;
|
|
673
733
|
for (const i of node.findAllExpressionsRecursive(Expressions.Source)) {
|
|
@@ -680,10 +740,10 @@ ${indentation} output = ${topTarget}.`;
|
|
|
680
740
|
}
|
|
681
741
|
const uniqueName = this.uniqueName(i.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
|
|
682
742
|
const type = this.findType(i, lowFile, highSyntax);
|
|
743
|
+
const indent = " ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
683
744
|
const abap = `DATA ${uniqueName} TYPE ${type}.\n` +
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
" ".repeat(node.getFirstToken().getStart().getCol() - 1);
|
|
745
|
+
indent + `${uniqueName} = ${body}.\n` +
|
|
746
|
+
indent;
|
|
687
747
|
const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, node.getFirstToken().getStart(), abap);
|
|
688
748
|
const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, i.getFirstToken().getStart(), i.getLastToken().getEnd(), uniqueName);
|
|
689
749
|
const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
|
|
@@ -24,10 +24,10 @@ class ExitOrCheck extends _abap_rule_1.ABAPRule {
|
|
|
24
24
|
return {
|
|
25
25
|
key: "exit_or_check",
|
|
26
26
|
title: "Find EXIT or CHECK outside loops",
|
|
27
|
-
shortDescription: `Detects usages of EXIT or CHECK statements outside of loops.
|
|
27
|
+
shortDescription: `Detects usages of EXIT or CHECK statements outside of loops.
|
|
28
28
|
Use RETURN to leave procesing blocks instead.`,
|
|
29
|
-
extendedInformation: `https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenleave_processing_blocks.htm
|
|
30
|
-
https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abapcheck_processing_blocks.htm
|
|
29
|
+
extendedInformation: `https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenleave_processing_blocks.htm
|
|
30
|
+
https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abapcheck_processing_blocks.htm
|
|
31
31
|
https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#check-vs-return`,
|
|
32
32
|
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix],
|
|
33
33
|
};
|
|
@@ -22,7 +22,7 @@ class Exporting extends _abap_rule_1.ABAPRule {
|
|
|
22
22
|
shortDescription: `Detects EXPORTING statements which can be omitted.`,
|
|
23
23
|
badExample: `call_method( EXPORTING foo = bar ).`,
|
|
24
24
|
goodExample: `call_method( foo = bar ).`,
|
|
25
|
-
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#omit-the-optional-keyword-exporting
|
|
25
|
+
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#omit-the-optional-keyword-exporting
|
|
26
26
|
https://docs.abapopenchecks.org/checks/30/`,
|
|
27
27
|
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.SingleFile],
|
|
28
28
|
};
|
|
@@ -26,7 +26,7 @@ class ForbiddenIdentifier extends _abap_rule_1.ABAPRule {
|
|
|
26
26
|
key: "forbidden_identifier",
|
|
27
27
|
title: "Forbidden Identifier",
|
|
28
28
|
shortDescription: `Forbid use of specified identifiers, list of regex.`,
|
|
29
|
-
extendedInformation: `Used in the transpiler to find javascript keywords in ABAP identifiers,
|
|
29
|
+
extendedInformation: `Used in the transpiler to find javascript keywords in ABAP identifiers,
|
|
30
30
|
https://github.com/abaplint/transpiler/blob/bda94b8b56e2b7f2f87be2168f12361aa530220e/packages/transpiler/src/validation.ts#L44`,
|
|
31
31
|
tags: [_irule_1.RuleTag.SingleFile],
|
|
32
32
|
};
|
|
@@ -28,8 +28,8 @@ class ForbiddenVoidType {
|
|
|
28
28
|
key: "forbidden_void_type",
|
|
29
29
|
title: "Forbidden Void Types",
|
|
30
30
|
shortDescription: `Avoid usage of specified void types.`,
|
|
31
|
-
extendedInformation: `Inspiration:
|
|
32
|
-
BOOLEAN, BOOLE_D, CHAR01, CHAR1, CHAR10, CHAR12, CHAR128, CHAR2, CHAR20, CHAR4, CHAR70,
|
|
31
|
+
extendedInformation: `Inspiration:
|
|
32
|
+
BOOLEAN, BOOLE_D, CHAR01, CHAR1, CHAR10, CHAR12, CHAR128, CHAR2, CHAR20, CHAR4, CHAR70,
|
|
33
33
|
DATS, TIMS, DATUM, FLAG, INT4, NUMC3, NUMC4, SAP_BOOL, TEXT25, TEXT80, X255, XFELD`,
|
|
34
34
|
};
|
|
35
35
|
}
|
|
@@ -28,26 +28,26 @@ class FunctionalWriting extends _abap_rule_1.ABAPRule {
|
|
|
28
28
|
key: "functional_writing",
|
|
29
29
|
title: "Use functional writing",
|
|
30
30
|
shortDescription: `Detects usage of call method when functional style calls can be used.`,
|
|
31
|
-
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#prefer-functional-to-procedural-calls
|
|
31
|
+
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#prefer-functional-to-procedural-calls
|
|
32
32
|
https://docs.abapopenchecks.org/checks/07/`,
|
|
33
33
|
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.SingleFile],
|
|
34
|
-
badExample: `CALL METHOD zcl_class=>method( ).
|
|
35
|
-
CALL METHOD cl_abap_typedescr=>describe_by_name
|
|
36
|
-
EXPORTING
|
|
37
|
-
p_name = 'NAME'
|
|
38
|
-
RECEIVING
|
|
39
|
-
p_descr_ref = lr_typedescr
|
|
40
|
-
EXCEPTIONS
|
|
41
|
-
type_not_found = 1
|
|
34
|
+
badExample: `CALL METHOD zcl_class=>method( ).
|
|
35
|
+
CALL METHOD cl_abap_typedescr=>describe_by_name
|
|
36
|
+
EXPORTING
|
|
37
|
+
p_name = 'NAME'
|
|
38
|
+
RECEIVING
|
|
39
|
+
p_descr_ref = lr_typedescr
|
|
40
|
+
EXCEPTIONS
|
|
41
|
+
type_not_found = 1
|
|
42
42
|
OTHERS = 2.`,
|
|
43
|
-
goodExample: `zcl_class=>method( ).
|
|
44
|
-
cl_abap_typedescr=>describe_by_name(
|
|
45
|
-
EXPORTING
|
|
46
|
-
p_name = 'NAME'
|
|
47
|
-
RECEIVING
|
|
48
|
-
p_descr_ref = lr_typedescr
|
|
49
|
-
EXCEPTIONS
|
|
50
|
-
type_not_found = 1
|
|
43
|
+
goodExample: `zcl_class=>method( ).
|
|
44
|
+
cl_abap_typedescr=>describe_by_name(
|
|
45
|
+
EXPORTING
|
|
46
|
+
p_name = 'NAME'
|
|
47
|
+
RECEIVING
|
|
48
|
+
p_descr_ref = lr_typedescr
|
|
49
|
+
EXCEPTIONS
|
|
50
|
+
type_not_found = 1
|
|
51
51
|
OTHERS = 2 ).`,
|
|
52
52
|
};
|
|
53
53
|
}
|
|
@@ -18,10 +18,10 @@ class GlobalClass extends _abap_rule_1.ABAPRule {
|
|
|
18
18
|
return {
|
|
19
19
|
key: "global_class",
|
|
20
20
|
title: "Global class checks",
|
|
21
|
-
shortDescription: `Checks related to global classes.
|
|
22
|
-
* global classes must be in own files
|
|
23
|
-
* file names must match class name
|
|
24
|
-
* global classes must be global definitions
|
|
21
|
+
shortDescription: `Checks related to global classes.
|
|
22
|
+
* global classes must be in own files
|
|
23
|
+
* file names must match class name
|
|
24
|
+
* global classes must be global definitions
|
|
25
25
|
* global interfaces must be global definitions`,
|
|
26
26
|
tags: [_irule_1.RuleTag.Syntax],
|
|
27
27
|
};
|
|
@@ -33,8 +33,8 @@ class IdenticalConditions extends _abap_rule_1.ABAPRule {
|
|
|
33
33
|
return {
|
|
34
34
|
key: "identical_conditions",
|
|
35
35
|
title: "Identical conditions",
|
|
36
|
-
shortDescription: `Find identical conditions in IF + CASE + WHILE etc
|
|
37
|
-
|
|
36
|
+
shortDescription: `Find identical conditions in IF + CASE + WHILE etc
|
|
37
|
+
|
|
38
38
|
Prerequsites: code is pretty printed with identical cAsE`,
|
|
39
39
|
tags: [_irule_1.RuleTag.SingleFile],
|
|
40
40
|
};
|
|
@@ -19,24 +19,24 @@ class IdenticalContents extends _abap_rule_1.ABAPRule {
|
|
|
19
19
|
return {
|
|
20
20
|
key: "identical_contents",
|
|
21
21
|
title: "Identical contents",
|
|
22
|
-
shortDescription: `Find identical contents in blocks inside IFs, both in the beginning and in the end.
|
|
23
|
-
|
|
24
|
-
Prerequsites: code is pretty printed with identical cAsE
|
|
25
|
-
|
|
22
|
+
shortDescription: `Find identical contents in blocks inside IFs, both in the beginning and in the end.
|
|
23
|
+
|
|
24
|
+
Prerequsites: code is pretty printed with identical cAsE
|
|
25
|
+
|
|
26
26
|
Chained statments are ignored`,
|
|
27
27
|
tags: [_irule_1.RuleTag.SingleFile],
|
|
28
|
-
badExample: `IF foo = bar.
|
|
29
|
-
WRITE 'bar'.
|
|
30
|
-
WRITE 'world'.
|
|
31
|
-
ELSE.
|
|
32
|
-
WRITE 'foo'.
|
|
33
|
-
WRITE 'world'.
|
|
28
|
+
badExample: `IF foo = bar.
|
|
29
|
+
WRITE 'bar'.
|
|
30
|
+
WRITE 'world'.
|
|
31
|
+
ELSE.
|
|
32
|
+
WRITE 'foo'.
|
|
33
|
+
WRITE 'world'.
|
|
34
34
|
ENDIF.`,
|
|
35
|
-
goodExample: `IF foo = bar.
|
|
36
|
-
WRITE 'bar'.
|
|
37
|
-
ELSE.
|
|
38
|
-
WRITE 'foo'.
|
|
39
|
-
ENDIF.
|
|
35
|
+
goodExample: `IF foo = bar.
|
|
36
|
+
WRITE 'bar'.
|
|
37
|
+
ELSE.
|
|
38
|
+
WRITE 'foo'.
|
|
39
|
+
ENDIF.
|
|
40
40
|
WRITE 'world'.`,
|
|
41
41
|
};
|
|
42
42
|
}
|
|
@@ -16,10 +16,10 @@ class IdenticalDescriptions {
|
|
|
16
16
|
key: "identical_descriptions",
|
|
17
17
|
title: "Identical descriptions",
|
|
18
18
|
shortDescription: `Searches for objects with the same type and same description`,
|
|
19
|
-
extendedInformation: `Case insensitive
|
|
20
|
-
|
|
21
|
-
Only checks the master language descriptions
|
|
22
|
-
|
|
19
|
+
extendedInformation: `Case insensitive
|
|
20
|
+
|
|
21
|
+
Only checks the master language descriptions
|
|
22
|
+
|
|
23
23
|
Works for: INTF, CLAS, DOMA, DTEL, FUNC in same FUGR`,
|
|
24
24
|
tags: [],
|
|
25
25
|
};
|
|
@@ -19,15 +19,15 @@ class IfInIf extends _abap_rule_1.ABAPRule {
|
|
|
19
19
|
key: "if_in_if",
|
|
20
20
|
title: "IF in IF",
|
|
21
21
|
shortDescription: `Detects nested ifs which can be refactored to a single condition using AND.`,
|
|
22
|
-
extendedInformation: `https://docs.abapopenchecks.org/checks/01/
|
|
22
|
+
extendedInformation: `https://docs.abapopenchecks.org/checks/01/
|
|
23
23
|
https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#keep-the-nesting-depth-low`,
|
|
24
|
-
badExample: `IF condition1.
|
|
25
|
-
IF condition2.
|
|
26
|
-
...
|
|
27
|
-
ENDIF.
|
|
24
|
+
badExample: `IF condition1.
|
|
25
|
+
IF condition2.
|
|
26
|
+
...
|
|
27
|
+
ENDIF.
|
|
28
28
|
ENDIF.`,
|
|
29
|
-
goodExample: `IF ( condition1 ) AND ( condition2 ).
|
|
30
|
-
...
|
|
29
|
+
goodExample: `IF ( condition1 ) AND ( condition2 ).
|
|
30
|
+
...
|
|
31
31
|
ENDIF.`,
|
|
32
32
|
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile],
|
|
33
33
|
};
|
|
@@ -97,9 +97,9 @@ class ImplementMethods extends _abap_rule_1.ABAPRule {
|
|
|
97
97
|
for (const i of ((_a = file.getStructure()) === null || _a === void 0 ? void 0 : _a.findAllStatements(Statements.ClassImplementation)) || []) {
|
|
98
98
|
const name = (_b = i.findFirstExpression(Expressions.ClassName)) === null || _b === void 0 ? void 0 : _b.getFirstToken().getStr().toUpperCase();
|
|
99
99
|
if (name === impl.identifier.getName().toUpperCase()) {
|
|
100
|
-
return edit_helper_1.EditHelper.insertAt(file, i.getLastToken().getEnd(), `
|
|
101
|
-
METHOD ${methodName.toLowerCase()}.
|
|
102
|
-
RETURN. " todo, implement method
|
|
100
|
+
return edit_helper_1.EditHelper.insertAt(file, i.getLastToken().getEnd(), `
|
|
101
|
+
METHOD ${methodName.toLowerCase()}.
|
|
102
|
+
RETURN. " todo, implement method
|
|
103
103
|
ENDMETHOD.`);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
@@ -31,19 +31,19 @@ class InStatementIndentation extends _abap_rule_1.ABAPRule {
|
|
|
31
31
|
key: "in_statement_indentation",
|
|
32
32
|
title: "In-statement indentation",
|
|
33
33
|
shortDescription: "Checks alignment within statements which span multiple lines.",
|
|
34
|
-
extendedInformation: `Lines following the first line should be indented once (2 spaces).
|
|
35
|
-
|
|
36
|
-
For block declaration statements, lines after the first should be indented an additional time (default: +2 spaces)
|
|
34
|
+
extendedInformation: `Lines following the first line should be indented once (2 spaces).
|
|
35
|
+
|
|
36
|
+
For block declaration statements, lines after the first should be indented an additional time (default: +2 spaces)
|
|
37
37
|
to distinguish them better from code within the block.`,
|
|
38
|
-
badExample: `IF 1 = 1
|
|
39
|
-
AND 2 = 2.
|
|
40
|
-
WRITE 'hello' &&
|
|
41
|
-
'world'.
|
|
38
|
+
badExample: `IF 1 = 1
|
|
39
|
+
AND 2 = 2.
|
|
40
|
+
WRITE 'hello' &&
|
|
41
|
+
'world'.
|
|
42
42
|
ENDIF.`,
|
|
43
|
-
goodExample: `IF 1 = 1
|
|
44
|
-
AND 2 = 2.
|
|
45
|
-
WRITE 'hello' &&
|
|
46
|
-
'world'.
|
|
43
|
+
goodExample: `IF 1 = 1
|
|
44
|
+
AND 2 = 2.
|
|
45
|
+
WRITE 'hello' &&
|
|
46
|
+
'world'.
|
|
47
47
|
ENDIF.`,
|
|
48
48
|
tags: [_irule_1.RuleTag.Whitespace, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.SingleFile],
|
|
49
49
|
};
|
|
@@ -26,9 +26,9 @@ class IntfReferencingClas {
|
|
|
26
26
|
key: "intf_referencing_clas",
|
|
27
27
|
title: "INTF referencing CLAS",
|
|
28
28
|
shortDescription: `Interface contains references to class`,
|
|
29
|
-
extendedInformation: `Only global interfaces are checked.
|
|
30
|
-
Only first level references are checked.
|
|
31
|
-
Exception class references are ignored.
|
|
29
|
+
extendedInformation: `Only global interfaces are checked.
|
|
30
|
+
Only first level references are checked.
|
|
31
|
+
Exception class references are ignored.
|
|
32
32
|
Void references are ignored.`,
|
|
33
33
|
};
|
|
34
34
|
}
|
|
@@ -15,8 +15,8 @@ class LineBreakStyle {
|
|
|
15
15
|
return {
|
|
16
16
|
key: "line_break_style",
|
|
17
17
|
title: "Makes sure line breaks are consistent in the ABAP code",
|
|
18
|
-
shortDescription: `Enforces LF as newlines in ABAP files
|
|
19
|
-
|
|
18
|
+
shortDescription: `Enforces LF as newlines in ABAP files
|
|
19
|
+
|
|
20
20
|
abapGit does not work with CRLF`,
|
|
21
21
|
tags: [_irule_1.RuleTag.Whitespace, _irule_1.RuleTag.SingleFile],
|
|
22
22
|
};
|
|
@@ -23,7 +23,7 @@ class LineLength extends _abap_rule_1.ABAPRule {
|
|
|
23
23
|
key: "line_length",
|
|
24
24
|
title: "Line length",
|
|
25
25
|
shortDescription: `Detects lines exceeding the provided maximum length.`,
|
|
26
|
-
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#stick-to-a-reasonable-line-length
|
|
26
|
+
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#stick-to-a-reasonable-line-length
|
|
27
27
|
https://docs.abapopenchecks.org/checks/04/`,
|
|
28
28
|
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile],
|
|
29
29
|
};
|
|
@@ -27,7 +27,7 @@ class LineOnlyPunc extends _abap_rule_1.ABAPRule {
|
|
|
27
27
|
key: "line_only_punc",
|
|
28
28
|
title: "Line containing only punctuation",
|
|
29
29
|
shortDescription: `Detects lines containing only punctuation.`,
|
|
30
|
-
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#close-brackets-at-line-end
|
|
30
|
+
extendedInformation: `https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md#close-brackets-at-line-end
|
|
31
31
|
https://docs.abapopenchecks.org/checks/16/`,
|
|
32
32
|
tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.Quickfix, _irule_1.RuleTag.SingleFile],
|
|
33
33
|
badExample: "zcl_class=>method(\n).",
|
|
@@ -30,8 +30,8 @@ class LocalVariableNames extends _abap_rule_1.ABAPRule {
|
|
|
30
30
|
return {
|
|
31
31
|
key: "local_variable_names",
|
|
32
32
|
title: "Local variable naming conventions",
|
|
33
|
-
shortDescription: `
|
|
34
|
-
Allows you to enforce a pattern, such as a prefix, for local variables, constants and field symbols.
|
|
33
|
+
shortDescription: `
|
|
34
|
+
Allows you to enforce a pattern, such as a prefix, for local variables, constants and field symbols.
|
|
35
35
|
Regexes are case-insensitive.`,
|
|
36
36
|
tags: [_irule_1.RuleTag.Naming, _irule_1.RuleTag.SingleFile],
|
|
37
37
|
};
|