@abaplint/core 2.112.16 → 2.112.18
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/README.md +6 -6
- package/build/abaplint.d.ts +24 -6
- package/build/src/abap/2_statements/expressions/index.js +3 -2
- package/build/src/abap/2_statements/expressions/lob_handle.js +12 -0
- package/build/src/abap/2_statements/expressions/type.js +1 -1
- package/build/src/abap/5_syntax/_builtin.js +0 -1
- package/build/src/abap/5_syntax/basic_types.js +0 -1
- package/build/src/abap/flow/flow_graph.js +42 -25
- package/build/src/abap/flow/statement_flow.js +65 -61
- package/build/src/abap/types/basic/character_type.js +1 -1
- package/build/src/index.js +3 -2
- package/build/src/lsp/help.js +7 -7
- package/build/src/registry.js +1 -1
- package/build/src/rules/7bit_ascii.js +4 -4
- package/build/src/rules/abapdoc.js +4 -4
- package/build/src/rules/align_parameters.js +40 -40
- package/build/src/rules/align_type_expressions.js +28 -28
- package/build/src/rules/ambiguous_statement.js +6 -6
- package/build/src/rules/avoid_use.js +10 -10
- package/build/src/rules/begin_end_names.js +4 -4
- package/build/src/rules/begin_single_include.js +13 -13
- package/build/src/rules/call_transaction_authority_check.js +3 -3
- package/build/src/rules/cds_comment_style.js +4 -4
- package/build/src/rules/cds_legacy_view.js +4 -5
- package/build/src/rules/chain_mainly_declarations.js +4 -4
- package/build/src/rules/change_if_to_case.js +8 -8
- package/build/src/rules/check_abstract.js +2 -2
- package/build/src/rules/check_comments.js +4 -4
- package/build/src/rules/check_include.js +3 -3
- package/build/src/rules/check_subrc.js +8 -8
- package/build/src/rules/classic_exceptions_overlap.js +10 -10
- package/build/src/rules/commented_code.js +1 -1
- package/build/src/rules/constructor_visibility_public.js +4 -4
- package/build/src/rules/contains_tab.js +2 -2
- package/build/src/rules/cyclic_oo.js +4 -4
- package/build/src/rules/dangerous_statement.js +1 -1
- package/build/src/rules/definitions_top.js +6 -6
- package/build/src/rules/downport.js +82 -82
- package/build/src/rules/easy_to_find_messages.js +6 -6
- package/build/src/rules/empty_line_in_statement.js +2 -2
- package/build/src/rules/empty_structure.js +6 -6
- package/build/src/rules/exit_or_check.js +3 -3
- package/build/src/rules/expand_macros.js +5 -5
- package/build/src/rules/exporting.js +1 -1
- package/build/src/rules/forbidden_identifier.js +1 -1
- package/build/src/rules/forbidden_void_type.js +2 -2
- package/build/src/rules/fully_type_itabs.js +1 -1
- package/build/src/rules/functional_writing.js +17 -17
- package/build/src/rules/global_class.js +8 -8
- package/build/src/rules/identical_conditions.js +12 -12
- package/build/src/rules/identical_contents.js +14 -14
- package/build/src/rules/identical_descriptions.js +6 -6
- package/build/src/rules/if_in_if.js +35 -35
- package/build/src/rules/implement_methods.js +3 -3
- package/build/src/rules/in_statement_indentation.js +11 -11
- package/build/src/rules/indentation.js +16 -16
- package/build/src/rules/intf_referencing_clas.js +3 -3
- package/build/src/rules/invalid_table_index.js +2 -2
- package/build/src/rules/line_break_style.js +2 -2
- package/build/src/rules/line_length.js +1 -1
- package/build/src/rules/line_only_punc.js +1 -1
- package/build/src/rules/local_variable_names.js +6 -6
- package/build/src/rules/macro_naming.js +2 -2
- package/build/src/rules/main_file_contents.js +4 -4
- package/build/src/rules/many_parentheses.js +10 -10
- package/build/src/rules/max_one_method_parameter_per_line.js +7 -7
- package/build/src/rules/max_one_statement.js +5 -5
- package/build/src/rules/method_length.js +2 -2
- package/build/src/rules/method_overwrites_builtin.js +12 -12
- package/build/src/rules/mix_returning.js +6 -6
- package/build/src/rules/nesting.js +1 -1
- package/build/src/rules/no_chained_assignment.js +1 -1
- package/build/src/rules/no_external_form_calls.js +2 -2
- package/build/src/rules/no_inline_in_optional_branches.js +11 -11
- package/build/src/rules/no_prefixes.js +6 -6
- package/build/src/rules/no_public_attributes.js +1 -1
- package/build/src/rules/no_yoda_conditions.js +4 -4
- package/build/src/rules/nrob_consistency.js +2 -2
- package/build/src/rules/obsolete_statement.js +51 -51
- package/build/src/rules/omit_parameter_name.js +3 -3
- package/build/src/rules/omit_receiving.js +13 -13
- package/build/src/rules/parser_702_chaining.js +2 -2
- package/build/src/rules/parser_error.js +2 -2
- package/build/src/rules/parser_missing_space.js +1 -1
- package/build/src/rules/prefer_inline.js +16 -16
- package/build/src/rules/prefer_is_not.js +9 -9
- package/build/src/rules/prefer_raise_exception_new.js +5 -5
- package/build/src/rules/prefer_returning_to_exporting.js +4 -4
- package/build/src/rules/prefer_xsdbool.js +2 -2
- package/build/src/rules/preferred_compare_operator.js +2 -2
- package/build/src/rules/reduce_procedural_code.js +17 -17
- package/build/src/rules/remove_descriptions.js +4 -4
- package/build/src/rules/rfc_error_handling.js +7 -7
- package/build/src/rules/select_add_order_by.js +5 -5
- package/build/src/rules/select_performance.js +5 -5
- package/build/src/rules/select_single_full_key.js +2 -2
- package/build/src/rules/sicf_consistency.js +2 -2
- package/build/src/rules/slow_parameter_passing.js +16 -16
- package/build/src/rules/space_before_dot.js +2 -2
- package/build/src/rules/sql_value_conversion.js +6 -6
- package/build/src/rules/start_at_tab.js +1 -1
- package/build/src/rules/strict_sql.js +6 -6
- package/build/src/rules/sy_modification.js +3 -3
- package/build/src/rules/tabl_enhancement_category.js +2 -2
- package/build/src/rules/tables_declared_locally.js +2 -2
- package/build/src/rules/type_form_parameters.js +2 -2
- package/build/src/rules/unnecessary_pragma.js +29 -29
- package/build/src/rules/unnecessary_return.js +11 -11
- package/build/src/rules/unused_macros.js +6 -6
- package/build/src/rules/unused_methods.js +11 -11
- package/build/src/rules/unused_variables.js +12 -12
- package/build/src/rules/use_bool_expression.js +8 -8
- package/build/src/rules/use_class_based_exceptions.js +8 -8
- package/build/src/rules/use_line_exists.js +6 -6
- package/build/src/rules/use_new.js +4 -4
- package/build/src/rules/when_others_last.js +6 -6
- package/package.json +70 -70
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# @abaplint/core
|
|
2
|
-
|
|
3
|
-
[abaplint](https://abaplint.org) core library
|
|
4
|
-
|
|
5
|
-
Exposes functionality like the parser and rules, which can be used in other projects.
|
|
6
|
-
|
|
1
|
+
# @abaplint/core
|
|
2
|
+
|
|
3
|
+
[abaplint](https://abaplint.org) core library
|
|
4
|
+
|
|
5
|
+
Exposes functionality like the parser and rules, which can be used in other projects.
|
|
6
|
+
|
|
7
7
|
For more information see https://github.com/abaplint/abaplint
|
package/build/abaplint.d.ts
CHANGED
|
@@ -2225,6 +2225,7 @@ declare namespace Expressions {
|
|
|
2225
2225
|
Language,
|
|
2226
2226
|
Length,
|
|
2227
2227
|
Let,
|
|
2228
|
+
LOBHandle,
|
|
2228
2229
|
LoopGroupByComponent,
|
|
2229
2230
|
LoopGroupByTarget,
|
|
2230
2231
|
LoopGroupBy,
|
|
@@ -2318,16 +2319,15 @@ declare namespace Expressions {
|
|
|
2318
2319
|
SQLJoin,
|
|
2319
2320
|
SQLOrderBy,
|
|
2320
2321
|
SQLPath,
|
|
2322
|
+
SQLSourceNoSpace,
|
|
2321
2323
|
SQLSourceSimple,
|
|
2322
2324
|
SQLSource,
|
|
2323
|
-
SQLSourceNoSpace,
|
|
2324
2325
|
SQLTarget,
|
|
2325
2326
|
SQLUpTo,
|
|
2326
2327
|
StringTemplateFormatting,
|
|
2327
2328
|
StringTemplateSource,
|
|
2328
2329
|
StringTemplate,
|
|
2329
2330
|
SuperClassName,
|
|
2330
|
-
TypeStructure,
|
|
2331
2331
|
SwitchBody,
|
|
2332
2332
|
TableBody,
|
|
2333
2333
|
TableExpression,
|
|
@@ -2343,6 +2343,7 @@ declare namespace Expressions {
|
|
|
2343
2343
|
TypeNameOrInfer,
|
|
2344
2344
|
TypeName,
|
|
2345
2345
|
TypeParam,
|
|
2346
|
+
TypeStructure,
|
|
2346
2347
|
TypeTableKey,
|
|
2347
2348
|
TypeTable,
|
|
2348
2349
|
Type_2 as Type,
|
|
@@ -2515,6 +2516,12 @@ declare class FloatType extends AbstractType {
|
|
|
2515
2516
|
toCDS(): string;
|
|
2516
2517
|
}
|
|
2517
2518
|
|
|
2519
|
+
export declare enum FLOW_EDGE_TYPE {
|
|
2520
|
+
true = "true",
|
|
2521
|
+
false = "false",
|
|
2522
|
+
undefined = "undefined"
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2518
2525
|
export declare class FlowGraph {
|
|
2519
2526
|
private edges;
|
|
2520
2527
|
private readonly startNode;
|
|
@@ -2524,23 +2531,30 @@ export declare class FlowGraph {
|
|
|
2524
2531
|
getStart(): string;
|
|
2525
2532
|
getLabel(): string;
|
|
2526
2533
|
getEnd(): string;
|
|
2527
|
-
addEdge(from: string, to: string): void;
|
|
2534
|
+
addEdge(from: string, to: string, type: FLOW_EDGE_TYPE): void;
|
|
2528
2535
|
removeEdge(from: string, to: string): void;
|
|
2529
2536
|
listEdges(): {
|
|
2530
2537
|
from: string;
|
|
2531
2538
|
to: string;
|
|
2539
|
+
type: FLOW_EDGE_TYPE;
|
|
2532
2540
|
}[];
|
|
2533
2541
|
listInto(to: string, skipStart?: boolean): string[];
|
|
2534
2542
|
listNodes(): string[];
|
|
2535
2543
|
hasEdges(): boolean;
|
|
2536
2544
|
/** return value: end node of to graph */
|
|
2537
|
-
addGraph(from: string, to: FlowGraph): string;
|
|
2545
|
+
addGraph(from: string, to: FlowGraph, type: FLOW_EDGE_TYPE): string;
|
|
2538
2546
|
toJSON(): string;
|
|
2539
2547
|
toTextEdges(): string;
|
|
2540
2548
|
setLabel(label: string): void;
|
|
2541
2549
|
toDigraph(): string;
|
|
2542
|
-
listSources(node: string):
|
|
2543
|
-
|
|
2550
|
+
listSources(node: string): {
|
|
2551
|
+
name: string;
|
|
2552
|
+
type: FLOW_EDGE_TYPE;
|
|
2553
|
+
}[];
|
|
2554
|
+
listTargets(node: string): {
|
|
2555
|
+
name: string;
|
|
2556
|
+
type: FLOW_EDGE_TYPE;
|
|
2557
|
+
}[];
|
|
2544
2558
|
/** removes all nodes containing "#" that have one in-going and one out-going edge */
|
|
2545
2559
|
reduce(): this;
|
|
2546
2560
|
}
|
|
@@ -4079,6 +4093,10 @@ declare class LoadReport implements IStatement {
|
|
|
4079
4093
|
getMatcher(): IStatementRunnable;
|
|
4080
4094
|
}
|
|
4081
4095
|
|
|
4096
|
+
declare class LOBHandle extends Expression {
|
|
4097
|
+
getRunnable(): IStatementRunnable;
|
|
4098
|
+
}
|
|
4099
|
+
|
|
4082
4100
|
declare class Local implements IStatement {
|
|
4083
4101
|
getMatcher(): IStatementRunnable;
|
|
4084
4102
|
}
|
|
@@ -104,6 +104,7 @@ __exportStar(require("./kernel_id"), exports);
|
|
|
104
104
|
__exportStar(require("./language"), exports);
|
|
105
105
|
__exportStar(require("./length"), exports);
|
|
106
106
|
__exportStar(require("./let"), exports);
|
|
107
|
+
__exportStar(require("./lob_handle"), exports);
|
|
107
108
|
__exportStar(require("./loop_group_by_component"), exports);
|
|
108
109
|
__exportStar(require("./loop_group_by_target"), exports);
|
|
109
110
|
__exportStar(require("./loop_group_by"), exports);
|
|
@@ -197,16 +198,15 @@ __exportStar(require("./sql_into_table"), exports);
|
|
|
197
198
|
__exportStar(require("./sql_join"), exports);
|
|
198
199
|
__exportStar(require("./sql_order_by"), exports);
|
|
199
200
|
__exportStar(require("./sql_path"), exports);
|
|
201
|
+
__exportStar(require("./sql_source_no_space"), exports);
|
|
200
202
|
__exportStar(require("./sql_source_simple"), exports);
|
|
201
203
|
__exportStar(require("./sql_source"), exports);
|
|
202
|
-
__exportStar(require("./sql_source_no_space"), exports);
|
|
203
204
|
__exportStar(require("./sql_target"), exports);
|
|
204
205
|
__exportStar(require("./sql_up_to"), exports);
|
|
205
206
|
__exportStar(require("./string_template_formatting"), exports);
|
|
206
207
|
__exportStar(require("./string_template_source"), exports);
|
|
207
208
|
__exportStar(require("./string_template"), exports);
|
|
208
209
|
__exportStar(require("./super_class_name"), exports);
|
|
209
|
-
__exportStar(require("./type_structure"), exports);
|
|
210
210
|
__exportStar(require("./switch_body"), exports);
|
|
211
211
|
__exportStar(require("./table_body"), exports);
|
|
212
212
|
__exportStar(require("./table_expression"), exports);
|
|
@@ -222,6 +222,7 @@ __exportStar(require("./transporting_fields"), exports);
|
|
|
222
222
|
__exportStar(require("./type_name_or_infer"), exports);
|
|
223
223
|
__exportStar(require("./type_name"), exports);
|
|
224
224
|
__exportStar(require("./type_param"), exports);
|
|
225
|
+
__exportStar(require("./type_structure"), exports);
|
|
225
226
|
__exportStar(require("./type_table_key"), exports);
|
|
226
227
|
__exportStar(require("./type_table"), exports);
|
|
227
228
|
__exportStar(require("./type"), exports);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LOBHandle = void 0;
|
|
4
|
+
const combi_1 = require("../combi");
|
|
5
|
+
const Expressions = require(".");
|
|
6
|
+
class LOBHandle extends combi_1.Expression {
|
|
7
|
+
getRunnable() {
|
|
8
|
+
return (0, combi_1.seq)("WRITER FOR COLUMNS", Expressions.Field);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.LOBHandle = LOBHandle;
|
|
12
|
+
//# sourceMappingURL=lob_handle.js.map
|
|
@@ -7,7 +7,7 @@ class Type extends combi_1.Expression {
|
|
|
7
7
|
getRunnable() {
|
|
8
8
|
const typeType = (0, combi_1.seq)(_1.TypeName, (0, combi_1.optPrio)(_1.Default));
|
|
9
9
|
const like = (0, combi_1.altPrio)((0, combi_1.seq)("LINE OF", _1.FieldChain), (0, combi_1.seq)("REF TO", _1.FieldChain), _1.FieldChain);
|
|
10
|
-
const type = (0, combi_1.altPrio)((0, combi_1.seq)("LINE OF", typeType), (0, combi_1.seq)("REF TO", typeType), typeType);
|
|
10
|
+
const type = (0, combi_1.altPrio)((0, combi_1.seq)("LINE OF", typeType), (0, combi_1.seq)("REF TO", typeType), (0, combi_1.seq)(typeType, (0, combi_1.optPrio)(_1.LOBHandle)));
|
|
11
11
|
const ret = (0, combi_1.altPrio)((0, combi_1.seq)("LIKE", like), (0, combi_1.seq)("TYPE", type));
|
|
12
12
|
return ret;
|
|
13
13
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BuiltIn = exports.BuiltInMethod = void 0;
|
|
4
|
-
/* eslint-disable max-len */
|
|
5
4
|
const _typed_identifier_1 = require("../types/_typed_identifier");
|
|
6
5
|
const basic_1 = require("../types/basic");
|
|
7
6
|
const tokens_1 = require("../1_lexer/tokens");
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BasicTypes = void 0;
|
|
4
|
-
/* eslint-disable default-case */
|
|
5
4
|
const _typed_identifier_1 = require("../types/_typed_identifier");
|
|
6
5
|
const Expressions = require("../2_statements/expressions");
|
|
7
6
|
const Statements = require("../2_statements/statements");
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FlowGraph = void 0;
|
|
3
|
+
exports.FlowGraph = exports.FLOW_EDGE_TYPE = void 0;
|
|
4
|
+
var FLOW_EDGE_TYPE;
|
|
5
|
+
(function (FLOW_EDGE_TYPE) {
|
|
6
|
+
FLOW_EDGE_TYPE["true"] = "true";
|
|
7
|
+
FLOW_EDGE_TYPE["false"] = "false";
|
|
8
|
+
FLOW_EDGE_TYPE["undefined"] = "undefined";
|
|
9
|
+
})(FLOW_EDGE_TYPE || (exports.FLOW_EDGE_TYPE = FLOW_EDGE_TYPE = {}));
|
|
4
10
|
class FlowGraph {
|
|
5
11
|
constructor(counter) {
|
|
6
12
|
this.edges = {};
|
|
@@ -17,11 +23,11 @@ class FlowGraph {
|
|
|
17
23
|
getEnd() {
|
|
18
24
|
return this.endNode;
|
|
19
25
|
}
|
|
20
|
-
addEdge(from, to) {
|
|
26
|
+
addEdge(from, to, type) {
|
|
21
27
|
if (this.edges[from] === undefined) {
|
|
22
28
|
this.edges[from] = {};
|
|
23
29
|
}
|
|
24
|
-
this.edges[from][to] =
|
|
30
|
+
this.edges[from][to] = type;
|
|
25
31
|
}
|
|
26
32
|
removeEdge(from, to) {
|
|
27
33
|
if (this.edges[from] === undefined) {
|
|
@@ -36,7 +42,7 @@ class FlowGraph {
|
|
|
36
42
|
const list = [];
|
|
37
43
|
for (const from of Object.keys(this.edges)) {
|
|
38
44
|
for (const to of Object.keys(this.edges[from])) {
|
|
39
|
-
list.push({ from, to });
|
|
45
|
+
list.push({ from, to, type: this.edges[from][to] });
|
|
40
46
|
}
|
|
41
47
|
}
|
|
42
48
|
return list;
|
|
@@ -65,12 +71,12 @@ class FlowGraph {
|
|
|
65
71
|
return Object.keys(this.edges).length > 0;
|
|
66
72
|
}
|
|
67
73
|
/** return value: end node of to graph */
|
|
68
|
-
addGraph(from, to) {
|
|
74
|
+
addGraph(from, to, type) {
|
|
69
75
|
if (to.hasEdges() === false) {
|
|
70
76
|
return from;
|
|
71
77
|
}
|
|
72
|
-
this.addEdge(from, to.getStart());
|
|
73
|
-
to.listEdges().forEach(e => this.addEdge(e.from, e.to));
|
|
78
|
+
this.addEdge(from, to.getStart(), type);
|
|
79
|
+
to.listEdges().forEach(e => this.addEdge(e.from, e.to, e.type));
|
|
74
80
|
return to.getEnd();
|
|
75
81
|
}
|
|
76
82
|
toJSON() {
|
|
@@ -79,7 +85,8 @@ class FlowGraph {
|
|
|
79
85
|
toTextEdges() {
|
|
80
86
|
let graph = "";
|
|
81
87
|
for (const l of this.listEdges()) {
|
|
82
|
-
|
|
88
|
+
const label = l.type === FLOW_EDGE_TYPE.undefined ? "" : ` [label="${l.type}"]`;
|
|
89
|
+
graph += `"${l.from}" -> "${l.to}"${label};\n`;
|
|
83
90
|
}
|
|
84
91
|
return graph.trim();
|
|
85
92
|
}
|
|
@@ -87,32 +94,32 @@ class FlowGraph {
|
|
|
87
94
|
this.label = label;
|
|
88
95
|
}
|
|
89
96
|
toDigraph() {
|
|
90
|
-
return `digraph G {
|
|
91
|
-
labelloc="t";
|
|
92
|
-
label="${this.label}";
|
|
93
|
-
graph [fontname = "helvetica"];
|
|
94
|
-
node [fontname = "helvetica", shape="box"];
|
|
95
|
-
edge [fontname = "helvetica"];
|
|
96
|
-
${this.toTextEdges()}
|
|
97
|
+
return `digraph G {
|
|
98
|
+
labelloc="t";
|
|
99
|
+
label="${this.label}";
|
|
100
|
+
graph [fontname = "helvetica"];
|
|
101
|
+
node [fontname = "helvetica", shape="box"];
|
|
102
|
+
edge [fontname = "helvetica"];
|
|
103
|
+
${this.toTextEdges()}
|
|
97
104
|
}`;
|
|
98
105
|
}
|
|
99
106
|
listSources(node) {
|
|
100
|
-
const set =
|
|
107
|
+
const set = [];
|
|
101
108
|
for (const l of this.listEdges()) {
|
|
102
109
|
if (node === l.to) {
|
|
103
|
-
set.
|
|
110
|
+
set.push({ name: l.from, type: l.type });
|
|
104
111
|
}
|
|
105
112
|
}
|
|
106
|
-
return
|
|
113
|
+
return set;
|
|
107
114
|
}
|
|
108
115
|
listTargets(node) {
|
|
109
|
-
const set =
|
|
116
|
+
const set = [];
|
|
110
117
|
for (const l of this.listEdges()) {
|
|
111
118
|
if (node === l.from) {
|
|
112
|
-
set.
|
|
119
|
+
set.push({ name: l.to, type: l.type });
|
|
113
120
|
}
|
|
114
121
|
}
|
|
115
|
-
return
|
|
122
|
+
return set;
|
|
116
123
|
}
|
|
117
124
|
/** removes all nodes containing "#" that have one in-going and one out-going edge */
|
|
118
125
|
reduce() {
|
|
@@ -125,20 +132,30 @@ ${this.toTextEdges()}
|
|
|
125
132
|
if (sources.length > 0 && targets.length > 0) {
|
|
126
133
|
// hash node in the middle of the graph
|
|
127
134
|
for (const s of sources) {
|
|
128
|
-
this.removeEdge(s, node);
|
|
135
|
+
this.removeEdge(s.name, node);
|
|
129
136
|
}
|
|
130
137
|
for (const t of targets) {
|
|
131
|
-
this.removeEdge(node, t);
|
|
138
|
+
this.removeEdge(node, t.name);
|
|
132
139
|
}
|
|
133
140
|
for (const s of sources) {
|
|
134
141
|
for (const t of targets) {
|
|
135
|
-
|
|
142
|
+
let type = FLOW_EDGE_TYPE.undefined;
|
|
143
|
+
if (s.type !== FLOW_EDGE_TYPE.undefined) {
|
|
144
|
+
type = s.type;
|
|
145
|
+
}
|
|
146
|
+
if (t.type !== FLOW_EDGE_TYPE.undefined) {
|
|
147
|
+
if (type !== FLOW_EDGE_TYPE.undefined) {
|
|
148
|
+
throw new Error("reduce: cannot merge, different edge types");
|
|
149
|
+
}
|
|
150
|
+
type = t.type;
|
|
151
|
+
}
|
|
152
|
+
this.addEdge(s.name, t.name, type);
|
|
136
153
|
}
|
|
137
154
|
}
|
|
138
155
|
}
|
|
139
156
|
if (node.startsWith("end#") && sources.length === 0) {
|
|
140
157
|
for (const t of targets) {
|
|
141
|
-
this.removeEdge(node, t);
|
|
158
|
+
this.removeEdge(node, t.name);
|
|
142
159
|
}
|
|
143
160
|
}
|
|
144
161
|
}
|
|
@@ -11,22 +11,26 @@ class StatementFlow {
|
|
|
11
11
|
this.counter = 0;
|
|
12
12
|
}
|
|
13
13
|
build(stru) {
|
|
14
|
-
var _a, _b;
|
|
14
|
+
var _a, _b, _c;
|
|
15
15
|
const ret = [];
|
|
16
|
-
const
|
|
17
|
-
for (const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
const structures = stru.findAllStructuresMulti([Structures.Form, Structures.Method, Structures.FunctionModule]);
|
|
17
|
+
for (const s of structures) {
|
|
18
|
+
let name = "";
|
|
19
|
+
if (s.get() instanceof Structures.Form) {
|
|
20
|
+
name = "FORM " + ((_a = s.findFirstExpression(Expressions.FormName)) === null || _a === void 0 ? void 0 : _a.concatTokens());
|
|
21
|
+
}
|
|
22
|
+
else if (s.get() instanceof Structures.Method) {
|
|
23
|
+
name = "METHOD " + ((_b = s.findFirstExpression(Expressions.MethodName)) === null || _b === void 0 ? void 0 : _b.concatTokens());
|
|
24
|
+
}
|
|
25
|
+
else if (s.get() instanceof Structures.FunctionModule) {
|
|
26
|
+
name = "FUNCTION " + ((_c = s.findFirstExpression(Expressions.Field)) === null || _c === void 0 ? void 0 : _c.concatTokens());
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
throw new Error("StatementFlow, unknown structure");
|
|
30
|
+
}
|
|
27
31
|
this.counter = 1;
|
|
28
|
-
const graph = this.traverseBody(this.findBody(
|
|
29
|
-
graph.setLabel(
|
|
32
|
+
const graph = this.traverseBody(this.findBody(s), { procedureEnd: "end#1" });
|
|
33
|
+
graph.setLabel(name);
|
|
30
34
|
ret.push(graph);
|
|
31
35
|
}
|
|
32
36
|
return ret.map(f => f.reduce());
|
|
@@ -60,7 +64,7 @@ class StatementFlow {
|
|
|
60
64
|
traverseBody(children, context) {
|
|
61
65
|
const graph = new flow_graph_1.FlowGraph(this.counter++);
|
|
62
66
|
if (children.length === 0) {
|
|
63
|
-
graph.addEdge(graph.getStart(), graph.getEnd());
|
|
67
|
+
graph.addEdge(graph.getStart(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
64
68
|
return graph;
|
|
65
69
|
}
|
|
66
70
|
let current = graph.getStart();
|
|
@@ -69,44 +73,44 @@ class StatementFlow {
|
|
|
69
73
|
const firstChild = c.getFirstChild(); // "Normal" only has one child
|
|
70
74
|
if (firstChild instanceof nodes_1.StatementNode) {
|
|
71
75
|
const name = this.buildName(firstChild);
|
|
72
|
-
graph.addEdge(current, name);
|
|
76
|
+
graph.addEdge(current, name, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
73
77
|
current = name;
|
|
74
78
|
if (firstChild.get() instanceof Statements.Check) {
|
|
75
79
|
if (context.loopStart) {
|
|
76
|
-
graph.addEdge(name, context.loopStart);
|
|
80
|
+
graph.addEdge(name, context.loopStart, flow_graph_1.FLOW_EDGE_TYPE.false);
|
|
77
81
|
}
|
|
78
82
|
else {
|
|
79
|
-
graph.addEdge(name, context.procedureEnd);
|
|
83
|
+
graph.addEdge(name, context.procedureEnd, flow_graph_1.FLOW_EDGE_TYPE.false);
|
|
80
84
|
}
|
|
81
85
|
}
|
|
82
86
|
else if (firstChild.get() instanceof Statements.Assert) {
|
|
83
|
-
graph.addEdge(name, context.procedureEnd);
|
|
87
|
+
graph.addEdge(name, context.procedureEnd, flow_graph_1.FLOW_EDGE_TYPE.false);
|
|
84
88
|
}
|
|
85
89
|
else if (firstChild.get() instanceof Statements.Continue && context.loopStart) {
|
|
86
|
-
graph.addEdge(name, context.loopStart);
|
|
90
|
+
graph.addEdge(name, context.loopStart, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
87
91
|
return graph;
|
|
88
92
|
}
|
|
89
93
|
else if (firstChild.get() instanceof Statements.Exit) {
|
|
90
94
|
if (context.loopEnd) {
|
|
91
|
-
graph.addEdge(name, context.loopEnd);
|
|
95
|
+
graph.addEdge(name, context.loopEnd, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
92
96
|
}
|
|
93
97
|
else {
|
|
94
|
-
graph.addEdge(name, context.procedureEnd);
|
|
98
|
+
graph.addEdge(name, context.procedureEnd, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
95
99
|
}
|
|
96
100
|
return graph;
|
|
97
101
|
}
|
|
98
102
|
else if (firstChild.get() instanceof Statements.Return) {
|
|
99
|
-
graph.addEdge(name, context.procedureEnd);
|
|
103
|
+
graph.addEdge(name, context.procedureEnd, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
100
104
|
return graph;
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
107
|
else if (firstChild instanceof nodes_1.StructureNode) {
|
|
104
108
|
const sub = this.traverseStructure(firstChild, context);
|
|
105
|
-
current = graph.addGraph(current, sub);
|
|
109
|
+
current = graph.addGraph(current, sub, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
112
|
}
|
|
109
|
-
graph.addEdge(current, graph.getEnd());
|
|
113
|
+
graph.addEdge(current, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
110
114
|
return graph;
|
|
111
115
|
}
|
|
112
116
|
traverseStructure(n, context) {
|
|
@@ -119,9 +123,9 @@ class StatementFlow {
|
|
|
119
123
|
if (type instanceof Structures.If) {
|
|
120
124
|
const ifName = this.buildName(n.findDirectStatement(Statements.If));
|
|
121
125
|
const sub = this.traverseBody(this.findBody(n), context);
|
|
122
|
-
graph.addEdge(current, ifName);
|
|
123
|
-
graph.addGraph(ifName, sub);
|
|
124
|
-
graph.addEdge(sub.getEnd(), graph.getEnd());
|
|
126
|
+
graph.addEdge(current, ifName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
127
|
+
graph.addGraph(ifName, sub, flow_graph_1.FLOW_EDGE_TYPE.true);
|
|
128
|
+
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
125
129
|
current = ifName;
|
|
126
130
|
for (const e of n.findDirectStructures(Structures.ElseIf)) {
|
|
127
131
|
const elseifst = e.findDirectStatement(Statements.ElseIf);
|
|
@@ -130,9 +134,9 @@ class StatementFlow {
|
|
|
130
134
|
}
|
|
131
135
|
const elseIfName = this.buildName(elseifst);
|
|
132
136
|
const sub = this.traverseBody(this.findBody(e), context);
|
|
133
|
-
graph.addEdge(current, elseIfName);
|
|
134
|
-
graph.addGraph(elseIfName, sub);
|
|
135
|
-
graph.addEdge(sub.getEnd(), graph.getEnd());
|
|
137
|
+
graph.addEdge(current, elseIfName, flow_graph_1.FLOW_EDGE_TYPE.false);
|
|
138
|
+
graph.addGraph(elseIfName, sub, flow_graph_1.FLOW_EDGE_TYPE.true);
|
|
139
|
+
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
136
140
|
current = elseIfName;
|
|
137
141
|
}
|
|
138
142
|
const els = n.findDirectStructure(Structures.Else);
|
|
@@ -140,12 +144,12 @@ class StatementFlow {
|
|
|
140
144
|
if (els && elsest) {
|
|
141
145
|
const elseName = this.buildName(elsest);
|
|
142
146
|
const sub = this.traverseBody(this.findBody(els), context);
|
|
143
|
-
graph.addEdge(current, elseName);
|
|
144
|
-
graph.addGraph(elseName, sub);
|
|
145
|
-
graph.addEdge(sub.getEnd(), graph.getEnd());
|
|
147
|
+
graph.addEdge(current, elseName, flow_graph_1.FLOW_EDGE_TYPE.false);
|
|
148
|
+
graph.addGraph(elseName, sub, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
149
|
+
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
146
150
|
}
|
|
147
151
|
else {
|
|
148
|
-
graph.addEdge(ifName, graph.getEnd());
|
|
152
|
+
graph.addEdge(ifName, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.false);
|
|
149
153
|
}
|
|
150
154
|
}
|
|
151
155
|
else if (type instanceof Structures.Loop
|
|
@@ -157,18 +161,18 @@ class StatementFlow {
|
|
|
157
161
|
|| type instanceof Structures.Do) {
|
|
158
162
|
const loopName = this.buildName(n.getFirstStatement());
|
|
159
163
|
const sub = this.traverseBody(this.findBody(n), Object.assign(Object.assign({}, context), { loopStart: loopName, loopEnd: graph.getEnd() }));
|
|
160
|
-
graph.addEdge(current, loopName);
|
|
161
|
-
graph.addGraph(loopName, sub);
|
|
162
|
-
graph.addEdge(sub.getEnd(), loopName);
|
|
163
|
-
graph.addEdge(loopName, graph.getEnd());
|
|
164
|
+
graph.addEdge(current, loopName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
165
|
+
graph.addGraph(loopName, sub, flow_graph_1.FLOW_EDGE_TYPE.true);
|
|
166
|
+
graph.addEdge(sub.getEnd(), loopName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
167
|
+
graph.addEdge(loopName, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.false);
|
|
164
168
|
}
|
|
165
169
|
else if (type instanceof Structures.Data
|
|
166
170
|
|| type instanceof Structures.Types) {
|
|
167
171
|
// these doesnt affect control flow, so just take the first statement
|
|
168
172
|
const statement = n.getFirstStatement();
|
|
169
173
|
const name = this.buildName(statement);
|
|
170
|
-
graph.addEdge(current, name);
|
|
171
|
-
graph.addEdge(name, graph.getEnd());
|
|
174
|
+
graph.addEdge(current, name, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
175
|
+
graph.addEdge(name, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
172
176
|
}
|
|
173
177
|
else if (type instanceof Structures.AtFirst
|
|
174
178
|
|| type instanceof Structures.AtLast
|
|
@@ -176,30 +180,30 @@ class StatementFlow {
|
|
|
176
180
|
|| type instanceof Structures.OnChange) {
|
|
177
181
|
const name = this.buildName(n.getFirstStatement());
|
|
178
182
|
const body = this.traverseBody(this.findBody(n), context);
|
|
179
|
-
graph.addEdge(current, name);
|
|
180
|
-
graph.addGraph(name, body);
|
|
181
|
-
graph.addEdge(body.getEnd(), graph.getEnd());
|
|
182
|
-
graph.addEdge(current, graph.getEnd());
|
|
183
|
+
graph.addEdge(current, name, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
184
|
+
graph.addGraph(name, body, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
185
|
+
graph.addEdge(body.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
186
|
+
graph.addEdge(current, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
183
187
|
}
|
|
184
188
|
else if (type instanceof Structures.Try) {
|
|
185
189
|
const tryName = this.buildName(n.getFirstStatement());
|
|
186
190
|
const body = this.traverseBody(this.findBody(n), context);
|
|
187
|
-
graph.addEdge(current, tryName);
|
|
188
|
-
graph.addGraph(tryName, body);
|
|
189
|
-
graph.addEdge(body.getEnd(), graph.getEnd());
|
|
191
|
+
graph.addEdge(current, tryName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
192
|
+
graph.addGraph(tryName, body, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
193
|
+
graph.addEdge(body.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
190
194
|
for (const c of n.findDirectStructures(Structures.Catch)) {
|
|
191
195
|
const catchName = this.buildName(c.getFirstStatement());
|
|
192
196
|
const catchBody = this.traverseBody(this.findBody(c), context);
|
|
193
197
|
// TODO: this does not take exceptions into account
|
|
194
|
-
graph.addEdge(body.getEnd(), catchName);
|
|
195
|
-
graph.addGraph(catchName, catchBody);
|
|
196
|
-
graph.addEdge(catchBody.getEnd(), graph.getEnd());
|
|
198
|
+
graph.addEdge(body.getEnd(), catchName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
199
|
+
graph.addGraph(catchName, catchBody, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
200
|
+
graph.addEdge(catchBody.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
197
201
|
}
|
|
198
202
|
// TODO, handle CLEANUP
|
|
199
203
|
}
|
|
200
204
|
else if (type instanceof Structures.Case) {
|
|
201
205
|
const caseName = this.buildName(n.getFirstStatement());
|
|
202
|
-
graph.addEdge(current, caseName);
|
|
206
|
+
graph.addEdge(current, caseName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
203
207
|
let othersFound = false;
|
|
204
208
|
for (const w of n.findDirectStructures(Structures.When)) {
|
|
205
209
|
const first = w.getFirstStatement();
|
|
@@ -211,17 +215,17 @@ class StatementFlow {
|
|
|
211
215
|
}
|
|
212
216
|
const firstName = this.buildName(first);
|
|
213
217
|
const sub = this.traverseBody(this.findBody(w), context);
|
|
214
|
-
graph.addEdge(caseName, firstName);
|
|
215
|
-
graph.addGraph(firstName, sub);
|
|
216
|
-
graph.addEdge(sub.getEnd(), graph.getEnd());
|
|
218
|
+
graph.addEdge(caseName, firstName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
219
|
+
graph.addGraph(firstName, sub, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
220
|
+
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
217
221
|
}
|
|
218
222
|
if (othersFound === false) {
|
|
219
|
-
graph.addEdge(caseName, graph.getEnd());
|
|
223
|
+
graph.addEdge(caseName, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
220
224
|
}
|
|
221
225
|
}
|
|
222
226
|
else if (type instanceof Structures.CaseType) {
|
|
223
227
|
const caseName = this.buildName(n.getFirstStatement());
|
|
224
|
-
graph.addEdge(current, caseName);
|
|
228
|
+
graph.addEdge(current, caseName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
225
229
|
let othersFound = false;
|
|
226
230
|
for (const w of n.findDirectStructures(Structures.WhenType)) {
|
|
227
231
|
const first = w.getFirstStatement();
|
|
@@ -233,12 +237,12 @@ class StatementFlow {
|
|
|
233
237
|
}
|
|
234
238
|
const firstName = this.buildName(first);
|
|
235
239
|
const sub = this.traverseBody(this.findBody(w), context);
|
|
236
|
-
graph.addEdge(caseName, firstName);
|
|
237
|
-
graph.addGraph(firstName, sub);
|
|
238
|
-
graph.addEdge(sub.getEnd(), graph.getEnd());
|
|
240
|
+
graph.addEdge(caseName, firstName, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
241
|
+
graph.addGraph(firstName, sub, flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
242
|
+
graph.addEdge(sub.getEnd(), graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
239
243
|
}
|
|
240
244
|
if (othersFound === false) {
|
|
241
|
-
graph.addEdge(caseName, graph.getEnd());
|
|
245
|
+
graph.addEdge(caseName, graph.getEnd(), flow_graph_1.FLOW_EDGE_TYPE.undefined);
|
|
242
246
|
}
|
|
243
247
|
}
|
|
244
248
|
else {
|
|
@@ -11,7 +11,7 @@ class CharacterType extends _abstract_type_1.AbstractType {
|
|
|
11
11
|
this.length = length;
|
|
12
12
|
}
|
|
13
13
|
cloneType(input) {
|
|
14
|
-
const clone = Object.assign({}, this.getAbstractTypeData())
|
|
14
|
+
const clone = Object.assign({}, this.getAbstractTypeData());
|
|
15
15
|
if (input.qualifiedName) {
|
|
16
16
|
clone.qualifiedName = input.qualifiedName;
|
|
17
17
|
}
|
package/build/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.LSPEdit = exports.RuleTag = exports.Severity = exports.Visibility = exports.Info = exports.Diagnostics = exports.Rename = exports.PrettyPrinter = exports.Position = void 0;
|
|
3
|
+
exports.ABAPFile = exports.RulesRunner = exports.SpaghettiScope = exports.SyntaxLogic = exports.ABAPObject = exports.Tokens = exports.ExpressionsCDS = exports.CDSParser = exports.LanguageServerTypes = exports.DDLParser = exports.FLOW_EDGE_TYPE = exports.FlowGraph = exports.StatementFlow = exports.NativeSQL = exports.MacroContent = exports.MacroCall = exports.applyEditList = exports.applyEditSingle = exports.SpaghettiScopeNode = exports.AbstractFile = exports.Token = exports.ScopeType = exports.BasicTypes = exports.TypedIdentifier = exports.AbstractType = exports.VirtualPosition = exports.Comment = exports.Unknown = exports.Empty = exports.Identifier = exports.Nodes = exports.Types = exports.Expressions = exports.Statements = exports.Structures = exports.SkipLogic = exports.Objects = exports.ArtifactsRules = exports.ArtifactsObjects = exports.ArtifactsABAP = exports.BuiltIn = exports.MethodLengthStats = exports.LanguageServer = exports.Registry = exports.CyclomaticComplexityStats = exports.ReferenceType = exports.Version = exports.Config = exports.Issue = exports.MemoryFile = void 0;
|
|
4
|
+
exports.LSPEdit = exports.RuleTag = exports.Severity = exports.Visibility = exports.Info = exports.Diagnostics = exports.Rename = exports.PrettyPrinter = exports.Position = exports.CurrentScope = void 0;
|
|
5
5
|
const issue_1 = require("./issue");
|
|
6
6
|
Object.defineProperty(exports, "Issue", { enumerable: true, get: function () { return issue_1.Issue; } });
|
|
7
7
|
const config_1 = require("./config");
|
|
@@ -113,4 +113,5 @@ const statement_flow_1 = require("./abap/flow/statement_flow");
|
|
|
113
113
|
Object.defineProperty(exports, "StatementFlow", { enumerable: true, get: function () { return statement_flow_1.StatementFlow; } });
|
|
114
114
|
const flow_graph_1 = require("./abap/flow/flow_graph");
|
|
115
115
|
Object.defineProperty(exports, "FlowGraph", { enumerable: true, get: function () { return flow_graph_1.FlowGraph; } });
|
|
116
|
+
Object.defineProperty(exports, "FLOW_EDGE_TYPE", { enumerable: true, get: function () { return flow_graph_1.FLOW_EDGE_TYPE; } });
|
|
116
117
|
//# sourceMappingURL=index.js.map
|
package/build/src/lsp/help.js
CHANGED
|
@@ -19,13 +19,13 @@ class Help {
|
|
|
19
19
|
/////////////////////////////////////////////////
|
|
20
20
|
static dumpABAP(file, reg, textDocument, position) {
|
|
21
21
|
let content = "";
|
|
22
|
-
content = `
|
|
23
|
-
<a href="#_tokens" rel="no-refresh">Tokens</a> |
|
|
24
|
-
<a href="#_statements" rel="no-refresh">Statements</a> |
|
|
25
|
-
<a href="#_structure" rel="no-refresh">Structure</a> |
|
|
26
|
-
<a href="#_files" rel="no-refresh">Files</a> |
|
|
27
|
-
<a href="#_info" rel="no-refresh">Info Dump</a>
|
|
28
|
-
<hr>
|
|
22
|
+
content = `
|
|
23
|
+
<a href="#_tokens" rel="no-refresh">Tokens</a> |
|
|
24
|
+
<a href="#_statements" rel="no-refresh">Statements</a> |
|
|
25
|
+
<a href="#_structure" rel="no-refresh">Structure</a> |
|
|
26
|
+
<a href="#_files" rel="no-refresh">Files</a> |
|
|
27
|
+
<a href="#_info" rel="no-refresh">Info Dump</a>
|
|
28
|
+
<hr>
|
|
29
29
|
` +
|
|
30
30
|
"<tt>" + textDocument.uri + " (" +
|
|
31
31
|
(position.line + 1) + ", " +
|