@abaplint/core 2.116.1 → 2.117.1
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/sql_into_list.js +2 -1
- package/build/src/abap/5_syntax/expressions/sql_compare.js +7 -7
- package/build/src/objects/_dynpros.js +10 -1
- package/build/src/objects/program.js +10 -0
- package/build/src/registry.js +1 -1
- package/build/src/rules/dynpro_checks.js +35 -1
- package/build/src/rules/index.js +1 -0
- package/build/src/rules/selection_screen_texts_missing.js +79 -0
- package/package.json +3 -3
package/build/abaplint.d.ts
CHANGED
|
@@ -1993,6 +1993,9 @@ declare type DynproField = {
|
|
|
1993
1993
|
name: string;
|
|
1994
1994
|
type: string;
|
|
1995
1995
|
length: number;
|
|
1996
|
+
line: number;
|
|
1997
|
+
column: number;
|
|
1998
|
+
height: number;
|
|
1996
1999
|
};
|
|
1997
2000
|
|
|
1998
2001
|
declare type DynproHeader = {
|
|
@@ -5534,6 +5537,7 @@ declare class Program extends ABAPObject {
|
|
|
5534
5537
|
isInclude(): boolean;
|
|
5535
5538
|
isModulePool(): boolean;
|
|
5536
5539
|
getDynpros(): DynproList;
|
|
5540
|
+
getSelectionTexts(): ITextElements;
|
|
5537
5541
|
private parseXML;
|
|
5538
5542
|
}
|
|
5539
5543
|
|
|
@@ -5,9 +5,10 @@ const combi_1 = require("../combi");
|
|
|
5
5
|
const _1 = require(".");
|
|
6
6
|
const wparen_leftw_1 = require("../../1_lexer/tokens/wparen_leftw");
|
|
7
7
|
const wparen_left_1 = require("../../1_lexer/tokens/wparen_left");
|
|
8
|
+
const version_1 = require("../../../version");
|
|
8
9
|
class SQLIntoList extends combi_1.Expression {
|
|
9
10
|
getRunnable() {
|
|
10
|
-
const intoList = (0, combi_1.seq)((0, combi_1.altPrio)((0, combi_1.tok)(wparen_left_1.WParenLeft), (0, combi_1.tok)(wparen_leftw_1.WParenLeftW)), (0, combi_1.starPrio)((0, combi_1.seq)(_1.SQLTarget, ",")), _1.SQLTarget, ")");
|
|
11
|
+
const intoList = (0, combi_1.seq)((0, combi_1.altPrio)((0, combi_1.tok)(wparen_left_1.WParenLeft), (0, combi_1.ver)(version_1.Version.v740sp02, (0, combi_1.tok)(wparen_leftw_1.WParenLeftW))), (0, combi_1.starPrio)((0, combi_1.seq)(_1.SQLTarget, ",")), _1.SQLTarget, ")");
|
|
11
12
|
return (0, combi_1.seq)("INTO", intoList);
|
|
12
13
|
}
|
|
13
14
|
}
|
|
@@ -33,36 +33,36 @@ class SQLCompare {
|
|
|
33
33
|
if (sqlin) {
|
|
34
34
|
sql_in_1.SQLIn.runSyntax(sqlin, input);
|
|
35
35
|
}
|
|
36
|
-
const fieldName = (_b = node.findDirectExpression(Expressions.SQLFieldName)) === null || _b === void 0 ? void 0 : _b.concatTokens();
|
|
36
|
+
const fieldName = (_b = node.findDirectExpression(Expressions.SQLFieldName)) === null || _b === void 0 ? void 0 : _b.concatTokens().toUpperCase();
|
|
37
37
|
if (fieldName && sourceType && token) {
|
|
38
38
|
// check compatibility for rule sql_value_conversion
|
|
39
39
|
const targetType = this.findType(fieldName, tables, input.scope);
|
|
40
40
|
let message = "";
|
|
41
41
|
if (sourceType instanceof basic_1.IntegerType
|
|
42
42
|
&& targetType instanceof basic_1.CharacterType) {
|
|
43
|
-
message =
|
|
43
|
+
message = `${fieldName}: Integer to CHAR conversion`;
|
|
44
44
|
}
|
|
45
45
|
else if (sourceType instanceof basic_1.IntegerType
|
|
46
46
|
&& targetType instanceof basic_1.NumericType) {
|
|
47
|
-
message =
|
|
47
|
+
message = `${fieldName}: Integer to NUMC conversion`;
|
|
48
48
|
}
|
|
49
49
|
else if (sourceType instanceof basic_1.NumericType
|
|
50
50
|
&& targetType instanceof basic_1.IntegerType) {
|
|
51
|
-
message =
|
|
51
|
+
message = `${fieldName}: NUMC to Integer conversion`;
|
|
52
52
|
}
|
|
53
53
|
else if (sourceType instanceof basic_1.CharacterType
|
|
54
54
|
&& targetType instanceof basic_1.IntegerType) {
|
|
55
|
-
message =
|
|
55
|
+
message = `${fieldName}: CHAR to Integer conversion`;
|
|
56
56
|
}
|
|
57
57
|
else if (sourceType instanceof basic_1.CharacterType
|
|
58
58
|
&& targetType instanceof basic_1.CharacterType
|
|
59
59
|
&& sourceType.getLength() > targetType.getLength()) {
|
|
60
|
-
message =
|
|
60
|
+
message = `${fieldName}: Source field longer than database field, CHAR -> CHAR`;
|
|
61
61
|
}
|
|
62
62
|
else if (sourceType instanceof basic_1.NumericType
|
|
63
63
|
&& targetType instanceof basic_1.NumericType
|
|
64
64
|
&& sourceType.getLength() > targetType.getLength()) {
|
|
65
|
-
message =
|
|
65
|
+
message = `${fieldName}: Source field longer than database field, NUMC -> NUMC`;
|
|
66
66
|
}
|
|
67
67
|
if (message !== "") {
|
|
68
68
|
input.scope.addSQLConversion(fieldName, message, token);
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseDynpros = parseDynpros;
|
|
4
4
|
const xml_utils_1 = require("../xml_utils");
|
|
5
|
+
function parseNumber(value) {
|
|
6
|
+
if (value === undefined) {
|
|
7
|
+
return 0;
|
|
8
|
+
}
|
|
9
|
+
return parseInt(value, 10);
|
|
10
|
+
}
|
|
5
11
|
function parseDynpros(parsed) {
|
|
6
12
|
var _a, _b, _c, _d;
|
|
7
13
|
const dynpros = [];
|
|
@@ -13,7 +19,10 @@ function parseDynpros(parsed) {
|
|
|
13
19
|
fields.push({
|
|
14
20
|
name: f.NAME,
|
|
15
21
|
type: f.TYPE,
|
|
16
|
-
length: f.LENGTH,
|
|
22
|
+
length: parseNumber(f.LENGTH),
|
|
23
|
+
line: parseNumber(f.LINE),
|
|
24
|
+
column: parseNumber(f.COLUMN),
|
|
25
|
+
height: parseNumber(f.HEIGHT),
|
|
17
26
|
});
|
|
18
27
|
}
|
|
19
28
|
dynpros.push({
|
|
@@ -42,6 +42,10 @@ class Program extends _abap_object_1.ABAPObject {
|
|
|
42
42
|
this.parseXML();
|
|
43
43
|
return this.parsedXML.dynpros || [];
|
|
44
44
|
}
|
|
45
|
+
getSelectionTexts() {
|
|
46
|
+
this.parseXML();
|
|
47
|
+
return this.parsedXML.selectionTexts;
|
|
48
|
+
}
|
|
45
49
|
////////////////////////////
|
|
46
50
|
parseXML() {
|
|
47
51
|
var _a, _b, _c;
|
|
@@ -56,14 +60,19 @@ class Program extends _abap_object_1.ABAPObject {
|
|
|
56
60
|
isModulePool: false,
|
|
57
61
|
description: undefined,
|
|
58
62
|
dynpros: [],
|
|
63
|
+
selectionTexts: {},
|
|
59
64
|
};
|
|
60
65
|
return;
|
|
61
66
|
}
|
|
62
67
|
let description = "";
|
|
68
|
+
const selectionTexts = {};
|
|
63
69
|
for (const t of (0, xml_utils_1.xmlToArray)((_c = (_b = (_a = parsed.abapGit) === null || _a === void 0 ? void 0 : _a["asx:abap"]["asx:values"]) === null || _b === void 0 ? void 0 : _b.TPOOL) === null || _c === void 0 ? void 0 : _c.item)) {
|
|
64
70
|
if ((t === null || t === void 0 ? void 0 : t.ID) === "R") {
|
|
65
71
|
description = t.ENTRY ? (0, xml_utils_1.unescape)(t.ENTRY) : "";
|
|
66
72
|
}
|
|
73
|
+
else if ((t === null || t === void 0 ? void 0 : t.ID) === "S" && t.KEY !== undefined) {
|
|
74
|
+
selectionTexts[t.KEY.toUpperCase()] = t.ENTRY ? (0, xml_utils_1.unescape)(t.ENTRY) : "";
|
|
75
|
+
}
|
|
67
76
|
}
|
|
68
77
|
const dynpros = (0, _dynpros_1.parseDynpros)(parsed);
|
|
69
78
|
this.parsedXML = {
|
|
@@ -71,6 +80,7 @@ class Program extends _abap_object_1.ABAPObject {
|
|
|
71
80
|
isModulePool: file ? file.getRaw().includes("<SUBC>M</SUBC>") : false,
|
|
72
81
|
dynpros: dynpros,
|
|
73
82
|
description: description,
|
|
83
|
+
selectionTexts: selectionTexts,
|
|
74
84
|
};
|
|
75
85
|
}
|
|
76
86
|
}
|
package/build/src/registry.js
CHANGED
|
@@ -18,7 +18,7 @@ class DynproChecks {
|
|
|
18
18
|
key: "dynpro_checks",
|
|
19
19
|
title: "Dynpro Checks",
|
|
20
20
|
shortDescription: `Various Dynpro checks`,
|
|
21
|
-
extendedInformation: `* Check length of PUSH elements less than 132`,
|
|
21
|
+
extendedInformation: `* Check length of PUSH elements less than 132\n* Check for overlapping screen elements`,
|
|
22
22
|
tags: [_irule_1.RuleTag.Syntax],
|
|
23
23
|
};
|
|
24
24
|
}
|
|
@@ -47,9 +47,43 @@ class DynproChecks {
|
|
|
47
47
|
ret.push(issue_1.Issue.atPosition(file, new position_1.Position(1, 1), message, this.getMetadata().key, this.getConfig().severity));
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
ret.push(...this.findOverlappingFields(dynpro, file));
|
|
50
51
|
}
|
|
51
52
|
return ret;
|
|
52
53
|
}
|
|
54
|
+
findOverlappingFields(dynpro, file) {
|
|
55
|
+
const ret = [];
|
|
56
|
+
for (let index = 0; index < dynpro.fields.length; index++) {
|
|
57
|
+
const current = dynpro.fields[index];
|
|
58
|
+
if (current.name === undefined) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
for (let compare = index + 1; compare < dynpro.fields.length; compare++) {
|
|
62
|
+
const other = dynpro.fields[compare];
|
|
63
|
+
if (other.name === undefined || this.overlaps(current, other) === false) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const message = `Screen ${dynpro.number}, fields ${current.name} and ${other.name} are overlapping`;
|
|
67
|
+
ret.push(issue_1.Issue.atPosition(file, new position_1.Position(1, 1), message, this.getMetadata().key, this.getConfig().severity));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return ret;
|
|
71
|
+
}
|
|
72
|
+
overlaps(first, second) {
|
|
73
|
+
if (first.line === 0 || second.line === 0 || first.column === 0 || second.column === 0) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const firstHeight = Math.max(first.height, 1);
|
|
77
|
+
const secondHeight = Math.max(second.height, 1);
|
|
78
|
+
const firstLastLine = first.line + firstHeight - 1;
|
|
79
|
+
const secondLastLine = second.line + secondHeight - 1;
|
|
80
|
+
if (firstLastLine < second.line || secondLastLine < first.line) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const firstLastColumn = first.column + Math.max(first.length, 1) - 1;
|
|
84
|
+
const secondLastColumn = second.column + Math.max(second.length, 1) - 1;
|
|
85
|
+
return first.column <= secondLastColumn && second.column <= firstLastColumn;
|
|
86
|
+
}
|
|
53
87
|
}
|
|
54
88
|
exports.DynproChecks = DynproChecks;
|
|
55
89
|
//# sourceMappingURL=dynpro_checks.js.map
|
package/build/src/rules/index.js
CHANGED
|
@@ -152,6 +152,7 @@ __exportStar(require("./select_add_order_by"), exports);
|
|
|
152
152
|
__exportStar(require("./select_performance"), exports);
|
|
153
153
|
__exportStar(require("./select_single_full_key"), exports);
|
|
154
154
|
__exportStar(require("./selection_screen_naming"), exports);
|
|
155
|
+
__exportStar(require("./selection_screen_texts_missing"), exports);
|
|
155
156
|
__exportStar(require("./sequential_blank"), exports);
|
|
156
157
|
__exportStar(require("./short_case"), exports);
|
|
157
158
|
__exportStar(require("./sicf_consistency"), exports);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SelectionScreenTextsMissing = exports.SelectionScreenTextsMissingConf = void 0;
|
|
4
|
+
const issue_1 = require("../issue");
|
|
5
|
+
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
6
|
+
const statements_1 = require("../abap/2_statements/statements");
|
|
7
|
+
const expressions_1 = require("../abap/2_statements/expressions");
|
|
8
|
+
const objects_1 = require("../objects");
|
|
9
|
+
class SelectionScreenTextsMissingConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
10
|
+
}
|
|
11
|
+
exports.SelectionScreenTextsMissingConf = SelectionScreenTextsMissingConf;
|
|
12
|
+
class SelectionScreenTextsMissing {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.conf = new SelectionScreenTextsMissingConf();
|
|
15
|
+
}
|
|
16
|
+
getMetadata() {
|
|
17
|
+
return {
|
|
18
|
+
key: "selection_screen_texts_missing",
|
|
19
|
+
title: "Selection screen texts missing",
|
|
20
|
+
shortDescription: `Checks that selection screen parameters and select-options have selection texts`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
getConfig() {
|
|
24
|
+
return this.conf;
|
|
25
|
+
}
|
|
26
|
+
setConfig(conf) {
|
|
27
|
+
this.conf = conf;
|
|
28
|
+
}
|
|
29
|
+
initialize(reg) {
|
|
30
|
+
this.reg = reg;
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
run(obj) {
|
|
34
|
+
if (!(obj instanceof objects_1.Program)) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
if (obj.isInclude()) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
const selTexts = obj.getSelectionTexts();
|
|
41
|
+
const output = [];
|
|
42
|
+
const checked = new Set();
|
|
43
|
+
this.checkFile(obj.getMainABAPFile(), selTexts, output, checked);
|
|
44
|
+
return output;
|
|
45
|
+
}
|
|
46
|
+
checkFile(file, selTexts, output, checked) {
|
|
47
|
+
if (file === undefined) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (checked.has(file.getFilename())) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
checked.add(file.getFilename());
|
|
54
|
+
for (const stat of file.getStatements()) {
|
|
55
|
+
const s = stat.get();
|
|
56
|
+
if (s instanceof statements_1.Parameter || s instanceof statements_1.SelectOption) {
|
|
57
|
+
const fieldNode = stat.findFirstExpression(expressions_1.FieldSub);
|
|
58
|
+
if (fieldNode) {
|
|
59
|
+
const fieldName = fieldNode.getFirstToken().getStr().toUpperCase();
|
|
60
|
+
if (selTexts[fieldName] === undefined) {
|
|
61
|
+
output.push(issue_1.Issue.atToken(file, fieldNode.getFirstToken(), `Selection text missing for "${fieldName}"`, this.getMetadata().key, this.conf.severity));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (s instanceof statements_1.Include) {
|
|
66
|
+
const nameNode = stat.findFirstExpression(expressions_1.IncludeName);
|
|
67
|
+
if (nameNode) {
|
|
68
|
+
const inclName = nameNode.getFirstToken().getStr().toUpperCase();
|
|
69
|
+
const inclObj = this.reg.getObject("PROG", inclName);
|
|
70
|
+
if (inclObj) {
|
|
71
|
+
this.checkFile(inclObj.getMainABAPFile(), selTexts, output, checked);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.SelectionScreenTextsMissing = SelectionScreenTextsMissing;
|
|
79
|
+
//# sourceMappingURL=selection_screen_texts_missing.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abaplint/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.117.1",
|
|
4
4
|
"description": "abaplint - Core API",
|
|
5
5
|
"main": "build/src/index.js",
|
|
6
6
|
"typings": "build/abaplint.d.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"url": "git+https://github.com/abaplint/abaplint.git"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
|
40
|
-
"node": ">=
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
43
43
|
"ABAP",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"typescript": "^5.9.3"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"fast-xml-parser": "^5.5.
|
|
66
|
+
"fast-xml-parser": "^5.5.5",
|
|
67
67
|
"json5": "^2.2.3",
|
|
68
68
|
"vscode-languageserver-types": "^3.17.5"
|
|
69
69
|
}
|