@abaplint/core 2.120.72 → 2.120.74

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.
@@ -1723,6 +1723,8 @@ declare class DataControl extends AbstractObject {
1723
1723
  declare class DataDefinition extends AbstractObject {
1724
1724
  private parserError;
1725
1725
  private parsedData;
1726
+ private typeCache;
1727
+ private typeCacheData;
1726
1728
  getType(): string;
1727
1729
  getAllowedNaming(): {
1728
1730
  maxLength: number;
@@ -4770,6 +4772,7 @@ declare class MethodDefinition extends Identifier implements IMethodDefinition {
4770
4772
  private readonly exceptions;
4771
4773
  private readonly className;
4772
4774
  constructor(node: StatementNode, visibility: Visibility, input: SyntaxInput);
4775
+ private checkRaisingParameter;
4773
4776
  getVisibility(): Visibility;
4774
4777
  getClassName(): string;
4775
4778
  isRedefinition(): boolean;
@@ -4860,6 +4863,7 @@ declare class MethodParameters_2 implements IMethodParameters {
4860
4863
  private checkNameLengths;
4861
4864
  private workaroundRAP;
4862
4865
  private add;
4866
+ private checkKeywordNames;
4863
4867
  private isPassByValue;
4864
4868
  }
4865
4869
 
@@ -5,7 +5,9 @@ const combi_1 = require("../combi");
5
5
  const _1 = require(".");
6
6
  class MethodParamOptional extends combi_1.Expression {
7
7
  getRunnable() {
8
- return (0, combi_1.seq)(_1.MethodParam, (0, combi_1.optPrio)("OPTIONAL"));
8
+ // not optPrio, a parameter named "optional" can follow one with a DEFAULT value:
9
+ // "a TYPE i DEFAULT 5 optional TYPE i"
10
+ return (0, combi_1.seq)(_1.MethodParam, (0, combi_1.opt)("OPTIONAL"));
9
11
  }
10
12
  }
11
13
  exports.MethodParamOptional = MethodParamOptional;
@@ -42,6 +42,7 @@ const Types = __importStar(require("../types/basic"));
42
42
  const _scope_type_1 = require("./_scope_type");
43
43
  const _object_oriented_1 = require("./_object_oriented");
44
44
  const class_constant_1 = require("../types/class_constant");
45
+ const class_attribute_1 = require("../types/class_attribute");
45
46
  const identifier_1 = require("../1_lexer/tokens/identifier");
46
47
  const _reference_1 = require("./_reference");
47
48
  const basic_1 = require("../types/basic");
@@ -892,15 +893,18 @@ class BasicTypes {
892
893
  const firstName = firstToken.getStr();
893
894
  if (firstNode.get() instanceof Expressions.Field) {
894
895
  const found = this.input.scope.findVariable(firstName);
895
- const val = found === null || found === void 0 ? void 0 : found.getValue();
896
- if (typeof val === "string") {
897
- this.input.scope.addReference(firstToken, found, _reference_1.ReferenceType.DataReadReference, this.input.filename);
898
- return val;
896
+ if (found === undefined) {
897
+ const message = `"${firstName}" not found, findTop`;
898
+ this.input.issues.push((0, _syntax_input_1.syntaxIssue)(this.input, firstToken, message));
899
+ return undefined;
899
900
  }
900
- else if ((found === null || found === void 0 ? void 0 : found.getType()) instanceof basic_1.StructureType) {
901
- this.input.scope.addReference(firstToken, found, _reference_1.ReferenceType.DataReadReference, this.input.filename);
901
+ this.input.scope.addReference(firstToken, found, _reference_1.ReferenceType.DataReadReference, this.input.filename);
902
+ if (this.isConstant(found) === false) {
903
+ const message = `VALUE, ${expr.concatTokens().toLowerCase()} is not a constant`;
904
+ this.input.issues.push((0, _syntax_input_1.syntaxIssue)(this.input, firstToken, message));
905
+ return undefined;
902
906
  }
903
- return undefined;
907
+ return this.constantValue(expr, found.getValue());
904
908
  }
905
909
  else if (firstNode.get() instanceof Expressions.ClassName
906
910
  && firstName.toLowerCase() === this.input.scope.getName().toLowerCase()
@@ -909,12 +913,18 @@ class BasicTypes {
909
913
  const children = expr.getChildren();
910
914
  const token = (_a = children[2]) === null || _a === void 0 ? void 0 : _a.getFirstToken();
911
915
  const found = this.input.scope.findVariable(token.getStr());
912
- const val = found === null || found === void 0 ? void 0 : found.getValue();
913
- if (typeof val === "string") {
914
- this.input.scope.addReference(firstToken, found, _reference_1.ReferenceType.DataReadReference, this.input.filename);
915
- return val;
916
+ if (found === undefined) {
917
+ const message = `"${token.getStr()}" not found, findTop`;
918
+ this.input.issues.push((0, _syntax_input_1.syntaxIssue)(this.input, token, message));
919
+ return undefined;
916
920
  }
917
- return undefined;
921
+ this.input.scope.addReference(firstToken, found, _reference_1.ReferenceType.DataReadReference, this.input.filename);
922
+ if (this.isConstant(found) === false) {
923
+ const message = `VALUE, ${expr.concatTokens().toLowerCase()} is not a constant`;
924
+ this.input.issues.push((0, _syntax_input_1.syntaxIssue)(this.input, token, message));
925
+ return undefined;
926
+ }
927
+ return this.constantValue(expr, found.getValue());
918
928
  }
919
929
  else if (firstNode.get() instanceof Expressions.ClassName) {
920
930
  const obj = this.input.scope.findObjectDefinition(firstName);
@@ -937,24 +947,58 @@ class BasicTypes {
937
947
  if (c instanceof class_constant_1.ClassConstant) {
938
948
  this.input.scope.addReference(firstToken, obj, _reference_1.ReferenceType.ObjectOrientedReference, this.input.filename, { ooName: obj.getName() });
939
949
  this.input.scope.addReference(token, c, _reference_1.ReferenceType.DataReadReference, this.input.filename);
940
- const val = c.getValue();
941
- if (typeof val === "string") {
942
- return val;
943
- }
944
- else if (typeof val === "object" && children[4]) {
945
- const name = children[4].getFirstToken().getStr();
946
- if (val[name] !== undefined) {
947
- return val[name];
948
- }
949
- }
950
+ return this.constantValue(expr, c.getValue());
951
+ }
952
+ const helper = new _object_oriented_1.ObjectOriented(this.input.scope);
953
+ const attribute = helper.searchAttributeName(obj, attr);
954
+ if (attribute) {
955
+ this.input.scope.addReference(firstToken, obj, _reference_1.ReferenceType.ObjectOrientedReference, this.input.filename, { ooName: obj.getName() });
956
+ this.input.scope.addReference(token, attribute, _reference_1.ReferenceType.DataReadReference, this.input.filename);
957
+ const message = `VALUE, ${expr.concatTokens().toLowerCase()} is not a constant`;
958
+ this.input.issues.push((0, _syntax_input_1.syntaxIssue)(this.input, token, message));
950
959
  return undefined;
951
960
  }
952
- throw new Error("resolveConstantValue, constant not found " + attr);
961
+ const message = `Attribute or constant "${attr}" not found in "${obj.getName()}"`;
962
+ this.input.issues.push((0, _syntax_input_1.syntaxIssue)(this.input, token, message));
963
+ return undefined;
953
964
  }
954
965
  else {
955
966
  throw new Error("resolveConstantValue, unexpected structure");
956
967
  }
957
968
  }
969
+ isConstant(identifier) {
970
+ if (identifier instanceof class_attribute_1.ClassAttribute) {
971
+ return false;
972
+ }
973
+ const meta = identifier.getMeta();
974
+ if (meta.includes("read_only" /* IdentifierMeta.ReadOnly */) === false) {
975
+ return false;
976
+ }
977
+ if (meta.includes("static" /* IdentifierMeta.Static */)) {
978
+ return true;
979
+ }
980
+ // Built-in constants and built-in system fields share ReadOnly/BuiltIn metadata.
981
+ // Only the constants have a value; system fields such as SY-INDEX do not.
982
+ return meta.includes("built-in" /* IdentifierMeta.BuiltIn */) && identifier.getValue() !== undefined;
983
+ }
984
+ constantValue(expr, value) {
985
+ if (typeof value === "string") {
986
+ return value;
987
+ }
988
+ else if (typeof value === "object" && value !== null) {
989
+ let current = value;
990
+ for (const child of expr.getChildren().slice(1)) {
991
+ if (child instanceof nodes_1.ExpressionNode && child.get() instanceof Expressions.ComponentName) {
992
+ if (typeof current !== "object" || current === null) {
993
+ return undefined;
994
+ }
995
+ current = current[child.getFirstToken().getStr()];
996
+ }
997
+ }
998
+ return typeof current === "string" ? current : undefined;
999
+ }
1000
+ return undefined;
1001
+ }
958
1002
  resolveTypeRef(chain) {
959
1003
  var _a;
960
1004
  if (chain === undefined) {
@@ -39,6 +39,7 @@ const Expressions = __importStar(require("../2_statements/expressions"));
39
39
  const method_parameters_1 = require("./method_parameters");
40
40
  const _identifier_1 = require("../4_file_information/_identifier");
41
41
  const _reference_1 = require("../5_syntax/_reference");
42
+ const _syntax_input_1 = require("../5_syntax/_syntax_input");
42
43
  class MethodDefinition extends _identifier_1.Identifier {
43
44
  // todo: final flag
44
45
  constructor(node, visibility, input) {
@@ -80,6 +81,7 @@ class MethodDefinition extends _identifier_1.Identifier {
80
81
  this.static = true;
81
82
  }
82
83
  this.raising = [];
84
+ this.checkRaisingParameter(node, input);
83
85
  for (const r of ((_c = node.findDirectExpression(Expressions.MethodDefRaising)) === null || _c === void 0 ? void 0 : _c.findAllExpressions(Expressions.ClassName)) || []) {
84
86
  const token = r.getFirstToken();
85
87
  const name = token.getStr();
@@ -104,6 +106,26 @@ class MethodDefinition extends _identifier_1.Identifier {
104
106
  this.visibility = visibility;
105
107
  this.parameters = new method_parameters_1.MethodParameters(node, input, this.abstract);
106
108
  }
109
+ // A system reads a parameter named RAISING as the RAISING addition, measured on 758:
110
+ // "IMPORTING a TYPE i raising TYPE i" fails with 'Type "TYPE" is unknown'
111
+ checkRaisingParameter(node, input) {
112
+ const raising = node.findDirectExpression(Expressions.MethodDefRaising);
113
+ const first = raising === null || raising === void 0 ? void 0 : raising.getChildren()[1];
114
+ if (raising === undefined
115
+ || first === undefined
116
+ || !(first.get() instanceof Expressions.ClassName)
117
+ || ["TYPE", "LIKE"].includes(first.getFirstToken().getStr().toUpperCase()) === false) {
118
+ return;
119
+ }
120
+ if (node.findDirectExpression(Expressions.MethodDefImporting) === undefined
121
+ && node.findDirectExpression(Expressions.MethodDefExporting) === undefined
122
+ && node.findDirectExpression(Expressions.MethodDefChanging) === undefined) {
123
+ return;
124
+ }
125
+ const token = raising.getFirstToken();
126
+ const message = "Parameter \"" + token.getStr() + "\" is read as the RAISING addition, rename it or escape it as !" + token.getStr();
127
+ input.issues.push((0, _syntax_input_1.syntaxIssue)(input, token, message));
128
+ }
107
129
  getVisibility() {
108
130
  return this.visibility;
109
131
  }
@@ -47,6 +47,12 @@ const _reference_1 = require("../5_syntax/_reference");
47
47
  const identifier_1 = require("../1_lexer/tokens/identifier");
48
48
  const _scope_type_1 = require("../5_syntax/_scope_type");
49
49
  const _syntax_input_1 = require("../5_syntax/_syntax_input");
50
+ // the keywords that can follow a parameter in each section, a plain parameter name is read as them
51
+ const KEYWORDS_AFTER = {
52
+ "IMPORTING": ["PREFERRED", "EXPORTING", "CHANGING", "RETURNING", "RAISING", "EXCEPTIONS"],
53
+ "EXPORTING": ["CHANGING", "RETURNING", "RAISING", "EXCEPTIONS"],
54
+ "CHANGING": ["RETURNING", "RAISING", "EXCEPTIONS"],
55
+ };
50
56
  // todo:
51
57
  // this.exceptions = [];
52
58
  // also consider RAISING vs EXCEPTIONS
@@ -291,6 +297,7 @@ class MethodParameters {
291
297
  }
292
298
  add(target, source, input, meta, abstractMethod) {
293
299
  var _a;
300
+ this.checkKeywordNames(source, input);
294
301
  for (const opt of source.findAllExpressions(Expressions.MethodParamOptional)) {
295
302
  const p = opt.findDirectExpression(Expressions.MethodParam);
296
303
  if (p === undefined) {
@@ -331,6 +338,56 @@ class MethodParameters {
331
338
  target.push(method_param_1.MethodParam.runSyntax(param, input, [...meta, ...extraMeta]));
332
339
  }
333
340
  }
341
+ // A system reads a plain parameter name as a keyword wherever that keyword can stand, measured on 758:
342
+ // "val TYPE clike default TYPE i" makes default the DEFAULT addition of val, and then fails
343
+ // with "Unable to interpret ..." on what follows. "!default" and "VALUE(default)" are names
344
+ checkKeywordNames(section, input) {
345
+ var _a;
346
+ const following = KEYWORDS_AFTER[section.getFirstToken().getStr().toUpperCase()];
347
+ if (following === undefined) {
348
+ return;
349
+ }
350
+ // undefined: no parameter before in this section, true: the one before has OPTIONAL or DEFAULT
351
+ let previousHasAddition = undefined;
352
+ for (const child of section.getChildren()) {
353
+ if (!(child instanceof nodes_1.ExpressionNode)) {
354
+ continue;
355
+ }
356
+ let param = undefined;
357
+ let optional = false;
358
+ if (child.get() instanceof Expressions.MethodParamOptional) {
359
+ param = child.findDirectExpression(Expressions.MethodParam);
360
+ optional = child.findDirectTokenByText("OPTIONAL") !== undefined;
361
+ }
362
+ else if (child.get() instanceof Expressions.MethodParam) {
363
+ param = child;
364
+ }
365
+ else if (child.get() instanceof Expressions.MethodParamName) {
366
+ // "TYPE ANY STRUCTURE", not measured, so never reported after it
367
+ previousHasAddition = true;
368
+ continue;
369
+ }
370
+ if (param === undefined) {
371
+ continue;
372
+ }
373
+ const first = param.getFirstToken();
374
+ const name = first.getStr().toUpperCase();
375
+ if (((_a = param.getChildren()[0]) === null || _a === void 0 ? void 0 : _a.get()) instanceof Expressions.MethodParamName) {
376
+ let readAs = undefined;
377
+ if (following.includes(name)) {
378
+ readAs = "the " + (name === "PREFERRED" ? "PREFERRED PARAMETER" : name) + " addition";
379
+ }
380
+ else if ((name === "DEFAULT" || name === "OPTIONAL") && previousHasAddition === false) {
381
+ readAs = "the " + name + " addition of the parameter before it";
382
+ }
383
+ if (readAs !== undefined) {
384
+ const message = "Parameter \"" + first.getStr() + "\" is read as " + readAs + ", rename it or escape it as !" + first.getStr();
385
+ input.issues.push((0, _syntax_input_1.syntaxIssue)(input, first, message));
386
+ }
387
+ }
388
+ previousHasAddition = optional || param.findFirstExpression(Expressions.Default) !== undefined;
389
+ }
390
+ }
334
391
  isPassByValue(param) {
335
392
  var _a;
336
393
  // "!" is the identifier escape, generated code has eg. "IMPORTING !VALUE(iv_foo)"
@@ -11,6 +11,8 @@ class DataDefinition extends _abstract_object_1.AbstractObject {
11
11
  super(...arguments);
12
12
  this.parserError = undefined;
13
13
  this.parsedData = undefined;
14
+ this.typeCache = undefined;
15
+ this.typeCacheData = undefined;
14
16
  }
15
17
  getType() {
16
18
  return "DDLS";
@@ -38,7 +40,11 @@ class DataDefinition extends _abstract_object_1.AbstractObject {
38
40
  }
39
41
  parseType(reg) {
40
42
  this.parse();
41
- return new cds_determine_types_1.CDSDetermineTypes().parseType(reg, this.parsedData, this.getName());
43
+ if (this.typeCache === undefined || this.typeCacheData !== this.parsedData) {
44
+ this.typeCacheData = this.parsedData;
45
+ this.typeCache = new cds_determine_types_1.CDSDetermineTypes().parseType(reg, this.parsedData, this.getName());
46
+ }
47
+ return this.typeCache;
42
48
  }
43
49
  getParsedData() {
44
50
  return this.parsedData;
@@ -50,6 +56,8 @@ class DataDefinition extends _abstract_object_1.AbstractObject {
50
56
  }
51
57
  setDirty() {
52
58
  this.parsedData = undefined;
59
+ this.typeCache = undefined;
60
+ this.typeCacheData = undefined;
53
61
  this.parserError = undefined;
54
62
  super.setDirty();
55
63
  }
@@ -75,7 +75,7 @@ class Registry {
75
75
  }
76
76
  static abaplintVersion() {
77
77
  // magic, see build script "version.js"
78
- return "2.120.72";
78
+ return "2.120.74";
79
79
  }
80
80
  getDDICReferences() {
81
81
  return this.ddicReferences;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GenericRegex = exports.GenericRegexConf = void 0;
4
+ const issue_1 = require("../issue");
5
+ const _basic_rule_config_1 = require("./_basic_rule_config");
6
+ const _irule_1 = require("./_irule");
7
+ class GenericRegexConf extends _basic_rule_config_1.BasicRuleConfig {
8
+ constructor() {
9
+ super(...arguments);
10
+ /** List of forbidden regular expressions, case insensitive, checked against each line of every file.
11
+ * Patterns use JavaScript regex syntax without enclosing slashes or flags.
12
+ * @uniqueItems true
13
+ */
14
+ this.regexes = [];
15
+ }
16
+ }
17
+ exports.GenericRegexConf = GenericRegexConf;
18
+ class GenericRegex {
19
+ constructor() {
20
+ this.conf = new GenericRegexConf();
21
+ this.regexes = [];
22
+ }
23
+ getMetadata() {
24
+ return {
25
+ key: "generic_regex",
26
+ title: "Generic regex",
27
+ shortDescription: "Report an issue for each match of a configured regular expression.",
28
+ extendedInformation: `Checks each line of every object file, including comments, strings, and XML.
29
+ Patterns use JavaScript regular expression syntax without enclosing slashes or flags.
30
+ Matching is case insensitive and does not span multiple lines. Each non-overlapping match is reported.
31
+ Use the standard exclude configuration to skip files.
32
+
33
+ Example configuration: \`{"generic_regex": {"regexes": ["TODO", "BREAK-POINT"]}}\``,
34
+ tags: [_irule_1.RuleTag.SingleFile],
35
+ };
36
+ }
37
+ getConfig() {
38
+ return this.conf;
39
+ }
40
+ setConfig(conf) {
41
+ var _a;
42
+ this.conf = Object.assign(new GenericRegexConf(), conf);
43
+ this.conf.regexes = (_a = this.conf.regexes) !== null && _a !== void 0 ? _a : [];
44
+ }
45
+ initialize(_reg) {
46
+ this.regexes = this.conf.regexes.map(pattern => {
47
+ try {
48
+ return new RegExp(pattern, "gi");
49
+ }
50
+ catch (_a) {
51
+ throw new Error(`generic_regex: Invalid regular expression "${pattern}"`);
52
+ }
53
+ });
54
+ return this;
55
+ }
56
+ run(obj) {
57
+ const issues = [];
58
+ if (this.regexes.length === 0) {
59
+ return issues;
60
+ }
61
+ for (const file of obj.getFiles()) {
62
+ const rows = file.getRawRows();
63
+ for (let row = 0; row < rows.length; row++) {
64
+ const text = rows[row].replace(/\r$/, "");
65
+ for (const regex of this.regexes) {
66
+ regex.lastIndex = 0;
67
+ for (;;) {
68
+ const match = regex.exec(text);
69
+ if (match === null) {
70
+ break;
71
+ }
72
+ const startCol = match.index + 1;
73
+ const message = `Text matches forbidden regular expression "${regex.source}"`;
74
+ issues.push(issue_1.Issue.atRowRange(file, row + 1, startCol, startCol + match[0].length, message, this.getMetadata().key, this.conf.severity));
75
+ if (match[0].length === 0) {
76
+ regex.lastIndex++;
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return issues;
83
+ }
84
+ }
85
+ exports.GenericRegex = GenericRegex;
86
+ //# sourceMappingURL=generic_regex.js.map
@@ -82,6 +82,7 @@ __exportStar(require("./fully_type_constants"), exports);
82
82
  __exportStar(require("./fully_type_itabs"), exports);
83
83
  __exportStar(require("./function_module_recommendations"), exports);
84
84
  __exportStar(require("./functional_writing"), exports);
85
+ __exportStar(require("./generic_regex"), exports);
85
86
  __exportStar(require("./global_class"), exports);
86
87
  __exportStar(require("./identical_conditions"), exports);
87
88
  __exportStar(require("./identical_contents"), exports);
@@ -7,6 +7,31 @@ const _basic_rule_config_1 = require("./_basic_rule_config");
7
7
  const _irule_1 = require("./_irule");
8
8
  const edit_helper_1 = require("../edit_helper");
9
9
  const tokens_1 = require("../abap/1_lexer/tokens");
10
+ const artifacts_1 = require("../abap/artifacts");
11
+ const combi_1 = require("../abap/2_statements/combi");
12
+ let keywords = undefined;
13
+ /** every word of the statement and expression grammar, upper case */
14
+ function getKeywords() {
15
+ if (keywords === undefined) {
16
+ const list = [];
17
+ for (const stat of artifacts_1.ArtifactsABAP.getStatements()) {
18
+ list.push(...combi_1.Combi.listKeywords(stat.getMatcher()));
19
+ }
20
+ for (const expr of artifacts_1.ArtifactsABAP.getExpressions()) {
21
+ list.push(...combi_1.Combi.listKeywords(new expr().getRunnable()));
22
+ }
23
+ keywords = new Set();
24
+ for (const words of list) {
25
+ // a WordSequence lists eg. "PREFERRED PARAMETER" or "IS-INITIAL" as one entry
26
+ for (const word of words.replace(/-/g, " ").split(" ")) {
27
+ if (word !== "") {
28
+ keywords.add(word.toUpperCase());
29
+ }
30
+ }
31
+ }
32
+ }
33
+ return keywords;
34
+ }
10
35
  class NoExclamationEscapeConf extends _basic_rule_config_1.BasicRuleConfig {
11
36
  }
12
37
  exports.NoExclamationEscapeConf = NoExclamationEscapeConf;
@@ -20,8 +45,10 @@ class NoExclamationEscape extends _abap_rule_1.ABAPRule {
20
45
  key: "no_exclamation_escape",
21
46
  title: "No exclamation escape",
22
47
  shortDescription: `Detects and removes exclamation marks (!) used to escape identifiers`,
23
- // eslint-disable-next-line max-len
24
- extendedInformation: `Exclamation marks are not needed when the identifier is not a reserved keyword, or could be avoided by renaming.`,
48
+ extendedInformation: `Exclamation marks are not needed when the identifier is not a keyword, or could be avoided by renaming.
49
+
50
+ An identifier named like an ABAP keyword is allowed to be escaped, eg. a method parameter "!default",
51
+ which the system otherwise reads as the DEFAULT addition of the parameter before it.`,
25
52
  tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix],
26
53
  badExample: "methods CONVERT changing !CO_sdf type ref to ZCL_sdf optional.",
27
54
  goodExample: "methods CONVERT changing CO_sdf type ref to ZCL_sdf optional.",
@@ -40,6 +67,9 @@ class NoExclamationEscape extends _abap_rule_1.ABAPRule {
40
67
  const str = token.getStr();
41
68
  if (str.startsWith("!")) {
42
69
  const replacement = str.substring(1);
70
+ if (getKeywords().has(replacement.toUpperCase())) {
71
+ continue;
72
+ }
43
73
  const fix = edit_helper_1.EditHelper.replaceToken(file, token, replacement);
44
74
  issues.push(issue_1.Issue.atToken(file, token, "Do not use exclamation mark to escape identifiers", this.getMetadata().key, this.conf.severity, fix));
45
75
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/core",
3
- "version": "2.120.72",
3
+ "version": "2.120.74",
4
4
  "description": "abaplint - Core API",
5
5
  "main": "build/src/index.js",
6
6
  "typings": "build/abaplint.d.ts",