@eagleoutice/flowr 2.8.12 → 2.8.13

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 (38) hide show
  1. package/README.md +23 -22
  2. package/control-flow/simple-visitor.js +3 -1
  3. package/dataflow/environments/default-builtin-config.d.ts +1 -1
  4. package/dataflow/environments/default-builtin-config.js +1 -1
  5. package/dataflow/extractor.js +1 -1
  6. package/dataflow/graph/graph.d.ts +1 -14
  7. package/dataflow/graph/graph.js +1 -10
  8. package/dataflow/internal/linker.d.ts +12 -0
  9. package/dataflow/internal/linker.js +24 -11
  10. package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +5 -4
  11. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +3 -2
  12. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +7 -4
  13. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +16 -10
  14. package/dataflow/internal/process/functions/call/built-in/built-in-get.js +1 -1
  15. package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.js +3 -2
  16. package/dataflow/internal/process/functions/process-argument.js +2 -1
  17. package/dataflow/internal/process/functions/process-parameter.js +4 -3
  18. package/documentation/wiki-query.js +2 -3
  19. package/package.json +1 -1
  20. package/queries/catalog/call-context-query/call-context-query-executor.d.ts +1 -1
  21. package/queries/catalog/call-context-query/call-context-query-executor.js +4 -3
  22. package/queries/catalog/call-context-query/call-context-query-format.d.ts +21 -1
  23. package/queries/catalog/call-context-query/call-context-query-format.js +15 -7
  24. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +11 -3
  25. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +14 -1
  26. package/queries/catalog/call-context-query/identify-link-to-nested-call-relation.d.ts +12 -0
  27. package/queries/catalog/call-context-query/identify-link-to-nested-call-relation.js +30 -0
  28. package/queries/catalog/call-context-query/identify-link-to-relation.d.ts +10 -0
  29. package/queries/catalog/call-context-query/identify-link-to-relation.js +21 -0
  30. package/queries/catalog/dependencies-query/dependencies-query-format.js +1 -1
  31. package/queries/catalog/dependencies-query/function-info/test-functions.js +146 -12
  32. package/queries/query.js +1 -1
  33. package/search/search-executor/search-enrichers.d.ts +8 -40
  34. package/search/search-executor/search-enrichers.js +17 -13
  35. package/search/search-executor/search-generators.js +5 -7
  36. package/search/search-executor/search-transformer.js +1 -7
  37. package/util/schema.js +6 -8
  38. 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.10, R grammar v14 (tree-sitter engine)
27
+ flowR repl using flowR v2.8.12, R grammar v14 (tree-sitter engine)
28
28
  R> :query @linter "read.csv(\"/root/x.txt\")"
29
29
  ```
30
30
 
@@ -33,13 +33,13 @@ It offers a wide variety of features, for example:
33
33
 
34
34
 
35
35
  ```text
36
- Query: linter (2 ms)
36
+ Query: linter (1 ms)
37
37
  ╰ Deprecated Functions (deprecated-functions):
38
- ╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0
38
+ ╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, 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: 0
42
+ ╰ Metadata: totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 1, 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):
@@ -53,12 +53,12 @@ It offers a wide variety of features, for example:
53
53
  ╰ Network Functions (network-functions):
54
54
  ╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0
55
55
  ╰ Dataframe Access Validation (dataframe-access-validation):
56
- ╰ Metadata: numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 1
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
60
  ╰ Metadata: numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0
