@abaplint/core 2.120.74 → 2.120.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/build/abaplint.d.ts +5 -1
  2. package/build/src/abap/2_statements/combi.js +143 -51
  3. package/build/src/abap/5_syntax/_builtin.js +8 -0
  4. package/build/src/abap/5_syntax/_loop_table_change.js +126 -0
  5. package/build/src/abap/5_syntax/expressions/_substring.js +83 -0
  6. package/build/src/abap/5_syntax/expressions/attribute_chain.js +3 -16
  7. package/build/src/abap/5_syntax/expressions/compare.js +33 -8
  8. package/build/src/abap/5_syntax/expressions/field_chain.js +5 -14
  9. package/build/src/abap/5_syntax/expressions/method_call_chain.js +16 -1
  10. package/build/src/abap/5_syntax/expressions/method_call_param.js +3 -0
  11. package/build/src/abap/5_syntax/expressions/target.js +14 -2
  12. package/build/src/abap/5_syntax/statements/append.js +6 -0
  13. package/build/src/abap/5_syntax/statements/read_table.js +39 -0
  14. package/build/src/abap/5_syntax/structures/cleanup.js +105 -0
  15. package/build/src/abap/5_syntax/syntax.js +13 -0
  16. package/build/src/ddic.js +1 -1
  17. package/build/src/objects/domain.js +4 -1
  18. package/build/src/registry.js +1 -1
  19. package/build/src/rules/abapdoc_html_tag.js +123 -0
  20. package/build/src/rules/abapdoc_leading_at.js +117 -0
  21. package/build/src/rules/abapdoc_parameter_names.js +149 -0
  22. package/build/src/rules/cds_check_syntax.js +4 -4
  23. package/build/src/rules/change_if_to_case.js +3 -2
  24. package/build/src/rules/changing_sorted_line.js +235 -0
  25. package/build/src/rules/class_attribute_names.js +15 -6
  26. package/build/src/rules/delete_index_in_loop.js +163 -0
  27. package/build/src/rules/final_newline.js +78 -0
  28. package/build/src/rules/forbidden_identifier.js +4 -3
  29. package/build/src/rules/forbidden_void_type.js +4 -9
  30. package/build/src/rules/index.js +11 -0
  31. package/build/src/rules/inline_packed_computation.js +220 -0
  32. package/build/src/rules/intf_referencing_clas.js +3 -1
  33. package/build/src/rules/line_only_punc.js +1 -1
  34. package/build/src/rules/local_class_naming.js +15 -2
  35. package/build/src/rules/local_variable_names.js +18 -9
  36. package/build/src/rules/macro_naming.js +3 -2
  37. package/build/src/rules/method_parameter_names.js +17 -7
  38. package/build/src/rules/modify_only_own_db_tables.js +3 -2
  39. package/build/src/rules/no_prefixes.js +25 -12
  40. package/build/src/rules/object_naming.js +18 -3
  41. package/build/src/rules/prefer_inline_cast.js +276 -0
  42. package/build/src/rules/range_row_values.js +200 -0
  43. package/build/src/rules/redundant_conversion.js +3 -0
  44. package/build/src/rules/selection_screen_naming.js +21 -2
  45. package/build/src/rules/sicf_consistency.js +1 -2
  46. package/build/src/rules/sort_empty_key.js +188 -0
  47. package/build/src/rules/subrc_after_assign.js +220 -0
  48. package/build/src/rules/types_naming.js +3 -2
  49. package/build/src/rules/use_message_class.js +64 -1
  50. package/build/src/rules/wrong_abapdoc_position.js +58 -11
  51. package/build/src/rules/xml_consistency.js +6 -2
  52. package/build/src/utils/name_validator.js +7 -3
  53. package/package.json +1 -1
