@eagleoutice/flowr 2.8.8 → 2.8.10

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.
Files changed (29) hide show
  1. package/README.md +19 -19
  2. package/abstract-interpretation/absint-visitor.d.ts +2 -2
  3. package/abstract-interpretation/absint-visitor.js +14 -12
  4. package/control-flow/cfg-dead-code.js +38 -21
  5. package/control-flow/semantic-cfg-guided-visitor.d.ts +38 -4
  6. package/control-flow/semantic-cfg-guided-visitor.js +46 -13
  7. package/control-flow/useless-loop.js +17 -20
  8. package/dataflow/environments/built-in-config.d.ts +1 -1
  9. package/dataflow/environments/built-in.js +2 -2
  10. package/dataflow/environments/default-builtin-config.d.ts +3 -3
  11. package/dataflow/environments/default-builtin-config.js +3 -3
  12. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +1 -1
  13. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +1 -1
  14. package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.js +78 -1
  15. package/documentation/wiki-interface.js +1 -1
  16. package/linter/linter-format.d.ts +2 -2
  17. package/linter/linter-rules.d.ts +1 -5
  18. package/linter/rules/dead-code.d.ts +3 -7
  19. package/linter/rules/dead-code.js +25 -7
  20. package/package.json +1 -1
  21. package/project/plugins/file-plugins/files/flowr-namespace-file.js +4 -3
  22. package/queries/catalog/dependencies-query/dependencies-query-executor.js +5 -2
  23. package/queries/catalog/dependencies-query/dependencies-query-format.js +1 -1
  24. package/queries/catalog/linter-query/linter-query-format.js +1 -1
  25. package/r-bridge/lang-4.x/ast/model/processing/decorate.d.ts +6 -1
  26. package/r-bridge/lang-4.x/ast/model/processing/decorate.js +6 -2
  27. package/util/mermaid/cfg.d.ts +6 -1
  28. package/util/mermaid/cfg.js +11 -2
  29. package/util/version.js +1 -1
package/README.md CHANGED
@@ -24,7 +24,7 @@ It offers a wide variety of features, for example:
24
24
 
25
25
  ```shell
26
26
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
27
- flowR repl using flowR v2.8.7, R grammar v14 (tree-sitter engine)
27
+ flowR repl using flowR v2.8.9, R grammar v14 (tree-sitter engine)
28
28
  R> :query @linter "read.csv(\"/root/x.txt\")"
29
29
  ```
30
30
 
@@ -35,11 +35,11 @@ It offers a wide variety of features, for example:
35
35
  ```text
36
36
  Query: linter (2 ms)
37
37
  ╰ Deprecated Functions (deprecated-functions):
38
- ╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0
38
+ ╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0
39
39
  ╰ File Path Validity (file-path-validity):
40
40
  ╰ certain:
41
41
  ╰ Path `/root/x.txt` at 1.1-23
42
- ╰ Metadata: totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 1
42
+ ╰ Metadata: totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 0
43
43
  ╰ Seeded Randomness (seeded-randomness):
44
44
  ╰ Metadata: consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 0
45
45
  ╰ Absolute Paths (absolute-file-paths):
@@ -51,14 +51,14 @@ It offers a wide variety of features, for example:
51
51
  ╰ Naming Convention (naming-convention):
52
52
  ╰ Metadata: numMatches: 0, numBreak: 0, searchTimeMs: 0, processTimeMs: 0
53
53
  ╰ Network Functions (network-functions):
54
- ╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0
54
+ ╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0
55
55
  ╰ Dataframe Access Validation (dataframe-access-validation):
56
56
  ╰ Metadata: numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0
57
57
  ╰ Dead Code (dead-code):
58
58
  ╰ Metadata: consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0
59
59
  ╰ Useless Loops (useless-loop):
60
- ╰ Metadata: numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 1
61
- All queries together required ≈2 ms (1ms accuracy, total 3 ms)
60
+ ╰ Metadata: numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0
61
+ All queries together required ≈2 ms (1ms accuracy, total 2 ms)
62
62
  ```
63
63
 
64
64
 
@@ -82,17 +82,17 @@ It offers a wide variety of features, for example:
82
82
 
83
83
  Query: **linter** (2 ms)\
84
84
     ╰ **Deprecated Functions** (deprecated-functions):\
85
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0</code>\
85
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0</code>\
86
86
  &nbsp;&nbsp;&nbsp;╰ **File Path Validity** (file-path-validity):\
87
87
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ certain:\
88
88
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
89
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 1</code>\
89
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 0</code>\
90
90
  &nbsp;&nbsp;&nbsp;╰ **Seeded Randomness** (seeded-randomness):\
91
91
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 0</code>\
92
92
  &nbsp;&nbsp;&nbsp;╰ **Absolute Paths** (absolute-file-paths):\
93
93
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ certain:\
94
94
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
95
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalConsidered: 1, totalUnknown: 0, searchTimeMs: 0, processTimeMs: 0</code>\
95
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalConsidered: 1, totalUnknown: 0, searchTimeMs: 1, processTimeMs: 0</code>\
96
96
  &nbsp;&nbsp;&nbsp;╰ **Unused Definitions** (unused-definitions):\
97
97
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalConsidered: 0, searchTimeMs: 0, processTimeMs: 0</code>\
98
98
  &nbsp;&nbsp;&nbsp;╰ **Naming Convention** (naming-convention):\
@@ -102,14 +102,14 @@ It offers a wide variety of features, for example:
102
102
  &nbsp;&nbsp;&nbsp;╰ **Dataframe Access Validation** (dataframe-access-validation):\
103
103
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0</code>\
104
104
  &nbsp;&nbsp;&nbsp;╰ **Dead Code** (dead-code):\
105
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consideredNodes: 5, searchTimeMs: 1, processTimeMs: 0</code>\
105
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0</code>\
106
106
  &nbsp;&nbsp;&nbsp;╰ **Useless Loops** (useless-loop):\
107
107
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0</code>\
108
108
  _All queries together required ≈2 ms (1ms accuracy, total 2 ms)_
109
109
 
110
110
  <details> <summary style="color:gray">Show Detailed Results as Json</summary>
111
111
 
112
- The analysis required _2.4 ms_ (including parsing and normalization and the query) within the generation environment.
112
+ The analysis required _2.2 ms_ (including parsing and normalization and the query) within the generation environment.
113
113
 
114
114
  In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
115
115
  Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
@@ -126,7 +126,7 @@ It offers a wide variety of features, for example:
126
126
  ".meta": {
127
127
  "totalCalls": 0,
128
128
  "totalFunctionDefinitions": 0,
129
- "searchTimeMs": 0,
129
+ "searchTimeMs": 1,
130
130
  "processTimeMs": 0
131
131
  }
132
132
  },
@@ -150,7 +150,7 @@ It offers a wide variety of features, for example:
150
150
  "totalWritesBeforeAlways": 0,
151
151
  "totalValid": 0,
152
152
  "searchTimeMs": 0,
153
- "processTimeMs": 1
153
+ "processTimeMs": 0
154
154
  }
155
155
  },
156
156
  "seeded-randomness": {
@@ -181,7 +181,7 @@ It offers a wide variety of features, for example:
181
181
  ".meta": {
182
182
  "totalConsidered": 1,
183
183
  "totalUnknown": 0,
184
- "searchTimeMs": 0,
184
+ "searchTimeMs": 1,
185
185
  "processTimeMs": 0
186
186
  }
187
187
  },
@@ -225,7 +225,7 @@ It offers a wide variety of features, for example:
225
225
  "results": [],
226
226
  ".meta": {
227
227
  "consideredNodes": 5,
228
- "searchTimeMs": 1,
228
+ "searchTimeMs": 0,
229
229
  "processTimeMs": 0
230
230
  }
231
231
  },
@@ -308,7 +308,7 @@ It offers a wide variety of features, for example:
308
308
 
