@abaplint/core 2.100.5 → 2.101.0
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 +7 -0
- package/build/src/abap/5_syntax/_current_scope.js +7 -0
- package/build/src/abap/5_syntax/expressions/database_table.js +2 -1
- package/build/src/abap/5_syntax/expressions/select.js +18 -8
- package/build/src/abap/5_syntax/expressions/sql_compare.js +63 -0
- package/build/src/abap/5_syntax/expressions/sql_from.js +3 -1
- package/build/src/abap/5_syntax/expressions/sql_source.js +18 -0
- package/build/src/abap/5_syntax/spaghetti_scope.js +1 -0
- package/build/src/registry.js +1 -1
- package/build/src/rules/index.js +1 -0
- package/build/src/rules/reduce_string_templates.js +3 -0
- package/build/src/rules/sql_value_conversion.js +65 -0
- package/package.json +1 -1
package/build/abaplint.d.ts
CHANGED
|
@@ -1265,6 +1265,7 @@ export declare class CurrentScope {
|
|
|
1265
1265
|
private static addBuiltIn;
|
|
1266
1266
|
private constructor();
|
|
1267
1267
|
getVersion(): Version;
|
|
1268
|
+
getRegistry(): IRegistry;
|
|
1268
1269
|
addType(type: TypedIdentifier | undefined): void;
|
|
1269
1270
|
addTypeNamed(name: string, type: TypedIdentifier | undefined): void;
|
|
1270
1271
|
addExtraLikeType(type: TypedIdentifier | undefined): void;
|
|
@@ -1278,6 +1279,7 @@ export declare class CurrentScope {
|
|
|
1278
1279
|
addListPrefix(identifiers: readonly TypedIdentifier[], prefix: string): void;
|
|
1279
1280
|
addList(identifiers: readonly TypedIdentifier[]): void;
|
|
1280
1281
|
addReference(usage: Token | undefined, referencing: Identifier | undefined, type: ReferenceType | undefined, filename: string, extra?: IReferenceExtras): void;
|
|
1282
|
+
addSQLConversion(fieldName: string, message: string, token: Token): void;
|
|
1281
1283
|
findFunctionModule(name: string | undefined): FunctionModuleDefinition | undefined;
|
|
1282
1284
|
findObjectDefinition(name: string | undefined): IClassDefinition | IInterfaceDefinition | undefined;
|
|
1283
1285
|
isBadiDef(name: string): boolean;
|
|
@@ -3547,6 +3549,11 @@ declare interface IScopeData {
|
|
|
3547
3549
|
idefs: IInterfaceDefinition[];
|
|
3548
3550
|
forms: IFormDefinition[];
|
|
3549
3551
|
references: IReference[];
|
|
3552
|
+
sqlConversion: {
|
|
3553
|
+
fieldName: string;
|
|
3554
|
+
message: string;
|
|
3555
|
+
token: Token;
|
|
3556
|
+
}[];
|
|
3550
3557
|
}
|
|
3551
3558
|
|
|
3552
3559
|
declare interface IScopeIdentifier {
|
|
@@ -38,6 +38,9 @@ class CurrentScope {
|
|
|
38
38
|
getVersion() {
|
|
39
39
|
return this.reg.getConfig().getVersion();
|
|
40
40
|
}
|
|
41
|
+
getRegistry() {
|
|
42
|
+
return this.reg;
|
|
43
|
+
}
|
|
41
44
|
addType(type) {
|
|
42
45
|
if (type === undefined) {
|
|
43
46
|
return;
|
|
@@ -137,6 +140,10 @@ class CurrentScope {
|
|
|
137
140
|
const position = new _identifier_1.Identifier(usage, filename);
|
|
138
141
|
(_a = this.current) === null || _a === void 0 ? void 0 : _a.getData().references.push({ position, resolved: referencing, referenceType: type, extra });
|
|
139
142
|
}
|
|
143
|
+
addSQLConversion(fieldName, message, token) {
|
|
144
|
+
var _a;
|
|
145
|
+
(_a = this.current) === null || _a === void 0 ? void 0 : _a.getData().sqlConversion.push({ fieldName, message, token });
|
|
146
|
+
}
|
|
140
147
|
///////////////////////////
|
|
141
148
|
findFunctionModule(name) {
|
|
142
149
|
if (name === undefined) {
|
|
@@ -8,7 +8,7 @@ class DatabaseTable {
|
|
|
8
8
|
const name = token.getStr();
|
|
9
9
|
if (name === "(") {
|
|
10
10
|
// dynamic
|
|
11
|
-
return;
|
|
11
|
+
return undefined;
|
|
12
12
|
}
|
|
13
13
|
const found = scope.getDDIC().lookupTableOrView2(name);
|
|
14
14
|
if (found === undefined && scope.getDDIC().inErrorNamespace(name) === true) {
|
|
@@ -21,6 +21,7 @@ class DatabaseTable {
|
|
|
21
21
|
scope.addReference(token, found.getIdentifier(), _reference_1.ReferenceType.TableReference, filename);
|
|
22
22
|
scope.getDDICReferences().addUsing(scope.getParentObj(), { object: found, token: token, filename: filename });
|
|
23
23
|
}
|
|
24
|
+
return found;
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
exports.DatabaseTable = DatabaseTable;
|
|
@@ -6,17 +6,16 @@ const basic_1 = require("../../types/basic");
|
|
|
6
6
|
const inline_data_1 = require("./inline_data");
|
|
7
7
|
const target_1 = require("./target");
|
|
8
8
|
const sql_from_1 = require("./sql_from");
|
|
9
|
-
const source_1 = require("./source");
|
|
10
9
|
const sql_for_all_entries_1 = require("./sql_for_all_entries");
|
|
11
10
|
const _scope_type_1 = require("../_scope_type");
|
|
11
|
+
const sql_source_1 = require("./sql_source");
|
|
12
|
+
const sql_compare_1 = require("./sql_compare");
|
|
12
13
|
class Select {
|
|
13
14
|
runSyntax(node, scope, filename, skipImplicitInto = false) {
|
|
14
15
|
var _a, _b;
|
|
15
16
|
const token = node.getFirstToken();
|
|
16
17
|
const from = node.findDirectExpression(Expressions.SQLFrom);
|
|
17
|
-
|
|
18
|
-
new sql_from_1.SQLFrom().runSyntax(from, scope, filename);
|
|
19
|
-
}
|
|
18
|
+
const dbSources = from ? new sql_from_1.SQLFrom().runSyntax(from, scope, filename) : [];
|
|
20
19
|
for (const inline of node.findAllExpressions(Expressions.InlineData)) {
|
|
21
20
|
// todo, for now these are voided
|
|
22
21
|
new inline_data_1.InlineData().runSyntax(inline, scope, filename, new basic_1.VoidType("SELECT_todo"));
|
|
@@ -42,11 +41,22 @@ class Select {
|
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
// OFFSET
|
|
45
|
+
for (const s of node.findDirectExpressions(Expressions.SQLSource)) {
|
|
46
|
+
new sql_source_1.SQLSource().runSyntax(s, scope, filename);
|
|
47
|
+
}
|
|
48
|
+
for (const up of node.findDirectExpressions(Expressions.SQLUpTo)) {
|
|
49
|
+
for (const s of up.findDirectExpressions(Expressions.SQLSource)) {
|
|
50
|
+
new sql_source_1.SQLSource().runSyntax(s, scope, filename);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const fae of node.findDirectExpressions(Expressions.SQLForAllEntries)) {
|
|
54
|
+
for (const s of fae.findDirectExpressions(Expressions.SQLSource)) {
|
|
55
|
+
new sql_source_1.SQLSource().runSyntax(s, scope, filename);
|
|
56
|
+
}
|
|
47
57
|
}
|
|
48
|
-
for (const s of node.findAllExpressions(Expressions.
|
|
49
|
-
new
|
|
58
|
+
for (const s of node.findAllExpressions(Expressions.SQLCompare)) {
|
|
59
|
+
new sql_compare_1.SQLCompare().runSyntax(s, scope, filename, dbSources);
|
|
50
60
|
}
|
|
51
61
|
if (scope.getType() === _scope_type_1.ScopeType.OpenSQL) {
|
|
52
62
|
scope.pop(node.getLastToken().getEnd());
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SQLCompare = void 0;
|
|
4
|
+
const Expressions = require("../../2_statements/expressions");
|
|
5
|
+
const basic_1 = require("../../types/basic");
|
|
6
|
+
const sql_source_1 = require("./sql_source");
|
|
7
|
+
class SQLCompare {
|
|
8
|
+
runSyntax(node, scope, filename, tables) {
|
|
9
|
+
var _a;
|
|
10
|
+
let sourceType;
|
|
11
|
+
let token;
|
|
12
|
+
for (const s of node.findAllExpressions(Expressions.SQLSource)) {
|
|
13
|
+
token = s.getFirstToken();
|
|
14
|
+
sourceType = new sql_source_1.SQLSource().runSyntax(s, scope, filename);
|
|
15
|
+
}
|
|
16
|
+
const fieldName = (_a = node.findDirectExpression(Expressions.SQLFieldName)) === null || _a === void 0 ? void 0 : _a.concatTokens();
|
|
17
|
+
if (fieldName && sourceType && token) {
|
|
18
|
+
// check compatibility for rule sql_value_conversion
|
|
19
|
+
const targetType = this.findType(fieldName, tables, scope);
|
|
20
|
+
let message = "";
|
|
21
|
+
if (sourceType instanceof basic_1.IntegerType
|
|
22
|
+
&& targetType instanceof basic_1.CharacterType) {
|
|
23
|
+
message = "Integer to CHAR conversion";
|
|
24
|
+
}
|
|
25
|
+
else if (sourceType instanceof basic_1.IntegerType
|
|
26
|
+
&& targetType instanceof basic_1.NumericType) {
|
|
27
|
+
message = "Integer to NUMC conversion";
|
|
28
|
+
}
|
|
29
|
+
else if (sourceType instanceof basic_1.NumericType
|
|
30
|
+
&& targetType instanceof basic_1.IntegerType) {
|
|
31
|
+
message = "NUMC to Integer conversion";
|
|
32
|
+
}
|
|
33
|
+
else if (sourceType instanceof basic_1.CharacterType
|
|
34
|
+
&& targetType instanceof basic_1.IntegerType) {
|
|
35
|
+
message = "CHAR to Integer conversion";
|
|
36
|
+
}
|
|
37
|
+
else if (sourceType instanceof basic_1.CharacterType
|
|
38
|
+
&& targetType instanceof basic_1.CharacterType
|
|
39
|
+
&& sourceType.getLength() > targetType.getLength()) {
|
|
40
|
+
message = "Source field longer than database field, CHAR -> CHAR";
|
|
41
|
+
}
|
|
42
|
+
else if (sourceType instanceof basic_1.NumericType
|
|
43
|
+
&& targetType instanceof basic_1.NumericType
|
|
44
|
+
&& sourceType.getLength() > targetType.getLength()) {
|
|
45
|
+
message = "Source field longer than database field, NUMC -> NUMC";
|
|
46
|
+
}
|
|
47
|
+
if (message !== "") {
|
|
48
|
+
scope.addSQLConversion(fieldName, message, token);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
findType(fieldName, tables, scope) {
|
|
53
|
+
for (const t of tables) {
|
|
54
|
+
const type = t === null || t === void 0 ? void 0 : t.parseType(scope.getRegistry());
|
|
55
|
+
if (type instanceof basic_1.StructureType) {
|
|
56
|
+
return type.getComponentByName(fieldName);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.SQLCompare = SQLCompare;
|
|
63
|
+
//# sourceMappingURL=sql_compare.js.map
|
|
@@ -6,6 +6,7 @@ const dynamic_1 = require("./dynamic");
|
|
|
6
6
|
const database_table_1 = require("./database_table");
|
|
7
7
|
class SQLFrom {
|
|
8
8
|
runSyntax(node, scope, filename) {
|
|
9
|
+
const ret = [];
|
|
9
10
|
const fromList = node.findAllExpressions(Expressions.SQLFromSource);
|
|
10
11
|
for (const from of fromList) {
|
|
11
12
|
for (const d of from.findAllExpressions(Expressions.Dynamic)) {
|
|
@@ -13,9 +14,10 @@ class SQLFrom {
|
|
|
13
14
|
}
|
|
14
15
|
const dbtab = from.findFirstExpression(Expressions.DatabaseTable);
|
|
15
16
|
if (dbtab !== undefined) {
|
|
16
|
-
new database_table_1.DatabaseTable().runSyntax(dbtab, scope, filename);
|
|
17
|
+
ret.push(new database_table_1.DatabaseTable().runSyntax(dbtab, scope, filename));
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
return ret;
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
exports.SQLFrom = SQLFrom;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SQLSource = void 0;
|
|
4
|
+
const Expressions = require("../../2_statements/expressions");
|
|
5
|
+
const source_1 = require("./source");
|
|
6
|
+
class SQLSource {
|
|
7
|
+
runSyntax(node, scope, filename) {
|
|
8
|
+
for (const s of node.findAllExpressions(Expressions.Source)) {
|
|
9
|
+
return new source_1.Source().runSyntax(s, scope, filename);
|
|
10
|
+
}
|
|
11
|
+
for (const s of node.findAllExpressions(Expressions.SimpleSource3)) {
|
|
12
|
+
return new source_1.Source().runSyntax(s, scope, filename);
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.SQLSource = SQLSource;
|
|
18
|
+
//# sourceMappingURL=sql_source.js.map
|
package/build/src/registry.js
CHANGED
package/build/src/rules/index.js
CHANGED
|
@@ -143,6 +143,7 @@ __exportStar(require("./smim_consistency"), exports);
|
|
|
143
143
|
__exportStar(require("./space_before_colon"), exports);
|
|
144
144
|
__exportStar(require("./space_before_dot"), exports);
|
|
145
145
|
__exportStar(require("./sql_escape_host_variables"), exports);
|
|
146
|
+
__exportStar(require("./sql_value_conversion"), exports);
|
|
146
147
|
__exportStar(require("./start_at_tab"), exports);
|
|
147
148
|
__exportStar(require("./static_call_via_instance"), exports);
|
|
148
149
|
__exportStar(require("./strict_sql"), exports);
|
|
@@ -42,6 +42,9 @@ class ReduceStringTemplates extends _abap_rule_1.ABAPRule {
|
|
|
42
42
|
for (const second of source.findDirectExpressions(Expressions.StringTemplate)) {
|
|
43
43
|
issues.push(issue_1.Issue.atToken(file, second.getFirstToken(), "Nested string templates, reduce", this.getMetadata().key, this.conf.severity));
|
|
44
44
|
}
|
|
45
|
+
if (ts.findDirectExpression(Expressions.StringTemplateFormatting)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
45
48
|
for (const constant of source.findDirectExpressions(Expressions.Constant)) {
|
|
46
49
|
for (const constantString of constant.findDirectExpressions(Expressions.ConstantString)) {
|
|
47
50
|
issues.push(issue_1.Issue.atToken(file, constantString.getFirstToken(), "Constant string in text template, reduce", this.getMetadata().key, this.conf.severity));
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SQLValueConversion = exports.SQLValueConversionConf = void 0;
|
|
4
|
+
const issue_1 = require("../issue");
|
|
5
|
+
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
6
|
+
const objects_1 = require("../objects");
|
|
7
|
+
const syntax_1 = require("../abap/5_syntax/syntax");
|
|
8
|
+
const _abap_object_1 = require("../objects/_abap_object");
|
|
9
|
+
class SQLValueConversionConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
10
|
+
}
|
|
11
|
+
exports.SQLValueConversionConf = SQLValueConversionConf;
|
|
12
|
+
class SQLValueConversion {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.conf = new SQLValueConversionConf();
|
|
15
|
+
}
|
|
16
|
+
getMetadata() {
|
|
17
|
+
return {
|
|
18
|
+
key: "sql_value_conversion",
|
|
19
|
+
title: "Implicit SQL Value Conversion",
|
|
20
|
+
shortDescription: `Ensure types match when selecting from database`,
|
|
21
|
+
extendedInformation: `
|
|
22
|
+
* Integer to CHAR conversion
|
|
23
|
+
* Integer to NUMC conversion
|
|
24
|
+
* NUMC to Integer conversion
|
|
25
|
+
* CHAR to Integer conversion
|
|
26
|
+
* Source field longer than database field, CHAR -> CHAR
|
|
27
|
+
* Source field longer than database field, NUMC -> NUMC`,
|
|
28
|
+
tags: [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
getConfig() {
|
|
32
|
+
return this.conf;
|
|
33
|
+
}
|
|
34
|
+
setConfig(conf) {
|
|
35
|
+
this.conf = conf;
|
|
36
|
+
}
|
|
37
|
+
initialize(reg) {
|
|
38
|
+
this.reg = reg;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
run(obj) {
|
|
42
|
+
if (!(obj instanceof _abap_object_1.ABAPObject) || obj instanceof objects_1.Interface) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
// messages defined in sql_compare.ts
|
|
46
|
+
const issues = this.traverse(new syntax_1.SyntaxLogic(this.reg, obj).run().spaghetti.getTop());
|
|
47
|
+
return issues;
|
|
48
|
+
}
|
|
49
|
+
traverse(node) {
|
|
50
|
+
const ret = [];
|
|
51
|
+
for (const r of node.getData().sqlConversion) {
|
|
52
|
+
const file = this.reg.getFileByName(node.getIdentifier().filename);
|
|
53
|
+
if (file === undefined) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
ret.push(issue_1.Issue.atToken(file, r.token, r.message, this.getMetadata().key, this.getConfig().severity));
|
|
57
|
+
}
|
|
58
|
+
for (const c of node.getChildren()) {
|
|
59
|
+
ret.push(...this.traverse(c));
|
|
60
|
+
}
|
|
61
|
+
return ret;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.SQLValueConversion = SQLValueConversion;
|
|
65
|
+
//# sourceMappingURL=sql_value_conversion.js.map
|