@@ -53,6 +53,7 @@ class TypesNaming extends _abap_rule_1.ABAPRule {
53
53
  constructor() {
54
54
  super(...arguments);
55
55
  this.conf = new TypesNamingConf();
56
+ this.regex = new RegExp(this.conf.pattern, "i");
56
57
  }
57
58
  getMetadata() {
58
59
  return {
@@ -70,10 +71,10 @@ class TypesNaming extends _abap_rule_1.ABAPRule {
70
71
  }
71
72
  setConfig(conf) {
72
73
  this.conf = conf;
74
+ this.regex = new RegExp(this.conf.pattern, "i");
73
75
  }
74
76
  runParsed(file, obj) {
75
77
  const issues = [];
76
- const testRegex = new RegExp(this.conf.pattern, "i");
77
78
  let nesting = 0;
78
79
  if (obj instanceof objects_1.TypePool) {
79
80
  return [];
@@ -102,7 +103,7 @@ class TypesNaming extends _abap_rule_1.ABAPRule {
102
103
  continue;
103
104
  }
104
105
  const token = expr.getFirstToken();
105
- if (testRegex.exec(token.getStr())) {
106
+ if (this.regex.exec(token.getStr())) {
106
107
  continue;
107
108
  }
108
109
  else {
@@ -40,7 +40,16 @@ const Expressions = __importStar(require("../abap/2_statements/expressions"));
40
40
  const Statements = __importStar(require("../abap/2_statements/statements"));
41
41
  const _basic_rule_config_1 = require("./_basic_rule_config");
42
42
  const _irule_1 = require("./_irule");
43
+ const syntax_1 = require("../abap/5_syntax/syntax");
44
+ const _typed_identifier_1 = require("../abap/types/_typed_identifier");
45
+ const basic_1 = require("../abap/types/basic");
46
+ const tokens_1 = require("../abap/1_lexer/tokens");
43
47
  class UseMessageClassConf extends _basic_rule_config_1.BasicRuleConfig {
48
+ constructor() {
49
+ super(...arguments);
50
+ /** Allow MESSAGE with an object reference, eg. an exception, like MESSAGE lx_error TYPE 'E' */
51
+ this.allowExceptions = true;
52
+ }
44
53
  }
45
54
  exports.UseMessageClassConf = UseMessageClassConf;
46
55
  class UseMessageClass extends _abap_rule_1.ABAPRule {
@@ -53,6 +62,9 @@ class UseMessageClass extends _abap_rule_1.ABAPRule {
53
62
  key: "use_message_class",
54
63
  title: "Use message class",
55
64
  shortDescription: `Define messages in SE91 instead of using inline text or variables`,
65
+ extendedInformation: `If allowExceptions is enabled, MESSAGE with an object reference is allowed, eg. MESSAGE lx_error TYPE 'E'.
66
+
67
+ Void types are only allowed if the type name contains "CX_", as void class references cannot be distinguished from other void types.`,
56
68
  tags: [_irule_1.RuleTag.Styleguide, _irule_1.RuleTag.SingleFile],
57
69
  badExample: `MESSAGE 'Something went wrong' TYPE 'E'.`,
58
70
  goodExample: `MESSAGE e001(bc_msg).`,
@@ -64,13 +76,14 @@ class UseMessageClass extends _abap_rule_1.ABAPRule {
64
76
  setConfig(conf) {
65
77
  this.conf = conf;
66
78
  }
67
- runParsed(file, _obj) {
79
+ runParsed(file, obj) {
68
80
  const structure = file.getStructure();
69
81
  if (structure === undefined) {
70
82
  return [];
71
83
  }
72
84
  const issues = [];
73
85
  const key = this.getMetadata().key;
86
+ let syntax = undefined;
74
87
  for (const statement of structure.findAllStatements(Statements.Message)) {
75
88
  // The parser populates MessageSource only for the class-based forms (e001(bc_msg) or ID/TYPE/NUMBER).
76
89
  // The text form (MESSAGE <source> TYPE <source>) uses MessageSourceSource directly with no MessageSource child.
@@ -78,10 +91,60 @@ class UseMessageClass extends _abap_rule_1.ABAPRule {
78
91
  if (statement.findDirectExpression(Expressions.MessageSource) !== undefined) {
79
92
  continue;
80
93
  }
94
+ if (this.conf.allowExceptions === true) {
95
+ syntax !== null && syntax !== void 0 ? syntax : (syntax = new syntax_1.SyntaxLogic(this.reg, obj).run());
96
+ if (this.isObjectReference(statement, file, syntax)) {
97
+ continue;
98
+ }
99
+ }
81
100
  issues.push(issue_1.Issue.atStatement(file, statement, "Use a message class (SE91) instead of inline text or variable", key, this.conf.severity));
82
101
  }
83
102
  return issues;
84
103
  }
104
+ isObjectReference(statement, file, syntax) {
105
+ var _a, _b;
106
+ const chain = (_b = (_a = statement.findDirectExpression(Expressions.MessageSourceSource)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(Expressions.Source)) === null || _b === void 0 ? void 0 : _b.findDirectExpression(Expressions.FieldChain);
107
+ if (chain === undefined) {
108
+ return false;
109
+ }
110
+ let type = undefined;
111
+ for (const child of chain.getChildren()) {
112
+ const c = child.get();
113
+ if (c instanceof Expressions.ComponentName) {
114
+ type = type instanceof basic_1.StructureType ? type.getComponentByName(child.concatTokens()) : undefined;
115
+ }
116
+ else if (c instanceof Expressions.SourceField
117
+ || c instanceof Expressions.SourceFieldSymbol
118
+ || c instanceof Expressions.AttributeName) {
119
+ type = this.findType(child.getFirstToken().getStart(), file, syntax);
120
+ }
121
+ else if (c instanceof Expressions.ClassName
122
+ || c instanceof tokens_1.InstanceArrow
123
+ || c instanceof tokens_1.StaticArrow
124
+ || c instanceof tokens_1.Dash) {
125
+ continue;
126
+ }
127
+ else {
128
+ return false;
129
+ }
130
+ if (type === undefined) {
131
+ return false;
132
+ }
133
+ }
134
+ // void class references cannot be distinguished from other void types, guess based on the name
135
+ return type instanceof basic_1.ObjectReferenceType
136
+ || type instanceof basic_1.GenericObjectReferenceType
137
+ || (type instanceof basic_1.VoidType && /CX_/i.test(type.getVoided() || ""));
138
+ }
139
+ findType(start, file, syntax) {
140
+ const scope = syntax.spaghetti.lookupPosition(start, file.getFilename());
141
+ for (const r of (scope === null || scope === void 0 ? void 0 : scope.getData().references) || []) {
142
+ if (r.position.getStart().equals(start) && r.resolved instanceof _typed_identifier_1.TypedIdentifier) {
143
+ return r.resolved.getType();
144
+ }
145
+ }
146
+ return undefined;
147
+ }
85
148
  }
86
149
  exports.UseMessageClass = UseMessageClass;
87
150
  //# sourceMappingURL=use_message_class.js.map
@@ -40,6 +40,8 @@ const _abap_rule_1 = require("./_abap_rule");
40
40
  const _basic_rule_config_1 = require("./_basic_rule_config");
41
41
  const _irule_1 = require("./_irule");
42
42
  const _statement_1 = require("../abap/2_statements/statements/_statement");
43
+ const edit_helper_1 = require("../edit_helper");
44
+ const position_1 = require("../position");
43
45
  class WrongAbapdocPositionConf extends _basic_rule_config_1.BasicRuleConfig {
44
46
  }
45
47
  exports.WrongAbapdocPositionConf = WrongAbapdocPositionConf;
@@ -59,10 +61,13 @@ elsewhere is silently ignored, leaving the declaration undocumented.
59
61
  The following positions are reported,
60
62
  * in front of a chained keyword, ie. before "CONSTANTS:" instead of after the colon,
61
63
  * in the middle of a statement, ie. between the parameters of a METHODS definition,
62
- * directly in front of ENDCLASS, ENDINTERFACE or a SECTION statement.
64
+ * directly in front of ENDCLASS, ENDINTERFACE or a SECTION statement,
65
+ * separated from its declaration by a blank line or a normal comment, the system reads both as the end of the block.
63
66
 
64
- Only checks ABAP Doc inside class definitions and interfaces.`,
65
- tags: [_irule_1.RuleTag.SingleFile],
67
+ Only checks ABAP Doc inside class definitions and interfaces, and directly in front of them.
68
+
69
+ The quick fix for a blank line deletes the blank lines.`,
70
+ tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix],
66
71
  badExample: `CLASS zcl_foo DEFINITION PUBLIC.
67
72
  PUBLIC SECTION.
68
73
  "! Navigation modes
@@ -88,6 +93,7 @@ ENDCLASS.`,
88
93
  this.conf = conf;
89
94
  }
90
95
  runParsed(file) {
96
+ var _a;
91
97
  const issues = [];
92
98
  const statements = file.getStatements();
93
99
  let definition = false;
@@ -102,7 +108,7 @@ ENDCLASS.`,
102
108
  definition = false;
103
109
  continue;
104
110
  }
105
- else if (definition === false || this.isAbapdoc(statement) === false) {
111
+ else if (this.isAbapdoc(statement) === false) {
106
112
  continue;
107
113
  }
108
114
  else if (this.isAbapdoc(statements[i - 1]) === true
@@ -110,19 +116,57 @@ ENDCLASS.`,
110
116
  continue; // only report the first row of each block
111
117
  }
112
118
  let next = undefined;
113
- for (let j = i + 1; j < statements.length; j++) {
114
- if (statements[j].get() instanceof _statement_1.Comment) {
115
- continue;
119
+ let j = i + 1;
120
+ for (; j < statements.length; j++) {
121
+ if (!(statements[j].get() instanceof _statement_1.Comment)) {
122
+ next = statements[j];
123
+ break;
116
124
  }
117
- next = statements[j];
118
- break;
119
125
  }
120
126
  if (next === undefined) {
121
127
  continue;
122
128
  }
123
- const message = this.check(statement, next);
129
+ else if (definition === false
130
+ && !(next.get() instanceof Statements.ClassDefinition)
131
+ && !(next.get() instanceof Statements.Interface)) {
132
+ continue;
133
+ }
134
+ // comments inside "next" are listed in front of it, only look at the rows above it
135
+ const nextRow = this.startRow(next);
136
+ const trailing = ((_a = statements[i - 1]) === null || _a === void 0 ? void 0 : _a.getEnd().getRow()) === statement.getStart().getRow();
137
+ let last = statement;
138
+ let separator = undefined;
139
+ let fix = undefined;
140
+ for (const s of trailing ? [] : statements.slice(i + 1, j)) {
141
+ if (s.getStart().getRow() >= nextRow) {
142
+ break;
143
+ }
144
+ else if (s.getStart().getRow() > last.getEnd().getRow() + 1) {
145
+ separator = "a blank line";
146
+ if (this.isAbapdoc(s) === true) {
147
+ fix = edit_helper_1.EditHelper.deleteRange(file, new position_1.Position(last.getEnd().getRow() + 1, 1), new position_1.Position(s.getStart().getRow(), 1));
148
+ }
149
+ break;
150
+ }
151
+ else if (this.isAbapdoc(s) === false) {
152
+ separator = "a comment";
153
+ break;
154
+ }
155
+ last = s;
156
+ }
157
+ if (separator === undefined && trailing === false && nextRow > last.getEnd().getRow() + 1) {
158
+ separator = "a blank line";
159
+ fix = edit_helper_1.EditHelper.deleteRange(file, new position_1.Position(last.getEnd().getRow() + 1, 1), new position_1.Position(nextRow, 1));
160
+ }
161
+ let message = this.check(statement, next);
162
+ if (message !== undefined) {
163
+ fix = undefined;
164
+ }
165
+ else if (separator !== undefined) {
166
+ message = "ABAP Doc is separated from its declaration by " + separator;
167
+ }
124
168
  if (message !== undefined) {
125
- issues.push(issue_1.Issue.atStatement(file, statement, message, this.getMetadata().key, this.conf.severity));
169
+ issues.push(issue_1.Issue.atStatement(file, statement, message, this.getMetadata().key, this.conf.severity, fix));
126
170
  }
127
171
  }
128
172
  return issues;
@@ -131,6 +175,9 @@ ENDCLASS.`,
131
175
  return (statement === null || statement === void 0 ? void 0 : statement.get()) instanceof _statement_1.Comment
132
176
  && statement.getFirstToken().getStr().startsWith(`"!`);
133
177
  }
178
+ startRow(statement) {
179
+ return Math.min(statement.getStart().getRow(), ...statement.getPragmas().map(p => p.getRow()));
180
+ }
134
181
  check(abapdoc, next) {
135
182
  const type = next.get();
136
183
  if (type instanceof Statements.EndClass
@@ -41,6 +41,10 @@ const _abap_object_1 = require("../objects/_abap_object");
41
41
  const _basic_rule_config_1 = require("./_basic_rule_config");
42
42
  const fast_xml_parser_1 = require("fast-xml-parser");
43
43
  const severity_1 = require("../severity");
44
+ const ABAPGIT_ATTRIBUTES = [
45
+ ["version", /<abapGit\b[^>]*\bversion="([^"]+)"/],
46
+ ["serializer_version", /<abapGit\b[^>]*\bserializer_version="([^"]+)"/],
47
+ ];
44
48
  class XMLConsistencyConf extends _basic_rule_config_1.BasicRuleConfig {
45
49
  constructor() {
46
50
  super(...arguments);
@@ -96,8 +100,8 @@ class XMLConsistency {
96
100
  issues.push(issue_1.Issue.atRow(file, 1, "XML parser error: " + res.err.msg, this.getMetadata().key, this.conf.severity));
97
101
  }
98
102
  else {
99
- for (const attribute of ["version", "serializer_version"]) {
100
- const value = (_a = xml.match(new RegExp(`<abapGit\\b[^>]*\\b${attribute}="([^"]+)"`))) === null || _a === void 0 ? void 0 : _a[1];
103
+ for (const [attribute, regex] of ABAPGIT_ATTRIBUTES) {
104
+ const value = (_a = xml.match(regex)) === null || _a === void 0 ? void 0 : _a[1];
101
105
  if (value !== undefined && value.match(/^v\d\.\d\.\d$/) === null) {
102
106
  issues.push(issue_1.Issue.atRow(file, 1, `Unexpected abapGit ${attribute} "${value}"`, this.getMetadata().key, this.conf.severity));
103
107
  }
@@ -2,12 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NameValidator = void 0;
4
4
  class NameValidator {
5
- static violatesRule(name, pattern, params) {
5
+ /** compile the configured ignorePatterns, case insensitive */
6
+ static compileIgnorePatterns(params) {
7
+ return (params.ignorePatterns || []).map(ignored => new RegExp(ignored, "i"));
8
+ }
9
+ static violatesRule(name, pattern, params, ignorePatterns) {
6
10
  if (params.ignoreNames && params.ignoreNames.indexOf(name.toUpperCase()) >= 0) {
7
11
  return false;
8
12
  }
9
- for (const ignored of params.ignorePatterns || []) {
10
- if (new RegExp(ignored, "i").test(name)) {
13
+ for (const ignored of ignorePatterns) {
14
+ if (ignored.test(name)) {
11
15
  return false;
12
16
  }
13
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.120.74",
3
+ "version": "2.120.76",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",