61
- All queries together required ≈2 ms (1ms accuracy, total 2 ms)
61
+ All queries together required ≈1 ms (1ms accuracy, total 1 ms)
62
62
  ```
63
63
 
64
64
 
@@ -80,13 +80,13 @@ It offers a wide variety of features, for example:
80
80
 
81
81
  _Results (prettified and summarized):_
82
82
 
83
- Query: **linter** (2 ms)\
83
+ Query: **linter** (1 ms)\
84
84
     ╰ **Deprecated Functions** (deprecated-functions):\
85
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0</code>\
85
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, 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: 0</code>\
89
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 1</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):\
@@ -98,18 +98,18 @@ It offers a wide variety of features, for example:
98
98
  &nbsp;&nbsp;&nbsp;╰ **Naming Convention** (naming-convention):\
99
99
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numMatches: 0, numBreak: 0, searchTimeMs: 0, processTimeMs: 0</code>\
100
100
  &nbsp;&nbsp;&nbsp;╰ **Network Functions** (network-functions):\
101
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0</code>\
101
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0</code>\
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
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
- _All queries together required ≈2 ms (1ms accuracy, total 2 ms)_
108
+ _All queries together required ≈1 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.3 ms_ (including parsing and normalization and the query) within the generation environment.
112
+ The analysis required _1.9 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": 1,
129
+ "searchTimeMs": 0,
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": 0
153
+ "processTimeMs": 1
154
154
  }
155
155
  },
156
156
  "seeded-randomness": {
@@ -207,7 +207,7 @@ It offers a wide variety of features, for example:
207
207
  ".meta": {
208
208
  "totalCalls": 0,
209
209
  "totalFunctionDefinitions": 0,
210
- "searchTimeMs": 1,
210
+ "searchTimeMs": 0,
211
211
  "processTimeMs": 0
212
212
  }
213
213
  },
@@ -239,11 +239,11 @@ It offers a wide variety of features, for example:
239
239
  }
240
240
  },
241
241
  ".meta": {
242
- "timing": 2
242
+ "timing": 1
243
243
  }
244
244
  },
245
245
  ".meta": {
246
- "timing": 2
246
+ "timing": 1
247
247
  }
248
248
  }
249
249
  ```
@@ -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.10, R grammar v14 (tree-sitter engine)
311
+ flowR repl using flowR v2.8.12, R grammar v14 (tree-sitter engine)
312
312
  R> :query @static-slice (11@sum) file://test/testfiles/example.R
313
313
  ```
314
314
 
@@ -322,7 +322,7 @@ It offers a wide variety of features, for example:
322
322
  N <- 10
323
323
  for(i in 1:(N-1)) sum <- sum + i + w
324
324
  sum
325
- All queries together required ≈3 ms (1ms accuracy, total 4 ms)
325
+ All queries together required ≈2 ms (1ms accuracy, total 3 ms)
326
326
  ```
327
327
 
328
328
 
@@ -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.8 ms</span></i> (as of Jan 27, 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!">122.5 ms</span></i> (as of Jan 28, 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.10, R grammar v14 (tree-sitter engine)
395
+ flowR repl using flowR v2.8.12, R grammar v14 (tree-sitter engine)
396
396
  R> :dataflow* test/testfiles/example.R
397
397
  ```
398
398
 
@@ -402,6 +402,7 @@ It offers a wide variety of features, for example:
402
402
 
403
403
  ```text
404
404
  https://mermaid.live/view#base64:eyJjb2RlIjoiZmxvd2NoYXJ0IEJUXG4gICAgMChbXCJgIzkxO1JTeW1ib2wjOTM7IHRlc3RcbiAgICAgICgwKVxuICAgICAgKjEuMS00KmBcIl0pXG4gICAgMShbXCJgIzkxO1JTeW1ib2wjOTM7IHRlc3RmaWxlc1xuICAgICAgKDEpXG4gICAgICAqMS42LTE0KmBcIl0pXG4gICAgMltbXCJgIzkxO1JCaW5hcnlPcCM5MzsgL1xuICAgICAgKDIpXG4gICAgICAqMS4xLTE0KlxuICAgICgwLCAxKWBcIl1dXG4gICAgYnVpbHQtaW46X1tcImBCdWlsdC1Jbjpcbi9gXCJdXG4gICAgc3R5bGUgYnVpbHQtaW46XyBzdHJva2U6Z3JheSxmaWxsOmdyYXksc3Ryb2tlLXdpZHRoOjJweCxvcGFjaXR5Oi44O1xuICAgIDMoW1wiYCM5MTtSU3ltYm9sIzkzOyBleGFtcGxlLlJcbiAgICAgICgzKVxuICAgICAgKjEuMTYtMjQqYFwiXSlcbiAgICA0W1tcImAjOTE7UkJpbmFyeU9wIzkzOyAvXG4gICAgICAoNClcbiAgICAgICoxLjEtMjQqXG4gICAgKDIsIDMpYFwiXV1cbiAgICAyIC0tPnxcInJlYWRzLCBhcmd1bWVudFwifCAwXG4gICAgMiAtLT58XCJyZWFkcywgYXJndW1lbnRcInwgMVxuICAgIDIgLS4tPnxcInJlYWRzLCBjYWxsc1wifCBidWlsdC1pbjpfXG4gICAgbGlua1N0eWxlIDIgc3Ryb2tlOmdyYXk7XG4gICAgNCAtLT58XCJyZWFkcywgYXJndW1lbnRcInwgMlxuICAgIDQgLS0+fFwicmVhZHMsIGFyZ3VtZW50XCJ8IDNcbiAgICA0IC0uLT58XCJyZWFkcywgY2FsbHNcInwgYnVpbHQtaW46X1xuICAgIGxpbmtTdHlsZSA1IHN0cm9rZTpncmF5OyIsIm1lcm1haWQiOnsiYXV0b1N5bmMiOnRydWV9fQ==
405
+ Copied mermaid url to clipboard (dataflow: 0ms).
405
406
  ```
406
407
 
407
408
 
@@ -697,7 +698,7 @@ It offers a wide variety of features, for example:
697
698
  ```
698
699
 
699
700
 
700
- (The analysis required _4.5 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
701
+ (The analysis required _1.2 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
701
702
 
702
703
 
703
704
 
@@ -37,7 +37,9 @@ visitor) {
37
37
  }
38
38
  const incoming = graph.outgoingEdges(current);
39
39
  if (incoming) {
40
- queue = queue.concat(incoming.keys().toArray());
40
+ for (const c of incoming.keys()) {
41
+ queue.push(c);
42
+ }
41
43
  }
42
44
  }
43
45
  }
