@abaplint/core 2.120.73 → 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.
- package/build/abaplint.d.ts +4 -0
- package/build/src/abap/2_statements/expressions/method_param_optional.js +3 -1
- package/build/src/abap/5_syntax/basic_types.js +67 -23
- package/build/src/abap/types/method_definition.js +22 -0
- package/build/src/abap/types/method_parameters.js +57 -0
- package/build/src/objects/data_definition.js +9 -1
- package/build/src/registry.js +1 -1
- package/build/src/rules/no_exclamation_escape.js +32 -2
- package/package.json +1 -1
package/build/abaplint.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
896
|
-
|
|
897
|
-
this.input.
|
|
898
|
-
return
|
|
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
|
-
|
|
901
|
-
|
|
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
|
|
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
|
-
|
|
913
|
-
|
|
914
|
-
this.input.
|
|
915
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/build/src/registry.js
CHANGED
|
@@ -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
|
-
|
|
24
|
-
|
|
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
|
}
|