@abaplint/core 2.115.2 → 2.115.4
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/src/abap/2_statements/statements/data_begin.js +1 -1
- package/build/src/abap/2_statements/statements/write.js +2 -1
- package/build/src/abap/5_syntax/statements/insert_internal.js +9 -4
- package/build/src/ddic.js +21 -0
- package/build/src/objects/rename/rename_icf_service.js +28 -4
- package/build/src/objects/rename/renamer.js +4 -1
- package/build/src/objects/rename/renamer_helper.js +28 -0
- package/build/src/registry.js +1 -1
- package/build/src/rules/many_parentheses.js +12 -10
- package/build/src/rules/obsolete_statement.js +10 -0
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ const combi_1 = require("../combi");
|
|
|
5
5
|
const expressions_1 = require("../expressions");
|
|
6
6
|
class DataBegin {
|
|
7
7
|
getMatcher() {
|
|
8
|
-
const occurs = (0, combi_1.seq)("OCCURS", expressions_1.Integer);
|
|
8
|
+
const occurs = (0, combi_1.seq)("OCCURS", (0, combi_1.altPrio)(expressions_1.Integer, expressions_1.FieldChain));
|
|
9
9
|
const common = (0, combi_1.seq)("COMMON PART", (0, combi_1.optPrio)(expressions_1.DefinitionName));
|
|
10
10
|
const structure = (0, combi_1.seq)("BEGIN OF", (0, combi_1.altPrio)(common, (0, combi_1.seq)(expressions_1.DefinitionName, (0, combi_1.optPrio)("READ-ONLY"), (0, combi_1.optPrio)(occurs))));
|
|
11
11
|
return (0, combi_1.seq)("DATA", structure);
|
|
@@ -12,7 +12,8 @@ class Write {
|
|
|
12
12
|
const as = (0, combi_1.seq)("AS", (0, combi_1.altPrio)("LINE", "ICON", "CHECKBOX", "SYMBOL"));
|
|
13
13
|
const to = (0, combi_1.seq)("TO", expressions_1.Target);
|
|
14
14
|
const options = (0, combi_1.per)(mask, to, (0, combi_1.seq)("EXPONENT", expressions_1.Source), "NO-GROUPING", "NO-ZERO", "CENTERED", (0, combi_1.seq)("INPUT", (0, combi_1.opt)(onOff)), "NO-GAP", "LEFT-JUSTIFIED", as, (0, combi_1.seq)("FRAMES", onOff), (0, combi_1.seq)("HOTSPOT", (0, combi_1.opt)(onOff)), "RIGHT-JUSTIFIED", (0, combi_1.seq)("TIME ZONE", expressions_1.Source), (0, combi_1.seq)("UNDER", expressions_1.Source), (0, combi_1.seq)("STYLE", expressions_1.Source), (0, combi_1.seq)("ROUND", expressions_1.Source), (0, combi_1.seq)("QUICKINFO", expressions_1.Source), "ENVIRONMENT TIME FORMAT", dateFormat, (0, combi_1.seq)("UNIT", expressions_1.Source), (0, combi_1.seq)("INTENSIFIED", (0, combi_1.opt)(onOff)), (0, combi_1.seq)("INDEX", expressions_1.Source), (0, combi_1.seq)("DECIMALS", expressions_1.Source), (0, combi_1.seq)("INVERSE", (0, combi_1.opt)(onOff)), expressions_1.Color, (0, combi_1.seq)("CURRENCY", expressions_1.Source), "RESET", "NO-SIGN");
|
|
15
|
-
|
|
15
|
+
// Need to refactor all this sometime,
|
|
16
|
+
const ret = (0, combi_1.seq)("WRITE", (0, combi_1.alt)((0, combi_1.seq)("AT /", (0, combi_1.opt)(expressions_1.Source), (0, combi_1.opt)("NO-GAP")), (0, combi_1.seq)((0, combi_1.opt)(expressions_1.WriteOffsetLength), (0, combi_1.alt)(expressions_1.Source, expressions_1.Dynamic, "/"), (0, combi_1.opt)(options))));
|
|
16
17
|
return (0, combi_1.verNot)(version_1.Version.Cloud, ret);
|
|
17
18
|
}
|
|
18
19
|
}
|
|
@@ -55,13 +55,18 @@ class InsertInternal {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
if (node.findDirectTokenByText("INITIAL") === undefined) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
let error = false;
|
|
59
|
+
if (sourceType instanceof basic_1.IntegerType && targetType instanceof basic_1.Integer8Type) {
|
|
60
|
+
error = true;
|
|
61
|
+
}
|
|
62
|
+
else if (new _type_utils_1.TypeUtils(input.scope).isAssignable(sourceType, targetType) === false) {
|
|
63
|
+
error = true;
|
|
62
64
|
}
|
|
63
65
|
else if (sourceType instanceof basic_1.CharacterType && targetType instanceof basic_1.StringType) {
|
|
64
66
|
// yea, well, INSERT doesnt convert the values automatically, like everything else?
|
|
67
|
+
error = true;
|
|
68
|
+
}
|
|
69
|
+
if (error === true) {
|
|
65
70
|
const message = "Types not compatible";
|
|
66
71
|
input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
|
|
67
72
|
return;
|
package/build/src/ddic.js
CHANGED
|
@@ -50,8 +50,22 @@ class DDIC {
|
|
|
50
50
|
case "XSTRING":
|
|
51
51
|
return Types.XStringType.get({ qualifiedName: qualifiedName || name });
|
|
52
52
|
case "D":
|
|
53
|
+
/*
|
|
54
|
+
if (length && length > 0) {
|
|
55
|
+
throw new Error("Length for type D not possible");
|
|
56
|
+
} else if (decimals && decimals > 0) {
|
|
57
|
+
throw new Error("Decimals for type D not possible");
|
|
58
|
+
}
|
|
59
|
+
*/
|
|
53
60
|
return new Types.DateType({ qualifiedName: qualifiedName || name });
|
|
54
61
|
case "T":
|
|
62
|
+
/*
|
|
63
|
+
if (length && length > 0) {
|
|
64
|
+
throw new Error("Length for type T not possible");
|
|
65
|
+
} else if (decimals && decimals > 0) {
|
|
66
|
+
throw new Error("Decimals for type T not possible");
|
|
67
|
+
}
|
|
68
|
+
*/
|
|
55
69
|
return new Types.TimeType({ qualifiedName: qualifiedName || name });
|
|
56
70
|
case "XSEQUENCE":
|
|
57
71
|
return new Types.XSequenceType({ qualifiedName: qualifiedName });
|
|
@@ -84,6 +98,13 @@ class DDIC {
|
|
|
84
98
|
case "INT8": // todo, take version into account
|
|
85
99
|
return new Types.Integer8Type({ qualifiedName: qualifiedName || name });
|
|
86
100
|
case "F":
|
|
101
|
+
/*
|
|
102
|
+
if (length && length > 0) {
|
|
103
|
+
throw new Error("Length for type F not possible");
|
|
104
|
+
} else if (decimals && decimals > 0) {
|
|
105
|
+
throw new Error("Decimals for type F not possible");
|
|
106
|
+
}
|
|
107
|
+
*/
|
|
87
108
|
return new Types.FloatType({ qualifiedName: qualifiedName || name });
|
|
88
109
|
case "P":
|
|
89
110
|
if (length && decimals) {
|
|
@@ -8,15 +8,39 @@ class RenameICFService {
|
|
|
8
8
|
this.reg = reg;
|
|
9
9
|
}
|
|
10
10
|
buildEdits(obj, oldName, newName) {
|
|
11
|
+
var _a, _b, _c, _d;
|
|
11
12
|
if (!(obj instanceof __1.ICFService)) {
|
|
12
13
|
throw new Error("RenameICFService, not a ICF Service");
|
|
13
14
|
}
|
|
15
|
+
// Preserve GUID suffix from the stored object/file name for the filename rename
|
|
16
|
+
// SICF files follow pattern: servicename.sicf or servicename {GUID}.sicf
|
|
17
|
+
const fileNewName = (() => {
|
|
18
|
+
// Look for pattern: space + GUID (32 hex chars in braces) + optional extension
|
|
19
|
+
const guidPattern = / \{[0-9A-Fa-f]{16,32}\}/;
|
|
20
|
+
const match = oldName.match(guidPattern);
|
|
21
|
+
if (match) {
|
|
22
|
+
// Extract everything from the GUID onwards (includes .sicf extension if present)
|
|
23
|
+
const guidIndex = match.index;
|
|
24
|
+
const suffix = oldName.substring(guidIndex);
|
|
25
|
+
// Only append suffix if newName doesn't already contain it
|
|
26
|
+
return newName.includes(suffix) ? newName : newName + suffix;
|
|
27
|
+
}
|
|
28
|
+
// Fallback: preserve any suffix after first space (legacy behavior)
|
|
29
|
+
const space = oldName.indexOf(" ");
|
|
30
|
+
if (space > -1) {
|
|
31
|
+
const suffix = oldName.substring(space);
|
|
32
|
+
return newName.includes(suffix) ? newName : newName + suffix;
|
|
33
|
+
}
|
|
34
|
+
return newName;
|
|
35
|
+
})();
|
|
36
|
+
const cleanOldName = (_b = (_a = oldName.match(/^[^ ]+/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : oldName;
|
|
37
|
+
const cleanNewName = (_d = (_c = newName.match(/^[^ ]+/)) === null || _c === void 0 ? void 0 : _c[0]) !== null && _d !== void 0 ? _d : newName;
|
|
14
38
|
let changes = [];
|
|
15
39
|
const helper = new renamer_helper_1.RenamerHelper(this.reg);
|
|
16
|
-
changes = changes.concat(helper.
|
|
17
|
-
changes = changes.concat(helper.buildXMLFileEdits(obj, "ICF_NAME",
|
|
18
|
-
changes = changes.concat(helper.buildXMLFileEdits(obj, "ORIG_NAME",
|
|
19
|
-
changes = changes.concat(helper.renameFiles(obj, oldName,
|
|
40
|
+
changes = changes.concat(helper.buildURLFileEdits(obj, cleanOldName, cleanNewName));
|
|
41
|
+
changes = changes.concat(helper.buildXMLFileEdits(obj, "ICF_NAME", cleanOldName, cleanNewName));
|
|
42
|
+
changes = changes.concat(helper.buildXMLFileEdits(obj, "ORIG_NAME", cleanOldName, cleanNewName, true));
|
|
43
|
+
changes = changes.concat(helper.renameFiles(obj, oldName, fileNewName));
|
|
20
44
|
return {
|
|
21
45
|
documentChanges: changes,
|
|
22
46
|
};
|
|
@@ -32,7 +32,10 @@ class Renamer {
|
|
|
32
32
|
/** Builds edits, but does not apply to registry, used by LSP */
|
|
33
33
|
buildEdits(type, oldName, newName) {
|
|
34
34
|
this.reg.parse(); // the registry must be parsed to dermine references
|
|
35
|
-
|
|
35
|
+
let obj = this.reg.getObject(type, oldName);
|
|
36
|
+
if (obj === undefined && type === "SICF") {
|
|
37
|
+
obj = Array.from(this.reg.getObjects()).find(o => o.getType() === "SICF" && o.getName().toUpperCase().startsWith(oldName.toUpperCase() + " "));
|
|
38
|
+
}
|
|
36
39
|
if (obj === undefined) {
|
|
37
40
|
throw new Error("rename, object not found");
|
|
38
41
|
}
|
|
@@ -123,6 +123,34 @@ class RenamerHelper {
|
|
|
123
123
|
}
|
|
124
124
|
return changes;
|
|
125
125
|
}
|
|
126
|
+
buildURLFileEdits(object, oldName, newName) {
|
|
127
|
+
const changes = [];
|
|
128
|
+
const xml = object.getXMLFile();
|
|
129
|
+
if (xml === undefined) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const oldNameLower = oldName.toLowerCase();
|
|
133
|
+
const newNameLower = newName.toLowerCase();
|
|
134
|
+
const rows = xml.getRawRows();
|
|
135
|
+
for (let i = 0; i < rows.length; i++) {
|
|
136
|
+
const row = rows[i];
|
|
137
|
+
const urlTagStart = row.indexOf("<URL>");
|
|
138
|
+
if (urlTagStart === -1) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const urlTagEnd = row.indexOf("</URL>");
|
|
142
|
+
if (urlTagEnd === -1) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const urlContent = row.substring(urlTagStart + 5, urlTagEnd);
|
|
146
|
+
const updatedUrl = urlContent.replace(oldNameLower, newNameLower);
|
|
147
|
+
if (updatedUrl !== urlContent) {
|
|
148
|
+
const range = vscode_languageserver_types_1.Range.create(i, urlTagStart + 5, i, urlTagEnd);
|
|
149
|
+
changes.push(vscode_languageserver_types_1.TextDocumentEdit.create({ uri: xml.getFilename(), version: 1 }, [vscode_languageserver_types_1.TextEdit.replace(range, updatedUrl)]));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return changes;
|
|
153
|
+
}
|
|
126
154
|
renameFiles(obj, oldName, name) {
|
|
127
155
|
const list = [];
|
|
128
156
|
const newName = name.toLowerCase().replace(/\//g, "#");
|
package/build/src/registry.js
CHANGED
|
@@ -50,7 +50,7 @@ ENDIF.
|
|
|
50
50
|
return [];
|
|
51
51
|
}
|
|
52
52
|
for (const cond of structure.findAllExpressionsMulti([Expressions.Cond, Expressions.ComponentCond])) {
|
|
53
|
-
issues.push(...this.
|
|
53
|
+
issues.push(...this.analyzeCondition(file, cond));
|
|
54
54
|
}
|
|
55
55
|
for (const sub of structure.findAllExpressionsMulti([Expressions.CondSub, Expressions.ComponentCondSub])) {
|
|
56
56
|
let cond = [];
|
|
@@ -104,18 +104,20 @@ ENDIF.
|
|
|
104
104
|
}
|
|
105
105
|
analyzeMove(file, m) {
|
|
106
106
|
const issues = [];
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
for (const source of m.findAllExpressionsRecursive(Expressions.Source)) {
|
|
108
|
+
const lastChildren = source.getChildren();
|
|
109
|
+
if (lastChildren.length === 3
|
|
110
|
+
&& lastChildren[0].getFirstToken().getStr() === "("
|
|
111
|
+
&& lastChildren[1].getChildren().length === 1
|
|
112
|
+
&& lastChildren[1].getFirstToken().getStr().startsWith("-") === false
|
|
113
|
+
&& lastChildren[2].getFirstToken().getStr() === ")") {
|
|
114
|
+
const issue = issue_1.Issue.atToken(file, lastChildren[0].getFirstToken(), "Too many parentheses", this.getMetadata().key, this.conf.severity);
|
|
115
|
+
issues.push(issue);
|
|
116
|
+
}
|
|
115
117
|
}
|
|
116
118
|
return issues;
|
|
117
119
|
}
|
|
118
|
-
|
|
120
|
+
analyzeCondition(file, cond) {
|
|
119
121
|
var _a, _b;
|
|
120
122
|
const issues = [];
|
|
121
123
|
let comparator = "";
|
|
@@ -77,6 +77,8 @@ class ObsoleteStatementConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
|
77
77
|
this.fieldGroups = true;
|
|
78
78
|
/** Check for REPLACE INTO */
|
|
79
79
|
this.replaceInto = true;
|
|
80
|
+
this.loopExtract = true;
|
|
81
|
+
this.sortExtract = true;
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
exports.ObsoleteStatementConf = ObsoleteStatementConf;
|
|
@@ -330,6 +332,14 @@ ENDIF.`,
|
|
|
330
332
|
issues.push(issue);
|
|
331
333
|
}
|
|
332
334
|
}
|
|
335
|
+
if (this.conf.loopExtract && sta instanceof Statements.LoopExtract) {
|
|
336
|
+
const issue = issue_1.Issue.atStatement(file, staNode, "LOOP extract", this.getMetadata().key, this.conf.severity);
|
|
337
|
+
issues.push(issue);
|
|
338
|
+
}
|
|
339
|
+
if (this.conf.sortExtract && sta instanceof Statements.SortDataset && staNode.getChildren().length === 2) {
|
|
340
|
+
const issue = issue_1.Issue.atStatement(file, staNode, "SORT extract", this.getMetadata().key, this.conf.severity);
|
|
341
|
+
issues.push(issue);
|
|
342
|
+
}
|
|
333
343
|
if (configVersion >= version_1.Version.v754 && this.conf.clientSpecified
|
|
334
344
|
&& (sta instanceof Statements.Select
|
|
335
345
|
|| sta instanceof Statements.SelectLoop
|