@@ -293,7 +293,7 @@ export declare const DefaultBuiltinConfig: [{
293
293
  readonly assumePrimitive: true;
294
294
  }, {
295
295
  readonly type: "function";
296
- readonly names: ["stopifnot"];
296
+ readonly names: ["stopifnot", "assert_that"];
297
297
  readonly processor: BuiltInProcName.StopIfNot;
298
298
  readonly config: {};
299
299
  readonly assumePrimitive: false;
@@ -242,7 +242,7 @@ exports.DefaultBuiltinConfig = [
242
242
  },
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
- { type: 'function', names: ['stopifnot'], processor: built_in_1.BuiltInProcName.StopIfNot, config: {}, assumePrimitive: false },
245
+ { type: 'function', names: ['stopifnot', 'assert_that'], processor: built_in_1.BuiltInProcName.StopIfNot, config: {}, assumePrimitive: false },
246
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 },
@@ -89,7 +89,7 @@ function resolveLinkToSideEffects(ast, graph) {
89
89
  continue;
90
90
  }
91
91
  /* this has to change whenever we add a new link to relations because we currently offer no abstraction for the type */
92
- const potentials = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(s.id, cf?.graph, graph, s.linkTo, knownCalls);
92
+ const potentials = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelationSync)(s.id, cf.graph, graph, s.linkTo, knownCalls);
93
93
  for (const pot of potentials) {
94
94
  graph.addEdge(s.id, pot, edge_1.EdgeType.Reads);
95
95
  }
@@ -177,20 +177,7 @@ export declare class DataflowGraph<Vertex extends DataflowGraphVertexInfo = Data
177
177
  * @see DataflowGraphVertexArgument
178
178
  */
179
179
  addVertex(vertex: DataflowGraphVertexArgument & Omit<Vertex, keyof DataflowGraphVertexArgument>, fallbackEnv: REnvironmentInformation, asRoot?: boolean, overwrite?: boolean): this;