309
309
  ```shell
310
310
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
311
- flowR repl using flowR v2.8.7, R grammar v14 (tree-sitter engine)
311
+ flowR repl using flowR v2.8.9, R grammar v14 (tree-sitter engine)
312
312
  R> :query @static-slice (11@sum) file://test/testfiles/example.R
313
313
  ```
314
314
 
@@ -356,7 +356,7 @@ It offers a wide variety of features, for example:
356
356
 
357
357
 
358
358
  * 🚀 **fast call-graph, data-, and control-flow graphs**\
359
- Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">119.5 ms</span></i> (as of Jan 16, 2026)](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark),
359
+ Within just [<i><span title="This measurement is automatically fetched from the latest benchmark!">117 ms</span></i> (as of Jan 18, 2026)](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark),
360
360
  _flowR_ can analyze the data- and control-flow of the average real-world R script. See the [benchmarks](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark) for more information,
361
361
  and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/dataflow-graph) for more details on the dataflow graphs as well as call graphs.
362
362
 
@@ -392,7 +392,7 @@ It offers a wide variety of features, for example:
392
392
 
393
393
  ```shell
394
394
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
395
- flowR repl using flowR v2.8.7, R grammar v14 (tree-sitter engine)
395
+ flowR repl using flowR v2.8.9, R grammar v14 (tree-sitter engine)
396
396
  R> :dataflow* test/testfiles/example.R
397
397
  ```
398
398
 