180
- /** {@inheritDoc} */
181
- addEdge(from: NodeId, to: NodeId, type: EdgeType | number): this;
182
- /** {@inheritDoc} */
183
- addEdge(from: {
184
- nodeId: NodeId;
185
- }, to: {
186
- nodeId: NodeId;
187
- }, type: EdgeType | number): this;
188
- /** {@inheritDoc} */
189
- addEdge(from: NodeId | {
190
- nodeId: NodeId;
191
- }, to: NodeId | {
192
- nodeId: NodeId;
193
- }, type: EdgeType | number): this;
180
+ addEdge(fromId: NodeId, toId: NodeId, type: EdgeType | number): this;
194
181
  /**
195
182
  * Merges the other graph into *this* one (in-place). The return value is only for convenience.
196
183
  * @param otherGraph - The graph to merge into this one
@@ -222,8 +222,7 @@ class DataflowGraph {
222
222
  }
223
223
  return this;
224
224
  }
225
- addEdge(from, to, type) {
226
- const [fromId, toId] = extractEdgeIds(from, to);
225
+ addEdge(fromId, toId, type) {
227
226
  if (fromId === toId) {
228
227
  return this;
229
228
  }
@@ -390,14 +389,6 @@ function mergeNodeInfos(current, next) {
390
389
  }
391
390
  return current;
392
391
  }
393
- /**
394
- * Returns the ids of the dataflow vertices referenced.
395
- */
396
- function extractEdgeIds(from, to) {
397
- const fromId = typeof from === 'object' ? from.nodeId : from;
398
- const toId = typeof to === 'object' ? to.nodeId : to;
399
- return [fromId, toId];
400
- }
401
392
  function envFromJson(json) {
402
393
  const parent = json.parent ? envFromJson(json.parent) : undefined;
403
394
  const memory = new Map();
@@ -63,6 +63,18 @@ export declare function linkFunctionCalls(graph: DataflowGraph, idMap: AstIdMap,
63
63
  export declare function getAllFunctionCallTargets(call: NodeId, graph: DataflowGraph, environment?: REnvironmentInformation): NodeId[];
64
64
  /**
65
65
  * Finds all linked function definitions starting from the given set of read ids.
66
+ * This is a complicated function, please only call it if you know what you are doing.
67
+ * For example, if you are interested in the called functions of a function call, use {@link getAllFunctionCallTargets} instead.
68
+ * This function here expects you to handle the accessed objects yourself (e.g,. already resolve the first layer of reads/returns/calls/... or resolve the identifier by name)
69
+ * and then pass in the relevant read ids.
70
+ * @example
71
+ * Consider a scenario like this:
72
+ * ```R
73
+ * x <- function() 3
74
+ * x()
75
+ * ```
76
+ * To resolve the call `x` in the second line, use {@link getAllFunctionCallTargets}!
77
+ * To know what fdefs the definition of `x` in the first line links to, you can use {@link getAllLinkedFunctionDefinitions|this function}.
66
78
  */
67
79
  export declare function getAllLinkedFunctionDefinitions(functionDefinitionReadIds: ReadonlySet<NodeId>, dataflowGraph: DataflowGraph): [Set<Required<DataflowGraphVertexFunctionDefinition>>, Set<BuiltIn>];
68
80
  /**
@@ -230,10 +230,10 @@ function linkFunctionCallWithSingleTarget(graph, { subflow: fnSubflow, exitPoint
230
230
  if (defs === undefined) {
231
231
  continue;
232
232
  }
233
- for (const def of defs) {
234
- if (!(0, built_in_1.isBuiltIn)(def.nodeId)) {
235
- graph.addEdge(ingoing, def, edge_1.EdgeType.DefinedByOnCall);
236
- graph.addEdge(id, def, edge_1.EdgeType.DefinesOnCall);
233
+ for (const { nodeId } of defs) {
234
+ if (!(0, built_in_1.isBuiltIn)(nodeId)) {
235
+ graph.addEdge(ingoing.nodeId, nodeId, edge_1.EdgeType.DefinedByOnCall);
236
+ graph.addEdge(id, nodeId, edge_1.EdgeType.DefinesOnCall);
237
237
  }
238
238
  }
239
239
  }
@@ -340,6 +340,18 @@ function getAllFunctionCallTargets(call, graph, environment) {
340
340
  const LinkedFnFollowBits = edge_1.EdgeType.Reads | edge_1.EdgeType.DefinedBy | edge_1.EdgeType.DefinedByOnCall;
341
341
  /**
342
342
  * Finds all linked function definitions starting from the given set of read ids.
343
+ * This is a complicated function, please only call it if you know what you are doing.
344
+ * For example, if you are interested in the called functions of a function call, use {@link getAllFunctionCallTargets} instead.
345
+ * This function here expects you to handle the accessed objects yourself (e.g,. already resolve the first layer of reads/returns/calls/... or resolve the identifier by name)
346
+ * and then pass in the relevant read ids.
347
+ * @example
348
+ * Consider a scenario like this:
349
+ * ```R
350
+ * x <- function() 3
351
+ * x()
352
+ * ```
353
+ * To resolve the call `x` in the second line, use {@link getAllFunctionCallTargets}!
354
+ * To know what fdefs the definition of `x` in the first line links to, you can use {@link getAllLinkedFunctionDefinitions|this function}.
343
355
  */
344
356
  function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGraph) {
345
357
  const result = new Set();
@@ -350,13 +362,14 @@ function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGrap
350
362
  const potential = Array.from(functionDefinitionReadIds);
351
363
  const visited = new Set();
352
364
  while (potential.length !== 0) {
353
- const currentId = potential.pop();
354
- visited.add(currentId);
355
- if ((0, built_in_1.isBuiltIn)(currentId)) {
356
- builtIns.add(currentId);
365
+ const cid = potential.pop();
366
+ // console.log(`visiting ${recoverName(cid, dataflowGraph.idMap)} ${cid}`);
367
+ visited.add(cid);
368
+ if ((0, built_in_1.isBuiltIn)(cid)) {
369
+ builtIns.add(cid);
357
370
  continue;
358
371
  }
359
- const currentInfo = dataflowGraph.get(currentId, true);
372
+ const currentInfo = dataflowGraph.get(cid, true);
360
373
  if (currentInfo === undefined) {
361
374
  continue;
362
375
  }
@@ -375,7 +388,7 @@ function getAllLinkedFunctionDefinitions(functionDefinitionReadIds, dataflowGrap
375
388
  }
376
389
  }
377
390
  }
378
- if (hasReturnEdge) {
391
+ if (vertex.tag === vertex_1.VertexType.FunctionCall || hasReturnEdge) {
379
392
  continue;
380
393
  }
381
394
  for (const [target, { types }] of edges) {
@@ -409,7 +422,7 @@ function linkInputs(referencesToLinkAgainstEnvironment, environmentInformation,
409
422
  let allBuiltIn = true;
410
423
  for (const target of probableTarget) {
411
424
  // we can stick with maybe even if readId.attribute is always
412
- graph.addEdge(bodyInput, target, edge_1.EdgeType.Reads);
425
+ graph.addEdge(bodyInput.nodeId, target.nodeId, edge_1.EdgeType.Reads);
413
426
  if (!(0, identifier_1.isReferenceType)(target.type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
414
427
  allBuiltIn = false;
415
428
  }
@@ -145,10 +145,11 @@ function processApply(name, args, rootId, data, config) {
145
145
  }
146
146
  (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${ingoing.nodeId} in closure of function definition ${rootId}`);
147
147
  let allBuiltIn = true;
148
- for (const ref of resolved) {
149
- information.graph.addEdge(ingoing, ref, edge_1.EdgeType.Reads);
150
- information.graph.addEdge(rootId, ref, edge_1.EdgeType.Reads); // because the def. is the anonymous call
151
- if (!(0, identifier_1.isReferenceType)(ref.type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
148
+ const inId = ingoing.nodeId;
149
+ for (const { nodeId, type } of resolved) {
150
+ information.graph.addEdge(inId, nodeId, edge_1.EdgeType.Reads);
151
+ information.graph.addEdge(rootId, nodeId, edge_1.EdgeType.Reads); // because the def. is the anonymous call
152
+ if (!(0, identifier_1.isReferenceType)(type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
152
153
  allBuiltIn = false;
153
154
  }
154
155
  }
@@ -323,12 +323,13 @@ function markAsAssignment(information, nodeToDefine, sourceIds, rootIdOfAssignme
323
323
  }
324
324
  information.environment = (0, define_1.define)(nodeToDefine, assignmentConfig?.superAssignment, information.environment, data.ctx.config);
325
325
  information.graph.setDefinitionOfVertex(nodeToDefine);
326
+ const nid = nodeToDefine.nodeId;
326
327
  if (!assignmentConfig?.quoteSource) {
327
328
  for (const sourceId of sourceIds) {
328
- information.graph.addEdge(nodeToDefine, sourceId, edge_1.EdgeType.DefinedBy);
329
+ information.graph.addEdge(nid, sourceId, edge_1.EdgeType.DefinedBy);
329
330
  }
330
331
  }
331
- information.graph.addEdge(nodeToDefine, rootIdOfAssignment, edge_1.EdgeType.DefinedBy);
332
+ information.graph.addEdge(nid, rootIdOfAssignment, edge_1.EdgeType.DefinedBy);
332
333
  // kinda dirty, but we have to remove existing read edges for the symbol, added by the child
333
334
  const out = information.graph.outgoingEdges(nodeToDefine.nodeId);
334
335
  for (const [id, edge] of (out ?? [])) {
@@ -39,11 +39,14 @@ function linkReadNameToWriteIfPossible(read, environments, listEnvironments, rem
39
39
  if (probableTarget === undefined) {
40
40
  return;
41
41
  }
42
+ const rid = read.nodeId;
42
43
  for (const target of probableTarget) {
43
- // we can stick with maybe even if readId.attribute is always
44
- nextGraph.addEdge(read, target, edge_1.EdgeType.Reads);
45
- if ((read.type === identifier_1.ReferenceType.Function || read.type === identifier_1.ReferenceType.BuiltInFunction) && ((0, built_in_1.isBuiltIn)(target.definedAt))) {
46
- nextGraph.addEdge(read, target, edge_1.EdgeType.Calls);
44
+ const tid = target.nodeId;
45
+ if ((read.type === identifier_1.ReferenceType.Function || read.type === identifier_1.ReferenceType.BuiltInFunction) && (0, built_in_1.isBuiltIn)(target.definedAt)) {
46
+ nextGraph.addEdge(rid, tid, edge_1.EdgeType.Reads | edge_1.EdgeType.Calls);
47
+ }
48
+ else {
49
+ nextGraph.addEdge(rid, tid, edge_1.EdgeType.Reads);
47
50
  }
48
51
  }
49
52
  }
@@ -204,10 +204,11 @@ function updateNestedFunctionClosures(graph, outEnvironment, fnId) {
204
204
  remainingIn.push(ingoing);
205
205
  continue;
206
206
  }
207
+ const inId = ingoing.nodeId;
207
208
  (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${id} in closure of function definition ${fnId}`);
208
209
  let allBuiltIn = true;
209
210
  for (const ref of resolved) {
210
- graph.addEdge(ingoing, ref, edge_1.EdgeType.Reads);
211
+ graph.addEdge(inId, ref.nodeId, edge_1.EdgeType.Reads);
211
212
  if (!(0, identifier_1.isReferenceType)(ref.type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
212
213
  allBuiltIn = false;
213
214
  }
@@ -259,6 +260,9 @@ function updateNestedFunctionCalls(graph, outEnvironment) {
259
260
  continue;
260
261
  }
261
262
  graph.addEdge(id, target, edge_1.EdgeType.Calls);
263
+ for (const exitPoint of targetVertex.exitPoints) {
264
+ graph.addEdge(id, exitPoint.nodeId, edge_1.EdgeType.Returns);
265
+ }
262
266
  const ingoingRefs = targetVertex.subflow.in;
263
267
  const remainingIn = [];
264
268
  for (const ingoing of ingoingRefs) {
@@ -267,11 +271,12 @@ function updateNestedFunctionCalls(graph, outEnvironment) {
267
271
  remainingIn.push(ingoing);
268
272
  continue;
269
273
  }
274
+ const inId = ingoing.nodeId;
270
275
  (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${id} in closure of function definition ${id}`);
271
- for (const def of resolved) {
272
- if (!(0, built_in_1.isBuiltIn)(def.nodeId)) {
273
- graph.addEdge(ingoing, def, edge_1.EdgeType.DefinedByOnCall);
274
- graph.addEdge(id, def, edge_1.EdgeType.DefinesOnCall);
276
+ for (const { nodeId } of resolved) {
277
+ if (!(0, built_in_1.isBuiltIn)(nodeId)) {
278
+ graph.addEdge(inId, nodeId, edge_1.EdgeType.DefinedByOnCall);
279
+ graph.addEdge(id, nodeId, edge_1.EdgeType.DefinesOnCall);
275
280
  }
276
281
  }
277
282
  }
@@ -305,9 +310,10 @@ function findPromiseLinkagesForParameters(parameters, readInParameters, paramete
305
310
  const remainingRead = [];
306
311
  for (const read of readInParameters) {
307
312
  const resolved = read.name ? (0, resolve_by_name_1.resolveByName)(read.name, parameterEnvs, read.type) : undefined;
313
+ const rid = read.nodeId;
308
314
  if (resolved !== undefined) {
309
- for (const ref of resolved) {
310
- parameters.addEdge(read, ref, edge_1.EdgeType.Reads);
315
+ for (const { nodeId } of resolved) {
316
+ parameters.addEdge(rid, nodeId, edge_1.EdgeType.Reads);
311
317
  }
312
318
  continue;
313
319
  }
@@ -320,11 +326,11 @@ function findPromiseLinkagesForParameters(parameters, readInParameters, paramete
320
326
  continue;
321
327
  }
322
328
  if (writingOuts[0].cds === undefined) {
323
- parameters.addEdge(read, writingOuts[0], edge_1.EdgeType.Reads);
329
+ parameters.addEdge(rid, writingOuts[0].nodeId, edge_1.EdgeType.Reads);
324
330
  continue;
325
331
  }
326
- for (const out of writingOuts) {
327
- parameters.addEdge(read, out, edge_1.EdgeType.Reads);
332
+ for (const { nodeId } of writingOuts) {
333
+ parameters.addEdge(rid, nodeId, edge_1.EdgeType.Reads);
328
334
  }
329
335
  }
330
336
  return remainingRead;
@@ -40,7 +40,7 @@ function processGet(name, args, rootId, data) {
40
40
  const firstArg = processedArguments[0];
41
41
  if (firstArg) {
42
42
  // get 'reads' its first argument
43
- information.graph.addEdge(rootId, firstArg.entryPoint, edge_1.EdgeType.Reads);
43
+ information.graph.addEdge(rootId, firstArg.entryPoint, edge_1.EdgeType.Returns | edge_1.EdgeType.Reads);
44
44
  }
45
45
  return information;
46
46
  }
@@ -132,8 +132,9 @@ function promoteCallToFunction(call, arg, info, data) {
132
132
  (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found ${resolved.length} references to open ref ${ingoing.nodeId} in closure of function definition ${call}`);
133
133
  let allBuiltIn = true;
134
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
135
+ const rid = ref.nodeId;
136
+ info.graph.addEdge(ingoing.nodeId, rid, edge_1.EdgeType.Reads);
137
+ info.graph.addEdge(call, rid, edge_1.EdgeType.Reads); // because the def. is the anonymous call
137
138
  if (!(0, identifier_1.isReferenceType)(ref.type, identifier_1.ReferenceType.BuiltInConstant | identifier_1.ReferenceType.BuiltInFunction)) {
138
139
  allBuiltIn = false;
139
140
  }
@@ -14,9 +14,10 @@ const vertex_1 = require("../../../graph/vertex");
14
14
  function linkReadsForArgument(root, ingoingRefs, graph) {
15
15
  const allIdsBeforeArguments = new Set((0, collect_1.collectAllIds)(root, n => n.type === type_1.RType.Argument && n.info.id !== root.info.id));
16
16
  const ingoingBeforeArgs = ingoingRefs.filter(r => allIdsBeforeArguments.has(r.nodeId));
17
+ const rid = root.info.id;
17
18
  for (const ref of ingoingBeforeArgs) {
18
19
  // link against the root reference currently I do not know how to deal with nested function calls otherwise
19
- graph.addEdge(root.info.id, ref, edge_1.EdgeType.Reads);
20
+ graph.addEdge(rid, ref.nodeId, edge_1.EdgeType.Reads);
20
21
  }
21
22
  }
22
23
  /**
@@ -21,17 +21,18 @@ function processFunctionParameter(parameter, data) {
21
21
  }));
22
22
  let environment = name.environment;
23
23
  for (const writtenNode of writtenNodes) {
24
- (0, log_1.expensiveTrace)(log_1.log, () => `parameter ${writtenNode.name} (${writtenNode.nodeId}) is defined at id ${writtenNode.definedAt} with ${defaultValue === undefined ? 'no default value' : ' a default value'}`);
24
+ const wid = writtenNode.nodeId;
25
+ (0, log_1.expensiveTrace)(log_1.log, () => `parameter ${writtenNode.name} (${wid}) is defined at id ${writtenNode.definedAt} with ${defaultValue === undefined ? 'no default value' : ' a default value'}`);
25
26
  graph.setDefinitionOfVertex(writtenNode);
26
27
  environment = (0, define_1.define)(writtenNode, false, environment, data.ctx.config);
27
28
  if (defaultValue !== undefined) {
28
29
  if (parameter.defaultValue?.type === type_1.RType.FunctionDefinition) {
29
- graph.addEdge(writtenNode, parameter.defaultValue.info.id, edge_1.EdgeType.DefinedBy);
30
+ graph.addEdge(wid, parameter.defaultValue.info.id, edge_1.EdgeType.DefinedBy);
30
31
  }
31
32
  else {
32
33
  const definedBy = defaultValue.in.concat(defaultValue.unknownReferences);
33
34
  for (const node of definedBy) {
34
- graph.addEdge(writtenNode, node, edge_1.EdgeType.DefinedBy);
35
+ graph.addEdge(wid, node.nodeId, edge_1.EdgeType.DefinedBy);
35
36
  }
36
37
  }
37
38
  }
@@ -57,8 +57,7 @@ Besides this, we provide the following ways to automatically categorize and link
57
57
 
58
58
  1. **Kind** (\`kind\`): This is a general category that can be used to group calls together. For example, you may want to link all calls to \`plot\` to \`visualize\`.
59
59
  2. **Subkind** (\`subkind\`): This is used to uniquely identify the respective call type when grouping the output. For example, you may want to link all calls to \`ggplot\` to \`plot\`.
60
- 3. **Linked Calls** (\`linkTo\`): This links the current call to the last call of the given kind. This way, you can link a call like \`points\` to the latest graphics plot etc.
61
- For now, we _only_ offer support for linking to the last call, as the current flow dependency over-approximation is not stable.
60
+ 3. **Linked Calls** (\`linkTo\`): This links the current call to the last/nested/.. call of the given kind. This way, you can link a call like \`points\` to the latest graphics plot etc.
62
61
  4. **Aliases** (\`includeAliases\`): Consider a case like \`f <- function_of_interest\`, do you want calls to \`f\` to be included in the results? There is probably no need to combine this with a global call target!
63
62
 
64
63
  It's also possible to filter the results based on the following properties:
@@ -804,7 +803,7 @@ ${(0, doc_query_1.tocForQueryType)('virtual')}
804
803
 
805
804
  <summary>Detailed Query Format (Automatically Generated)</summary>
806
805
 
807
- Although it is probably better to consult the detailed explanations below, if you want to have a look at the schema, here is its description:
806
+ Although it is probably better to consult the detailed explanations, if you want to have a look at the schema, here is its description:
808
807
 
809
808
  ${(0, schema_1.describeSchema)((0, query_1.QueriesSchema)(), ansi_1.markdownFormatter)}
810
809
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.8.12",
3
+ "version": "2.8.13",
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": {
@@ -4,7 +4,7 @@ import type { BasicQueryData } from '../../base-query-format';
4
4
  *
5
5
  */
6
6
  export declare function promoteCallName(callName: CallNameTypes, exact?: boolean): RegExp | Set<string>;
7
- export type PromotedLinkTo = Omit<LinkTo, 'callName'> & {
7
+ export type PromotedLinkTo<LT = LinkTo> = Omit<LT, 'callName'> & {
8
8
  callName: RegExp | Set<string>;
9
9
  };
10
10
  /**
@@ -10,6 +10,7 @@ const objects_1 = require("../../../util/objects");
10
10
  const identify_link_to_last_call_relation_1 = require("./identify-link-to-last-call-relation");
11
11
  const cfg_kind_1 = require("../../../project/cfg-kind");
12
12
  const extract_cfg_1 = require("../../../control-flow/extract-cfg");
13
+ const identify_link_to_relation_1 = require("./identify-link-to-relation");
13
14
  /* if the node is effected by nse, we have an ingoing nse edge */
14
15
  function isQuoted(node, graph) {
15
16
  const vertex = graph.ingoingEdges(node);
@@ -239,10 +240,10 @@ async function executeCallContextQueries({ analyzer }, queries) {
239
240
  const linked = Array.isArray(query.linkTo) ? query.linkTo : [query.linkTo];
240
241
  for (const link of linked) {
241
242
  /* if we have a linkTo query, we have to find the last call */
242
- const lastCall = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(nodeId, cfg.graph, dataflow.graph, link, calls);
243
- if (lastCall) {
243
+ const linkTos = await (0, identify_link_to_relation_1.identifyLinkToRelation)(nodeId, analyzer, link, calls);
244
+ if (linkTos) {
244
245
  linkedIds ??= new Set();
245
- for (const l of lastCall) {
246
+ for (const l of linkTos) {
246
247
  if (link.attachLinkInfo) {
247
248
  linkedIds.add({ id: l, info: link.attachLinkInfo });
248
249
  }