@@ -697,7 +697,7 @@ It offers a wide variety of features, for example:
697
697
  ```
698
698
 
699
699
 
700
- (The analysis required _2.4 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
700
+ (The analysis required _2.0 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
701
701
 
702
702
 
703
703
 
@@ -1,7 +1,7 @@
1
1
  import type { CfgSimpleVertex, ControlFlowInformation } from '../control-flow/control-flow-graph';
2
2
  import type { SemanticCfgGuidedVisitorConfiguration } from '../control-flow/semantic-cfg-guided-visitor';
3
3
  import { SemanticCfgGuidedVisitor } from '../control-flow/semantic-cfg-guided-visitor';
4
- import { BuiltInProcessorMapper } from '../dataflow/environments/built-in';
4
+ import { BuiltInProcName } from '../dataflow/environments/built-in';
5
5
  import type { DataflowGraph } from '../dataflow/graph/graph';
6
6
  import { type DataflowGraphVertexFunctionCall, type DataflowGraphVertexVariableDefinition } from '../dataflow/graph/vertex';
7
7
  import type { NoInfo, RNode } from '../r-bridge/lang-4.x/ast/model/model';
@@ -62,7 +62,7 @@ export declare abstract class AbstractInterpretationVisitor<Domain extends AnyAb
62
62
  getAbstractTrace(): ReadonlyMap<NodeId, StateAbstractDomain<Domain>>;
63
63
  start(): void;
64
64
  protected visitNode(vertexId: NodeId): boolean;
65
- protected onDispatchFunctionCallOrigin(call: DataflowGraphVertexFunctionCall, origin: keyof typeof BuiltInProcessorMapper | string): void;
65
+ protected onDispatchFunctionCallOrigin(call: DataflowGraphVertexFunctionCall, origin: BuiltInProcName): void;
66
66
  protected onVariableDefinition({ vertex }: {
67
67
  vertex: DataflowGraphVertexVariableDefinition;
68
68
  }): void;
@@ -151,19 +151,21 @@ class AbstractInterpretationVisitor extends semantic_cfg_guided_visitor_1.Semant
151
151
  }
152
152
  onDispatchFunctionCallOrigin(call, origin) {
153
153
  super.onDispatchFunctionCallOrigin(call, origin);
154
- if (!(origin in built_in_1.BuiltInProcessorMapper)) {
155
- return this.onFunctionCall({ call });
156
- }
157
154
  switch (origin) {
158
- case 'builtin:expression-list':
159
- case 'builtin:if-then-else':
160
- case 'builtin:for-loop':
161
- case 'builtin:while-loop':
162
- case 'builtin:repeat-loop':
163
- case 'builtin:assignment':
164
- case 'builtin:assignment-like':
165
- case 'builtin:replacement':
166
- case 'builtin:access':
155
+ case built_in_1.BuiltInProcName.ExpressionList:
156
+ case built_in_1.BuiltInProcName.IfThenElse:
157
+ case built_in_1.BuiltInProcName.ForLoop:
158
+ case built_in_1.BuiltInProcName.WhileLoop:
159
+ case built_in_1.BuiltInProcName.RepeatLoop:
160
+ case built_in_1.BuiltInProcName.FunctionDefinition:
161
+ case built_in_1.BuiltInProcName.Assignment:
162
+ case built_in_1.BuiltInProcName.AssignmentLike:
163
+ case built_in_1.BuiltInProcName.TableAssignment:
164
+ case built_in_1.BuiltInProcName.Replacement:
165
+ case built_in_1.BuiltInProcName.Access:
166
+ case built_in_1.BuiltInProcName.Pipe:
167
+ case built_in_1.BuiltInProcName.Break:
168
+ case built_in_1.BuiltInProcName.Return:
167
169
  return;
168
170
  default:
169
171
  return this.onFunctionCall({ call });
@@ -8,9 +8,11 @@ const log_1 = require("../util/log");
8
8
  const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
9
9
  const general_1 = require("../dataflow/eval/values/general");
10
10
  const r_value_1 = require("../dataflow/eval/values/r-value");
11
+ const simple_visitor_1 = require("./simple-visitor");
11
12
  class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor {
12
13
  cachedConditions = new Map();
13
14
  cachedStatements = new Map();
15
+ inTry = new Set();
14
16
  getValue(id) {
15
17
  const has = this.cachedConditions.get(id);
16
18
  if (has) {
@@ -20,6 +22,9 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
20
22
  return this.cachedConditions.get(id) ?? logic_1.Ternary.Maybe;
21
23
  }
22
24
  isUnconditionalJump(id) {
25
+ if (this.inTry.has(id)) {
26
+ return false;
27
+ }
23
28
  const has = this.cachedStatements.get(id);
24
29
  if (has) {
25
30
  return has;
@@ -45,9 +50,14 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
45
50
  this.config.controlFlow.graph.removeEdge(from, target);
46
51
  }
47
52
  }
48
- else if (edge.label === 0 /* CfgEdgeType.Fd */) {
49
- if (this.isUnconditionalJump(target)) {
50
- this.config.controlFlow.graph.removeEdge(from, target);
53
+ else if (edge.label === 0 /* CfgEdgeType.Fd */ && this.isUnconditionalJump(target)) {
54
+ // for each unconditional jump, we find the corresponding end/exit nodes and remove any flow edges
55
+ for (const end of this.getCfgVertex(target)?.end ?? []) {
56
+ for (const [target, edge] of this.config.controlFlow.graph.ingoingEdges(end) ?? []) {
57
+ if (edge.label === 0 /* CfgEdgeType.Fd */) {
58
+ this.config.controlFlow.graph.removeEdge(target, end);
59
+ }
60
+ }
51
61
  }
52
62
  }
53
63
  }
@@ -91,32 +101,39 @@ class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.Semant
91
101
  }
92
102
  return Boolean(values.elements[0].value);
93
103
  }
94
- handleFunctionCall(data) {
95
- switch (data.call.origin[0]) {
96
- case 'builtin:return':
97
- case 'builtin:stop':
98
- this.cachedStatements.set(data.call.id, true);
99
- break;
100
- case 'builtin:stopifnot': {
101
- const arg = this.getBoolArgValue(data);
102
- if (arg !== undefined) {
103
- this.cachedStatements.set(data.call.id, !arg);
104
- }
105
- break;
106
- }
107
- }
108
- }
109
104
  onIfThenElseCall(data) {
110
105
  this.handleWithCondition(data);
111
106
  }
112
107
  onWhileLoopCall(data) {
113
108
  this.handleWithCondition(data);
114
109
  }
115
- onDefaultFunctionCall(data) {
116
- this.handleFunctionCall(data);
110
+ onReturnCall(data) {
111
+ this.cachedStatements.set(data.call.id, true);
112
+ }
113
+ onStopCall(data) {
114
+ this.cachedStatements.set(data.call.id, true);
117
115
  }
118
116
  onStopIfNotCall(data) {
119
- this.handleFunctionCall(data);
117
+ const arg = this.getBoolArgValue(data);
118
+ if (arg !== undefined) {
119
+ this.cachedStatements.set(data.call.id, !arg);
120
+ }
121
+ }
122
+ onTryCall(data) {
123
+ if (data.call.args.length < 1 || data.call.args[0] === r_function_call_1.EmptyArgument) {
124
+ return;
125
+ }
126
+ const body = this.getCfgVertex(data.call.args[0].nodeId);
127
+ if (!body) {
128
+ return;
129
+ }
130
+ (0, simple_visitor_1.visitCfgInOrder)(this.config.controlFlow.graph, [body.id], n => {
131
+ if (body.end?.includes(n)) {
132
+ return true;
133
+ }
134
+ this.inTry.add(n);
135
+ return false;
136
+ });
120
137
  }
121
138
  }
122
139
  /** Breaks unsatisfiable control dependencies */
@@ -11,9 +11,10 @@ import type { RLogical } from '../r-bridge/lang-4.x/ast/model/nodes/r-logical';
11
11
  import type { DataflowGraph, FunctionArgument } from '../dataflow/graph/graph';
12
12
  import type { NoInfo, RNode } from '../r-bridge/lang-4.x/ast/model/model';
13
13
  import type { RSymbol } from '../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
14
- import { BuiltInProcessorMapper } from '../dataflow/environments/built-in';
14
+ import { BuiltInProcName } from '../dataflow/environments/built-in';
15
15
  import type { RExpressionList } from '../r-bridge/lang-4.x/ast/model/nodes/r-expression-list';
16
16
  import type { ReadOnlyFlowrAnalyzerContext } from '../project/context/flowr-analyzer-context';
17
+ import { RNull } from '../r-bridge/lang-4.x/convert-values';
17
18
  export interface SemanticCfgGuidedVisitorConfiguration<OtherInfo = NoInfo, ControlFlow extends ControlFlowInformation = ControlFlowInformation, Ast extends NormalizedAst<OtherInfo> = NormalizedAst<OtherInfo>, Dfg extends DataflowGraph = DataflowGraph> extends DataflowCfgGuidedVisitorConfiguration<ControlFlow, Dfg>, SyntaxCfgGuidedVisitorConfiguration<OtherInfo, ControlFlow, Ast> {
18
19
  readonly ctx: ReadOnlyFlowrAnalyzerContext;
19
20
  }
@@ -100,7 +101,7 @@ export declare class SemanticCfgGuidedVisitor<OtherInfo = NoInfo, ControlFlow ex
100
101
  * with the logic you desire.
101
102
  * @protected
102
103
  */
103
- protected onDispatchFunctionCallOrigins(call: DataflowGraphVertexFunctionCall, origins: readonly string[]): void;
104
+ protected onDispatchFunctionCallOrigins(call: DataflowGraphVertexFunctionCall, origins: readonly BuiltInProcName[]): void;
104
105
  /**
105
106
  * This function is responsible for dispatching the appropriate event
106
107
  * based on a given dataflow vertex. The default serves as a backend
@@ -108,7 +109,7 @@ export declare class SemanticCfgGuidedVisitor<OtherInfo = NoInfo, ControlFlow ex
108
109
  * @see {@link onDispatchFunctionCallOrigins} for the aggregation in case the function call target is ambiguous.
109
110
  * @protected
110
111
  */
111
- protected onDispatchFunctionCallOrigin(call: DataflowGraphVertexFunctionCall, origin: keyof typeof BuiltInProcessorMapper | string): void;
112
+ protected onDispatchFunctionCallOrigin(call: DataflowGraphVertexFunctionCall, origin: BuiltInProcName): void;
112
113
  /**
113
114
  * This event is called for the root program node, i.e., the program that is being analyzed.
114
115
  * @protected
@@ -125,7 +126,7 @@ export declare class SemanticCfgGuidedVisitor<OtherInfo = NoInfo, ControlFlow ex
125
126
  */
126
127
  protected onNullConstant(_data: {
127
128
  vertex: DataflowGraphVertexValue;
128
- node: RSymbol<OtherInfo & ParentInformation, 'NULL'>;
129
+ node: RSymbol<OtherInfo & ParentInformation, typeof RNull>;
129
130
  }): void;
130
131
  /**
131
132
  * Called for every constant string value in the program.
@@ -447,6 +448,17 @@ export declare class SemanticCfgGuidedVisitor<OtherInfo = NoInfo, ControlFlow ex
447
448
  protected onVectorCall(_data: {
448
449
  call: DataflowGraphVertexFunctionCall;
449
450
  }): void;
451
+ /**
452
+ * This event triggers for every call to the `stop` function.
453
+ *
454
+ * For example, this triggers for `stop` in `stop()`.
455
+ *
456
+ * More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
457
+ * @protected
458
+ */
459
+ protected onStopCall(_data: {
460
+ call: DataflowGraphVertexFunctionCall;
461
+ }): void;
450
462
  /**
451
463
  * This event triggers for every call to the `stopifnot` function.
452
464
  *
@@ -500,4 +512,26 @@ export declare class SemanticCfgGuidedVisitor<OtherInfo = NoInfo, ControlFlow ex
500
512
  protected onRegisterHookCall(_data: {
501
513
  call: DataflowGraphVertexFunctionCall;
502
514
  }): void;
515
+ /**
516
+ * This event triggers for every call to `break` to exit a loop.
517
+ *
518
+ * For example, this triggers for `break` in `repeat { break }`.
519
+ *
520
+ * More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
521
+ * @protected
522
+ */
523
+ protected onBreakCall(_data: {
524
+ call: DataflowGraphVertexFunctionCall;
525
+ }): void;
526
+ /**
527
+ * This event triggers for every call to `return` to explicitly return a value in a function.
528
+ *
529
+ * For example, this triggers for `return` in `f <- function() { return(42) }`.
530
+ *
531
+ * More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
532
+ * @protected
533
+ */
534
+ protected onReturnCall(_data: {
535
+ call: DataflowGraphVertexFunctionCall;
536
+ }): void;
503
537
  }
@@ -8,6 +8,7 @@ const edge_1 = require("../dataflow/graph/edge");
8
8
  const assert_1 = require("../util/assert");
9
9
  const built_in_1 = require("../dataflow/environments/built-in");
10
10
  const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
11
+ const convert_values_1 = require("../r-bridge/lang-4.x/convert-values");
11
12
  /**
12
13
  * This visitor extends on the {@link DataflowAwareCfgGuidedVisitor} by dispatching visitors for separate function calls as well,
13
14
  * providing more information!
@@ -53,7 +54,7 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
53
54
  case type_1.RType.Number: return this.onNumberConstant({ vertex: val, node: astNode });
54
55
  case type_1.RType.Logical: return this.onLogicalConstant({ vertex: val, node: astNode });
55
56
  case type_1.RType.Symbol:
56
- if (astNode.lexeme === 'NULL') {
57
+ if (astNode.lexeme === convert_values_1.RNull) {
57
58
  return this.onNullConstant({
58
59
  vertex: val,
59
60
  node: astNode
@@ -115,7 +116,7 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
115
116
  */
116
117
  visitFunctionCall(vertex) {
117
118
  super.visitFunctionCall(vertex);
118
- if (vertex.origin === 'unnamed') {
119
+ if (vertex.origin === built_in_1.BuiltInProcName.Unnamed) {
119
120
  this.onUnnamedCall({ call: vertex });
120
121
  }
121
122
  else {
@@ -157,11 +158,7 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
157
158
  * @protected
158
159
  */
159
160
  onDispatchFunctionCallOrigin(call, origin) {
160
- if (!(origin in built_in_1.BuiltInProcessorMapper)) {
161
- return this.onDefaultFunctionCall({ call });
162
- }
163
- const type = origin;
164
- switch (type) {
161
+ switch (origin) {
165
162
  case built_in_1.BuiltInProcName.Eval:
166
163
  return this.onEvalFunctionCall({ call });
167
164
  case built_in_1.BuiltInProcName.Apply:
@@ -201,7 +198,8 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
201
198
  case built_in_1.BuiltInProcName.Vector:
202
199
  return this.onVectorCall({ call });
203
200
  case built_in_1.BuiltInProcName.Assignment:
204
- case built_in_1.BuiltInProcName.AssignmentLike: {
201
+ case built_in_1.BuiltInProcName.AssignmentLike:
202
+ case built_in_1.BuiltInProcName.TableAssignment: {
205
203
  const outgoing = this.config.dfg.outgoingEdges(call.id);
206
204
  if (outgoing) {
207
205
  const target = [...outgoing.entries()].filter(([, e]) => (0, edge_1.edgeIncludesType)(e.types, edge_1.EdgeType.Returns));
@@ -253,22 +251,30 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
253
251
  }
254
252
  case built_in_1.BuiltInProcName.Library:
255
253
  return this.onLibraryCall({ call });
256
- case built_in_1.BuiltInProcName.Default:
257
- return this.onDefaultFunctionCall({ call });
258
254
  case built_in_1.BuiltInProcName.Try:
259
255
  return this.onTryCall({ call });
256
+ case built_in_1.BuiltInProcName.Stop:
257
+ return this.onStopCall({ call });
260
258
  case built_in_1.BuiltInProcName.StopIfNot:
261
259
  return this.onStopIfNotCall({ call });
262
260
  case built_in_1.BuiltInProcName.RegisterHook:
263
261
  return this.onRegisterHookCall({ call });
264
262
  case built_in_1.BuiltInProcName.Local:
265
263
  return this.onLocalCall({ call });
266
- case built_in_1.BuiltInProcName.FunctionDefinition:
267
- throw new Error('Function call vertex cannot be a function definition');
268
264
  case built_in_1.BuiltInProcName.S3Dispatch:
269
265
  return this.onS3DispatchCall({ call });
266
+ case built_in_1.BuiltInProcName.Break:
267
+ return this.onBreakCall({ call });
268
+ case built_in_1.BuiltInProcName.Return:
269
+ return this.onReturnCall({ call });
270
+ case built_in_1.BuiltInProcName.Unnamed:
271
+ return this.onUnnamedCall({ call });
272
+ case built_in_1.BuiltInProcName.Default:
273
+ case built_in_1.BuiltInProcName.Function:
274
+ case built_in_1.BuiltInProcName.FunctionDefinition:
275
+ return this.onDefaultFunctionCall({ call });
270
276
  default:
271
- (0, assert_1.assertUnreachable)(type);
277
+ (0, assert_1.assertUnreachable)(origin);
272
278
  }
273
279
  }
274
280
  /**
@@ -530,6 +536,15 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
530
536
  * @protected
531
537
  */
532
538
  onVectorCall(_data) { }
539
+ /**
540
+ * This event triggers for every call to the `stop` function.
541
+ *
542
+ * For example, this triggers for `stop` in `stop()`.
543
+ *
544
+ * More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
545
+ * @protected
546
+ */
547
+ onStopCall(_data) { }
533
548
  /**
534
549
  * This event triggers for every call to the `stopifnot` function.
535
550
  *
@@ -573,6 +588,24 @@ class SemanticCfgGuidedVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfg
573
588
  * @protected
574
589
  */
575
590
  onRegisterHookCall(_data) { }
591
+ /**
592
+ * This event triggers for every call to `break` to exit a loop.
593
+ *
594
+ * For example, this triggers for `break` in `repeat { break }`.
595
+ *
596
+ * More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
597
+ * @protected
598
+ */
599
+ onBreakCall(_data) { }
600
+ /**
601
+ * This event triggers for every call to `return` to explicitly return a value in a function.
602
+ *
603
+ * For example, this triggers for `return` in `f <- function() { return(42) }`.
604
+ *
605
+ * More specifically, this relates to the corresponding {@link BuiltInProcessorMapper} handler.
606
+ * @protected
607
+ */
608
+ onReturnCall(_data) { }
576
609
  }
577
610
  exports.SemanticCfgGuidedVisitor = SemanticCfgGuidedVisitor;
578
611
  //# sourceMappingURL=semantic-cfg-guided-visitor.js.map
@@ -29,7 +29,7 @@ function onlyLoopsOnce(loop, dataflow, controlflow, ast, ctx) {
29
29
  (0, assert_1.guard)(vertex.tag === vertex_1.VertexType.FunctionCall, 'invalid vertex type for onlyLoopsOnce');
30
30
  (0, assert_1.guard)(vertex.origin !== 'unnamed' && exports.loopyFunctions.has(vertex.origin[0]), 'onlyLoopsOnce can only be called with loops');
31
31
  // 1. In case of for loop, check if vector has only one element
32
- if (vertex.origin[0] === 'builtin:for-loop') {
32
+ if (vertex.origin[0] === built_in_1.BuiltInProcName.ForLoop) {
33
33
  if (vertex.args.length < 2) {
34
34
  return undefined;
35
35
  }
@@ -120,28 +120,25 @@ class CfgSingleIterationLoopDetector extends semantic_cfg_guided_visitor_1.Seman
120
120
  }
121
121
  }
122
122
  }
123
- handleFunctionCall(data) {
124
- for (const origin of data.call.origin) {
125
- if (origin === 'builtin:stop' || origin === 'builtin:return' || origin === 'builtin:break') {
126
- this.encounteredLoopBreaker = true;
127
- this.app(data.call.cds);
128
- return;
129
- }
130
- else if (origin === 'builtin:stopifnot') {
131
- const arg = this.getBoolArgValue(data);
132
- if (arg === false) {
133
- this.encounteredLoopBreaker = true;
134
- this.app(data.call.cds);
135
- return;
136
- }
137
- }
138
- }
123
+ onBreakCall(data) {
124
+ this.encounteredLoopBreaker = true;
125
+ this.app(data.call.cds);
139
126
  }
140
- onDefaultFunctionCall(data) {
141
- this.handleFunctionCall(data);
127
+ onReturnCall(data) {
128
+ this.encounteredLoopBreaker = true;
129
+ this.app(data.call.cds);
130
+ }
131
+ onStopCall(data) {
132
+ this.encounteredLoopBreaker = true;
133
+ this.app(data.call.cds);
142
134
  }
143
135
  onStopIfNotCall(data) {
144
- this.handleFunctionCall(data);
136
+ const arg = this.getBoolArgValue(data);
137
+ if (arg === false) {
138
+ this.encounteredLoopBreaker = true;
139
+ this.app(data.call.cds);
140
+ return;
141
+ }
145
142
  }
146
143
  loopsOnlyOnce() {
147
144
  this.startVisitor([]);
@@ -32,7 +32,7 @@ export interface BuiltInFunctionDefinition<BuiltInProcessor extends keyof typeof
32
32
  }
33
33
  /**
34
34
  * Define a built-in replacement (like `[` or `$`) and the processor to use.
35
- * This is a convenience for manually combined function calls with `builtin:replacement`.
35
+ * This is a convenience for manually combined replacement function calls.
36
36
  */
37
37
  export interface BuiltInReplacementDefinition extends BaseBuiltInDefinition {
38
38
  readonly type: 'replacement';
@@ -268,8 +268,8 @@ class BuiltIns {
268
268
  * Registers all combinations of replacements
269
269
  */
270
270
  registerReplacementFunctions({ names, suffixes, assumePrimitive, config }) {
271
- const replacer = exports.BuiltInProcessorMapper['builtin:replacement'];
272
- (0, assert_1.guard)(replacer !== undefined, () => 'Processor for builtin:replacement is undefined!');
271
+ const replacer = exports.BuiltInProcessorMapper[BuiltInProcName.Replacement];
272
+ (0, assert_1.guard)(replacer !== undefined, () => `Processor for ${BuiltInProcName.Replacement} is undefined!`);
273
273
  for (const assignment of names) {
274
274
  for (const suffix of suffixes) {
275
275
  const effectiveName = `${assignment}${suffix}`;
@@ -257,7 +257,7 @@ export declare const DefaultBuiltinConfig: [{
257
257
  readonly config: {
258
258
  readonly returnsNthArgument: 0;
259
259
  readonly cfg: ExitPointType.Return;
260
- readonly useAsProcessor: "builtin:return";
260
+ readonly useAsProcessor: BuiltInProcName.Return;
261
261
  };
262
262
  readonly assumePrimitive: true;
263
263
  }, {
@@ -302,7 +302,7 @@ export declare const DefaultBuiltinConfig: [{
302
302
  readonly names: ["break"];
303
303
  readonly processor: BuiltInProcName.Default;
304
304
  readonly config: {
305
- readonly useAsProcessor: "builtin:break";
305
+ readonly useAsProcessor: BuiltInProcName.Break;
306
306
  readonly cfg: ExitPointType.Break;
307
307
  };
308
308
  readonly assumePrimitive: false;
@@ -604,7 +604,7 @@ export declare const DefaultBuiltinConfig: [{
604
604
  readonly processor: BuiltInProcName.Vector;
605
605
  readonly config: {};
606
606
  readonly assumePrimitive: true;
607
- readonly evalHandler: "builtin:c";
607
+ readonly evalHandler: "built-in:c";
608
608
  }, {
609
609
  readonly type: "function";
610
610
  readonly names: ["setnames", "setNames", "setkey", "setkeyv", "setindex", "setindexv", "setattr"];
@@ -232,7 +232,7 @@ exports.DefaultBuiltinConfig = [
232
232
  { type: 'function', names: ['eval'], processor: built_in_1.BuiltInProcName.Eval, config: { includeFunctionCall: true }, assumePrimitive: true },
233
233
  { type: 'function', names: ['cat'], processor: built_in_1.BuiltInProcName.Default, config: { forceArgs: 'all', hasUnknownSideEffects: { type: 'link-to-last-call', callName: /^sink$/ } }, assumePrimitive: false },
234
234
  { type: 'function', names: ['switch'], processor: built_in_1.BuiltInProcName.Default, config: { forceArgs: [true] }, assumePrimitive: false },
235
- { type: 'function', names: ['return'], processor: built_in_1.BuiltInProcName.Default, config: { returnsNthArgument: 0, cfg: 1 /* ExitPointType.Return */, useAsProcessor: 'builtin:return' }, assumePrimitive: true },
235
+ { type: 'function', names: ['return'], processor: built_in_1.BuiltInProcName.Default, config: { returnsNthArgument: 0, cfg: 1 /* ExitPointType.Return */, useAsProcessor: built_in_1.BuiltInProcName.Return }, assumePrimitive: true },
236
236
  {
237
237
  type: 'function',
238
238
  names: ['stop', 'abort', 'cli_abort', 'throw', 'stop_bad_type', 'stop_bad_element_type', 'stop_bad_element_length'],
@@ -243,7 +243,7 @@ exports.DefaultBuiltinConfig = [
243
243
  { type: 'function', names: ['try'], processor: built_in_1.BuiltInProcName.Try, config: { block: 'expr', handlers: {} }, assumePrimitive: true },
244
244
  { type: 'function', names: ['tryCatch', 'tryCatchLog'], processor: built_in_1.BuiltInProcName.Try, config: { block: 'expr', handlers: { error: 'error', finally: 'finally' } }, assumePrimitive: true },
245
245
  { type: 'function', names: ['stopifnot'], processor: built_in_1.BuiltInProcName.StopIfNot, config: {}, assumePrimitive: false },
246
- { type: 'function', names: ['break'], processor: built_in_1.BuiltInProcName.Default, config: { useAsProcessor: 'builtin:break', cfg: 2 /* ExitPointType.Break */ }, assumePrimitive: false },
246
+ { type: 'function', names: ['break'], processor: built_in_1.BuiltInProcName.Default, config: { useAsProcessor: built_in_1.BuiltInProcName.Break, cfg: 2 /* ExitPointType.Break */ }, assumePrimitive: false },
247
247
  { type: 'function', names: ['next'], processor: built_in_1.BuiltInProcName.Default, config: { cfg: 3 /* ExitPointType.Next */ }, assumePrimitive: false },
248
248
  { type: 'function', names: ['{'], processor: built_in_1.BuiltInProcName.ExpressionList, config: {}, assumePrimitive: true },
249
249
  { type: 'function', names: ['source'], processor: built_in_1.BuiltInProcName.Source, config: { includeFunctionCall: true, forceFollow: false }, assumePrimitive: false },
@@ -278,7 +278,7 @@ exports.DefaultBuiltinConfig = [
278
278
  { type: 'function', names: ['interference'], processor: built_in_1.BuiltInProcName.Apply, config: { unquoteFunction: true, nameOfFunctionArgument: 'propensity_integrand', libFn: true }, assumePrimitive: false },
279
279
  { type: 'function', names: ['ddply'], processor: built_in_1.BuiltInProcName.Apply, config: { unquoteFunction: true, indexOfFunction: 2, nameOfFunctionArgument: '.fun', libFn: true }, assumePrimitive: false },
280
280
  { type: 'function', names: ['list'], processor: built_in_1.BuiltInProcName.List, config: {}, assumePrimitive: true },
281
- { type: 'function', names: ['c'], processor: built_in_1.BuiltInProcName.Vector, config: {}, assumePrimitive: true, evalHandler: 'builtin:c' },
281
+ { type: 'function', names: ['c'], processor: built_in_1.BuiltInProcName.Vector, config: {}, assumePrimitive: true, evalHandler: 'built-in:c' },
282
282
  {
283
283
  type: 'function',
284
284
  names: ['setnames', 'setNames', 'setkey', 'setkeyv', 'setindex', 'setindexv', 'setattr'],
@@ -56,7 +56,7 @@ function tryReplacementPassingIndices(rootId, functionName, data, name, args, in
56
56
  if (resolved.length !== 1 || resolved[0].type !== identifier_1.ReferenceType.BuiltInFunction) {
57
57
  return (0, process_named_call_1.processAsNamedCall)(functionName, data, name, args);
58
58
  }
59
- const info = built_in_1.BuiltInProcessorMapper['builtin:replacement']({
59
+ const info = built_in_1.BuiltInProcessorMapper[built_in_1.BuiltInProcName.Replacement]({
60
60
  type: type_1.RType.Symbol,
61
61
  info: functionName.info,
62
62
  content: name,
@@ -35,7 +35,7 @@ args, rootId, data, config) {
35
35
  indices ??= constructAccessedIndices(name.content, args);
36
36
  }
37
37
  /* we assign the first argument by the last for now and maybe mark as maybe!, we can keep the symbol as we now know we have an assignment */
38
- let res = built_in_1.BuiltInProcessorMapper['builtin:assignment'](name, [args[0], args.at(-1)], rootId, data, {
38
+ let res = built_in_1.BuiltInProcessorMapper[built_in_1.BuiltInProcName.Assignment](name, [args[0], args.at(-1)], rootId, data, {
39
39
  superAssignment: config.assignmentOperator === '<<-',
40
40
  makeMaybe: indices === undefined ? config.makeMaybe : false,
41
41
  indicesCollection: indices,
@@ -12,6 +12,10 @@ const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
12
12
  const assert_1 = require("../../../../../../util/assert");
13
13
  const edge_1 = require("../../../../../graph/edge");
14
14
  const built_in_1 = require("../../../../../environments/built-in");
15
+ const unnamed_call_handling_1 = require("../unnamed-call-handling");
16
+ const identifier_1 = require("../../../../../environments/identifier");
17
+ const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
18
+ const log_1 = require("../../../../../../util/log");
15
19
  function getArgsOfName(argMaps, name) {
16
20
  return new Set(argMaps.entries().filter(([, v]) => v === name).map(([k]) => k));
17
21
  }
@@ -72,7 +76,11 @@ function processTryCatch(name, args, rootId, data, config) {
72
76
  }
73
77
  }
74
78
  for (const e of errorArg) {
75
- info.graph.addEdge(rootId, e, edge_1.EdgeType.Calls);
79
+ info.graph.addEdge(rootId, e, edge_1.EdgeType.Reads | edge_1.EdgeType.Calls);
80
+ const linkTo = promoteCallToFunction(rootId, e, info, data);
81
+ if (linkTo) {
82
+ info.graph.addEdge(e, linkTo, edge_1.EdgeType.Calls);
83
+ }
76
84
  }
77
85
  for (const f of finallyArg) {
78
86
  info.graph.addEdge(rootId, f, edge_1.EdgeType.Calls);
@@ -84,6 +92,75 @@ function processTryCatch(name, args, rootId, data, config) {
84
92
  }
85
93
  return info;
86
94
  }
95
+ function promoteCallToFunction(call, arg, info, data) {
96
+ let functionId = undefined;
97
+ let functionName = undefined;
98
+ let anonymous = false;
99
+ const argNode = data.completeAst.idMap.get(arg);
100
+ if (!argNode) {
101
+ return undefined;
102
+ }
103
+ const val = argNode.type === type_1.RType.Argument ? (0, unpack_argument_1.unpackArg)(argNode) : argNode;
104
+ if (!val) {
105
+ return undefined;
106
+ }
107
+ if (val.type === type_1.RType.Symbol) {
108
+ functionId = val.info.id;
109
+ functionName = val.content;
110
+ }
111
+ else if (val.type === type_1.RType.FunctionDefinition) {
112
+ anonymous = true;
113
+ functionId = val.info.id;
114
+ functionName = `${unnamed_call_handling_1.UnnamedFunctionCallPrefix}${functionId}`;
115
+ }
116
+ if (functionName === undefined || functionId === undefined) {
117
+ return undefined;
118
+ }
119
+ if (anonymous) {
120
+ info.graph.addEdge(arg, functionId, edge_1.EdgeType.Calls | edge_1.EdgeType.Reads);
121
+ const dfVert = info.graph.getVertex(call);
122
+ if (dfVert && dfVert.tag === vertex_1.VertexType.FunctionDefinition) {
123
+ // resolve all ingoings against the environment
124
+ const ingoingRefs = dfVert.subflow.in;
125
+ const remainingIn = [];
126
+ for (const ingoing of ingoingRefs) {
127
+ const resolved = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, data.environment, ingoing.type) : undefined;
128
+ if (resolved === undefined) {
129
+ remainingIn.push(ingoing);
130
+ continue;
131
+ }
132
+ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${ingoing.nodeId} in closure of function definition ${call}`);
133
+ let allBuiltIn = true;
134
+ for (const ref of resolved) {
135
+ info.graph.addEdge(ingoing, ref, edge_1.EdgeType.Reads);
136
+ info.graph.addEdge(call, ref, edge_1.EdgeType.Reads); // because the def. is the anonymous call
137
+ if (!(0, identifier_1.isReferenceType)(ref.type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
138
+ allBuiltIn = false;
139
+ }
140
+ }
141
+ if (allBuiltIn) {
142
+ remainingIn.push(ingoing);
143
+ }
144
+ }
145
+ dfVert.subflow.in = remainingIn;
146
+ }
147
+ // we did the linking
148
+ return undefined;
149
+ }
150
+ else {
151
+ info.graph.updateToFunctionCall({
152
+ tag: vertex_1.VertexType.FunctionCall,
153
+ id: functionId,
154
+ name: functionName,
155
+ args: [],
156
+ environment: data.environment,
157
+ onlyBuiltin: false,
158
+ cds: data.cds,
159
+ origin: [built_in_1.BuiltInProcName.Function]
160
+ });
161
+ return functionId;
162
+ }
163
+ }
87
164
  function getExitPoints(vertex, graph) {
88
165
  if (!vertex) {
89
166
  return undefined;
@@ -291,7 +291,7 @@ ${(0, doc_code_1.codeBlock)('json', JSON.stringify({
291
291
  | Type | Description | Example |
292
292
  | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
293
293
  | \`constant\` | Additionally allows for a \`value\` this should resolve to. | \`{ type: 'constant', names: ['NULL', 'NA'], value: null }\` |
294
- | \`function\` | Is a rather flexible way to define and bind built-in functions. For the time, we do not have extensive documentation to cover all the cases, so please either consult the sources with the \`default-builtin-config.ts\` or open a [new issue](${doc_issue_1.NewIssueUrl}). | \`{ type: 'function', names: ['next'], processor: 'builtin:default', config: { cfg: ExitPointType.Next } }\` |
294
+ | \`function\` | Is a rather flexible way to define and bind built-in functions. For the time, we do not have extensive documentation to cover all the cases, so please either consult the sources with the \`default-builtin-config.ts\` or open a [new issue](${doc_issue_1.NewIssueUrl}). | \`{ type: 'function', names: ['next'], processor: '${built_in_1.BuiltInProcName.Default}', config: { cfg: ExitPointType.Next } }\` |
295
295
  | \`replacement\` | A comfortable way to specify replacement functions like \`$<-\` or \`names<-\`. \`suffixes\` describes the... suffixes to attach automatically. | \`{ type: 'replacement', suffixes: ['<-', '<<-'], names: ['[', '[['] }\` |
296
296
 
297
297
 
@@ -102,9 +102,9 @@ export interface LintingResult {
102
102
  */
103
103
  readonly quickFix?: LintQuickFix[];
104
104
  /**
105
- * The node ID involved in this linting result, if applicable.
105
+ * The node ID or IDs involved in this linting result, if applicable.
106
106
  */
107
- readonly involvedId: NodeId | undefined;
107
+ readonly involvedId: NodeId | NodeId[] | undefined;
108
108
  }
109
109
  export interface ConfiguredLintingRule<Name extends LintingRuleNames = LintingRuleNames> {
110
110
  readonly name: Name;
@@ -249,11 +249,7 @@ export declare const LintingRules: {
249
249
  cfg: import("../control-flow/control-flow-graph").ControlFlowInformation;
250
250
  analyzer: import("../project/flowr-analyzer").ReadonlyFlowrAnalysisProvider;
251
251
  }) => {
252
- results: {
253
- certainty: import("./linter-format").LintingResultCertainty.Certain;
254
- involvedId: undefined;
255
- range: import("../util/range").SourceRange;
256
- }[];
252
+ results: import("./rules/dead-code").DeadCodeResult[];
257
253
  '.meta': import("./rules/dead-code").DeadCodeMetadata;
258
254
  };
259
255
  readonly prettyPrint: {
@@ -1,10 +1,10 @@
1
- import { LintingResultCertainty, type LintingResult, LintingRuleCertainty } from '../linter-format';
1
+ import { type LintingResult, LintingRuleCertainty } from '../linter-format';
2
2
  import { type SourceRange } from '../../util/range';
3
3
  import type { MergeableRecord } from '../../util/objects';
4
4
  import { LintingRuleTag } from '../linter-tags';
5
5
  import { type CfgSimplificationPassName } from '../../control-flow/cfg-simplification';
6
6
  export interface DeadCodeResult extends LintingResult {
7
- range: SourceRange;
7
+ readonly range: SourceRange;
8
8
  }
9
9
  export interface DeadCodeConfig extends MergeableRecord {
10
10
  /**
@@ -24,11 +24,7 @@ export declare const DEAD_CODE: {
24
24
  cfg: import("../../control-flow/control-flow-graph").ControlFlowInformation;
25
25
  analyzer: import("../../project/flowr-analyzer").ReadonlyFlowrAnalysisProvider;
26
26
  }) => {
27
- results: {
28
- certainty: LintingResultCertainty.Certain;
29
- involvedId: undefined;
30
- range: SourceRange;
31
- }[];
27
+ results: DeadCodeResult[];
32
28
  '.meta': DeadCodeMetadata;
33
29
  };
34
30
  readonly prettyPrint: {
@@ -19,19 +19,18 @@ exports.DEAD_CODE = {
19
19
  consideredNodes: 0
20
20
  };
21
21
  return {
22
- results: (0, range_1.combineRanges)(...new Set(elements.getElements()
22
+ results: combineResults(elements.getElements()
23
23
  .filter(element => {
24
24
  meta.consideredNodes++;
25
25
  const cfgInformation = (0, search_enrichers_1.enrichmentContent)(element, search_enrichers_1.Enrichment.CfgInformation);
26
26
  return cfgInformation.isRoot && !cfgInformation.isReachable;
27
27
  })
28
- .map(element => element.node.info.fullRange ?? element.node.location)
29
- .filter(assert_1.isNotUndefined)))
30
- .map(range => ({
28
+ .map(element => ({
31
29
  certainty: linter_format_1.LintingResultCertainty.Certain,
32
- involvedId: undefined,
33
- range
34
- })),
30
+ involvedId: element.node.info.id,
31
+ range: (element.node.info.fullRange ?? element.node.location)
32
+ }))
33
+ .filter(element => (0, assert_1.isNotUndefined)(element.range))),
35
34
  '.meta': meta
36
35
  };
37
36
  },
@@ -48,4 +47,23 @@ exports.DEAD_CODE = {
48
47
  defaultConfig: {}
49
48
  }
50
49
  };
50
+ function combineResults(results) {
51
+ for (let i = results.length - 1; i >= 0; i--) {
52
+ const result = results[i];
53
+ const other = results.find(other => result !== other && (0, range_1.rangeIsSubsetOf)(result.range, other.range));
54
+ if (other !== undefined) {
55
+ if (!Array.isArray(other.involvedId)) {
56
+ other.involvedId = other.involvedId !== undefined ? [other.involvedId] : [];
57
+ }
58
+ if (Array.isArray(result.involvedId)) {
59
+ other.involvedId.push(...result.involvedId);
60
+ }
61
+ else if (result.involvedId !== undefined) {
62
+ other.involvedId.push(result.involvedId);
63
+ }
64
+ results.splice(i, 1);
65
+ }
66
+ }
67
+ return results;
68
+ }
51
69
  //# sourceMappingURL=dead-code.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.8.8",
3
+ "version": "2.8.10",
4
4
  "description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "repository": {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FlowrNamespaceFile = void 0;
4
4
  const flowr_file_1 = require("../../../context/flowr-file");
5
5
  const resolve_args_1 = require("../../../../abstract-interpretation/data-frame/resolve-args");
6
+ const retriever_1 = require("../../../../r-bridge/retriever");
6
7
  /**
7
8
  * This decorates a text file and provides access to its content in the {@link NamespaceFormat}.
8
9
  */
@@ -60,11 +61,11 @@ function parseNamespace(file) {
60
61
  switch (type) {
61
62
  case 'exportClasses':
62
63
  case 'exportMethods':
63
- result.current.exportedFunctions.push(args);
64
+ result.current.exportedFunctions.push((0, retriever_1.removeRQuotes)(args));
64
65
  break;
65
66
  case 'S3method':
66
67
  {
67
- const parts = args.split(',').map(s => s.trim());
68
+ const parts = args.split(',').map(s => (0, retriever_1.removeRQuotes)(s.trim()));
68
69
  if (parts.length !== 2) {
69
70
  continue;
70
71
  }
@@ -78,7 +79,7 @@ function parseNamespace(file) {
78
79
  break;
79
80
  }
80
81
  case 'export':
81
- result.current.exportedSymbols.push(args);
82
+ result.current.exportedSymbols.push((0, retriever_1.removeRQuotes)(args));
82
83
  break;
83
84
  case 'useDynLib':
84
85
  {
@@ -39,7 +39,10 @@ async function executeDependenciesQuery({ analyzer, }, queries) {
39
39
  const results = Object.fromEntries(await Promise.all(functions.entries().map(async ([c, f]) => {
40
40
  const results = getResults(queries, { dataflow, config, normalize }, queryResults, c, f, data);
41
41
  // only default categories allow additional analyses, so we null-coalesce here!
42
- await dependencies_query_format_1.DefaultDependencyCategories[c]?.additionalAnalysis?.(data, ignoreDefault, f, queryResults, results);
42
+ const enabled = query.enabledCategories;
43
+ if (enabled === undefined || (enabled?.length > 0 && enabled.includes(c))) {
44
+ await dependencies_query_format_1.DefaultDependencyCategories[c]?.additionalAnalysis?.(data, ignoreDefault, f, queryResults, results);
45
+ }
43
46
  return [c, results];
44
47
  })));
45
48
  return {
@@ -202,7 +205,7 @@ function collectValuesFromLinks(args, data, linkedIds) {
202
205
  }
203
206
  function getFunctionsToCheck(customFunctions, functionFlag, enabled, ignoreDefaultFunctions, defaultFunctions) {
204
207
  // "If unset or empty, all function types are searched for."
205
- if (enabled?.length && enabled.indexOf(functionFlag) < 0) {
208
+ if (enabled !== undefined && (enabled?.length === 0 || enabled.indexOf(functionFlag) < 0)) {
206
209
  return [];
207
210
  }
208
211
  let functions = ignoreDefaultFunctions ? [] : [...defaultFunctions];
@@ -119,7 +119,7 @@ exports.DependenciesQueryDefinition = {
119
119
  type: joi_1.default.string().valid('dependencies').required().description('The type of the query.'),
120
120
  ignoreDefaultFunctions: joi_1.default.boolean().optional().description('Should the set of functions that are detected by default be ignored/skipped? Defaults to false.'),
121
121
  ...Object.fromEntries(Object.keys(exports.DefaultDependencyCategories).map(c => [`${c}Functions`, functionInfoSchema.description(`The set of ${c} functions to search for.`)])),
122
- enabledCategories: joi_1.default.array().optional().items(joi_1.default.string().valid(...Object.keys(exports.DefaultDependencyCategories))).description('A set of flags that determines what types of dependencies are searched for. If unset or empty, all dependency types are searched for.'),
122
+ enabledCategories: joi_1.default.array().optional().items(joi_1.default.string()).description('A set of flags that determines what types of dependencies are searched for. If unset, all dependency types are searched for.'),
123
123
  additionalCategories: joi_1.default.object().allow(joi_1.default.object({
124
124
  queryDisplayName: joi_1.default.string().description('The display name in the query result.'),
125
125
  functions: functionInfoSchema.description('The functions that this additional category should search for.'),
@@ -111,7 +111,7 @@ exports.LinterQueryDefinition = {
111
111
  if ((0, linter_format_1.isLintingResultsError)(v)) {
112
112
  return [];
113
113
  }
114
- return v.results.map(v => v.involvedId);
114
+ return v.results.flatMap(v => Array.isArray(v.involvedId) ? v.involvedId : [v.involvedId]);
115
115
  }).filter(assert_1.isNotUndefined);
116
116
  }
117
117
  };
@@ -12,6 +12,7 @@ import type { SourceRange } from '../../../../../util/range';
12
12
  import { BiMap } from '../../../../../util/collections/bimap';
13
13
  import type { MergeableRecord } from '../../../../../util/objects';
14
14
  import { RoleInParent } from './role';
15
+ import { RType } from '../type';
15
16
  import type { NodeId } from './node-id';
16
17
  import type { RDelimiter } from '../nodes/info/r-delimiter';
17
18
  import type { RProject } from '../nodes/r-project';
@@ -49,7 +50,7 @@ export interface ParentContextInfo extends MergeableRecord {
49
50
  /**
50
51
  * The nesting of the node in the AST
51
52
  *
52
- * The root node has a nesting of 0, nested function calls, loops etc. will increase the nesting
53
+ * The root node has a nesting of 0, contexts listed in ${@link nestForElement} will increase the nesting
53
54
  */
54
55
  nesting: number;
55
56
  /**
@@ -80,6 +81,10 @@ export interface NormalizedAst<OtherInfo = ParentInformation, Node = RProject<Ot
80
81
  /** marks whether the AST contains potential syntax errors */
81
82
  hasError?: boolean;
82
83
  }
84
+ /**
85
+ * Node types for which the nesting score will be increased
86
+ */
87
+ export declare const nestForElement: ReadonlySet<RType>;
83
88
  export interface NormalizedAstDecorationConfiguration<OtherInfo> {
84
89
  /** The id generator: must generate a unique id für each passed node */
85
90
  getId?: IdGenerator<OtherInfo>;
@@ -9,6 +9,7 @@
9
9
  * @module
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.nestForElement = void 0;
12
13
  exports.deterministicCountingIdGenerator = deterministicCountingIdGenerator;
13
14
  exports.deterministicPrefixIdGenerator = deterministicPrefixIdGenerator;
14
15
  exports.sourcedDeterministicCountingIdGenerator = sourcedDeterministicCountingIdGenerator;
@@ -64,7 +65,10 @@ const defaultParentContext = {
64
65
  role: "root" /* RoleInParent.Root */,
65
66
  index: 0
66
67
  };
67
- const nestForElement = new Set([
68
+ /**
69
+ * Node types for which the nesting score will be increased
70
+ */
71
+ exports.nestForElement = new Set([
68
72
  type_1.RType.FunctionDefinition, type_1.RType.ForLoop, type_1.RType.WhileLoop, type_1.RType.RepeatLoop, type_1.RType.IfThenElse,
69
73
  ]);
70
74
  /**
@@ -83,7 +87,7 @@ function decorateAst(project, { getId = deterministicCountingIdGenerator(0) }) {
83
87
  const unaryOp = createFoldForUnaryOp(info);
84
88
  const folds = {
85
89
  down: (n, nesting) => {
86
- if (nestForElement.has(n.type)) {
90
+ if (exports.nestForElement.has(n.type)) {
87
91
  return nesting + 1;
88
92
  }
89
93
  else {
@@ -2,9 +2,14 @@ import type { NormalizedAst } from '../../r-bridge/lang-4.x/ast/model/processing
2
2
  import { type ControlFlowInformation } from '../../control-flow/control-flow-graph';
3
3
  import type { MermaidMarkStyle, MermaidGraphPrinterInfo } from './info';
4
4
  export interface MermaidCfgGraphPrinterInfo extends MermaidGraphPrinterInfo {
5
+ /** The style to apply to mark an entry point marker node */
5
6
  entryPointStyle?: MermaidMarkStyle['vertex'];
7
+ /** The stly eto apply to mark an exit point marker node */
6
8
  exitPointStyle?: MermaidMarkStyle['vertex'];
9
+ /** If true, a simplified basic block will have "Basic Block (id)" prepended */
7
10
  includeBasicBlockLabel?: boolean;
11
+ /** If this threshold is reached (lexemes of a simplified basic block), the remaning character will be replaced by ... */
12
+ basicBlockCharacterLimit?: number;
8
13
  }
9
14
  export declare const MermaidEntryPointDefaultMarkStyle: MermaidMarkStyle['vertex'];
10
15
  export declare const MermaidExitPointDefaultMarkStyle: MermaidMarkStyle['vertex'];
@@ -18,7 +23,7 @@ export declare const MermaidExitPointDefaultMarkStyle: MermaidMarkStyle['vertex'
18
23
  * @param includeOnlyIds - If provided, only include the vertices with the given IDs.
19
24
  * @param mark - If provided, mark the given vertices and edges.
20
25
  */
21
- export declare function cfgToMermaid(cfg: ControlFlowInformation, normalizedAst: NormalizedAst, { prefix, simplify, markStyle, entryPointStyle, exitPointStyle, includeOnlyIds, mark, includeBasicBlockLabel }?: MermaidCfgGraphPrinterInfo): string;
26
+ export declare function cfgToMermaid(cfg: ControlFlowInformation, normalizedAst: NormalizedAst, { prefix, simplify, markStyle, entryPointStyle, exitPointStyle, includeOnlyIds, mark, includeBasicBlockLabel, basicBlockCharacterLimit }?: MermaidCfgGraphPrinterInfo): string;
22
27
  /**
23
28
  * Use mermaid to visualize the normalized AST.
24
29
  */
@@ -50,7 +50,7 @@ function shouldIncludeNode(simplify, element, include) {
50
50
  * @param includeOnlyIds - If provided, only include the vertices with the given IDs.
51
51
  * @param mark - If provided, mark the given vertices and edges.
52
52
  */
53
- function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify = false, markStyle = info_1.MermaidDefaultMarkStyle, entryPointStyle = exports.MermaidEntryPointDefaultMarkStyle, exitPointStyle = exports.MermaidExitPointDefaultMarkStyle, includeOnlyIds, mark, includeBasicBlockLabel = true } = {}) {
53
+ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify = false, markStyle = info_1.MermaidDefaultMarkStyle, entryPointStyle = exports.MermaidEntryPointDefaultMarkStyle, exitPointStyle = exports.MermaidExitPointDefaultMarkStyle, includeOnlyIds, mark, includeBasicBlockLabel = true, basicBlockCharacterLimit = 100 } = {}) {
54
54
  const hasBbandSimplify = simplify && cfg.graph.mayHaveBasicBlocks();
55
55
  let output = prefix;
56
56
  if (includeOnlyIds) {
@@ -89,7 +89,7 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
89
89
  continue;
90
90
  }
91
91
  const ids = vertex.elems?.map(e => e.id) ?? [];
92
- const reconstruct = (0, reconstruct_1.reconstructToCode)(normalizedAst, { nodes: new Set(ids) }, auto_select_defaults_1.doNotAutoSelect).code;
92
+ const reconstruct = limitTo((0, reconstruct_1.reconstructToCode)(normalizedAst, { nodes: new Set(ids) }, auto_select_defaults_1.doNotAutoSelect).code, basicBlockCharacterLimit);
93
93
  const name = `"\`${includeBasicBlockLabel ? `Basic Block (${id})\n` : ''}${(0, mermaid_1.escapeMarkdown)(reconstruct)}\`"`;
94
94
  output += ` n${id}[[${name}]]\n`;
95
95
  diagramIncludedIds.add(vertex.id);
@@ -166,4 +166,13 @@ function cfgToMermaid(cfg, normalizedAst, { prefix = 'flowchart BT\n', simplify
166
166
  function cfgToMermaidUrl(cfg, normalizedAst, info) {
167
167
  return (0, mermaid_1.mermaidCodeToUrl)(cfgToMermaid(cfg, normalizedAst, info ?? {}));
168
168
  }
169
+ /**
170
+ * Limits a string to n chars, after which the remainder will be replaced with ...
171
+ */
172
+ function limitTo(str, limit) {
173
+ if (str.length <= limit) {
174
+ return str;
175
+ }
176
+ return `${str.substring(0, limit)}...`;
177
+ }
169
178
  //# sourceMappingURL=cfg.js.map
package/util/version.js CHANGED
@@ -6,7 +6,7 @@ exports.printVersionInformation = printVersionInformation;
6
6
  const semver_1 = require("semver");
7
7
  const assert_1 = require("./assert");
8
8
  // this is automatically replaced with the current version by release-it
9
- const version = '2.8.8';
9
+ const version = '2.8.10';
10
10
  /**
11
11
  * Retrieves the current flowR version as a new {@link SemVer} object.
12
12
  */