@eagleoutice/flowr 2.7.6 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +67 -64
  2. package/cli/wiki.js +1 -1
  3. package/control-flow/extract-cfg.js +3 -3
  4. package/control-flow/useless-loop.d.ts +1 -1
  5. package/control-flow/useless-loop.js +2 -2
  6. package/dataflow/cluster.js +3 -3
  7. package/dataflow/environments/built-in-config.d.ts +8 -4
  8. package/dataflow/environments/built-in.d.ts +27 -14
  9. package/dataflow/environments/built-in.js +27 -12
  10. package/dataflow/environments/default-builtin-config.d.ts +614 -3
  11. package/dataflow/environments/default-builtin-config.js +50 -15
  12. package/dataflow/environments/environment.js +3 -2
  13. package/dataflow/environments/identifier.d.ts +5 -1
  14. package/dataflow/environments/reference-to-maybe.d.ts +2 -2
  15. package/dataflow/environments/reference-to-maybe.js +23 -14
  16. package/dataflow/environments/resolve-by-name.d.ts +6 -2
  17. package/dataflow/environments/resolve-by-name.js +5 -1
  18. package/dataflow/environments/scoping.js +1 -3
  19. package/dataflow/eval/resolve/alias-tracking.js +5 -1
  20. package/dataflow/extractor.js +3 -3
  21. package/dataflow/fn/exceptions-of-function.d.ts +13 -0
  22. package/dataflow/fn/exceptions-of-function.js +47 -0
  23. package/dataflow/fn/higher-order-function.d.ts +1 -1
  24. package/dataflow/fn/higher-order-function.js +3 -3
  25. package/dataflow/fn/recursive-function.d.ts +6 -0
  26. package/dataflow/fn/recursive-function.js +32 -0
  27. package/dataflow/graph/call-graph.d.ts +10 -0
  28. package/dataflow/graph/call-graph.js +209 -0
  29. package/dataflow/graph/dataflowgraph-builder.d.ts +7 -2
  30. package/dataflow/graph/dataflowgraph-builder.js +14 -9
  31. package/dataflow/graph/diff-dataflow-graph.js +96 -2
  32. package/dataflow/graph/graph.d.ts +10 -7
  33. package/dataflow/graph/graph.js +7 -8
  34. package/dataflow/graph/vertex.d.ts +6 -3
  35. package/dataflow/hooks.d.ts +30 -0
  36. package/dataflow/hooks.js +38 -0
  37. package/dataflow/info.d.ts +28 -5
  38. package/dataflow/info.js +66 -31
  39. package/dataflow/internal/linker.d.ts +13 -3
  40. package/dataflow/internal/linker.js +155 -53
  41. package/dataflow/internal/process/functions/call/argument/unpack-argument.d.ts +4 -0
  42. package/dataflow/internal/process/functions/call/argument/unpack-argument.js +7 -0
  43. package/dataflow/internal/process/functions/call/built-in/built-in-apply.d.ts +1 -1
  44. package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +19 -3
  45. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +14 -0
  46. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +30 -0
  47. package/dataflow/internal/process/functions/call/built-in/built-in-eval.js +2 -1
  48. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +24 -17
  49. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +2 -1
  50. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.d.ts +5 -1
  51. package/dataflow/internal/process/functions/call/built-in/built-in-function-definition.js +59 -21
  52. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +4 -3
  53. package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.d.ts +34 -0
  54. package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.js +92 -0
  55. package/dataflow/internal/process/functions/call/built-in/built-in-repeat-loop.js +1 -0
  56. package/dataflow/internal/process/functions/call/built-in/built-in-stop-if-not.d.ts +21 -0
  57. package/dataflow/internal/process/functions/call/built-in/built-in-stop-if-not.js +129 -0
  58. package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.d.ts +16 -0
  59. package/dataflow/internal/process/functions/call/built-in/built-in-try-catch.js +127 -0
  60. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +5 -3
  61. package/dataflow/internal/process/functions/call/common.d.ts +13 -1
  62. package/dataflow/internal/process/functions/call/common.js +33 -2
  63. package/dataflow/internal/process/functions/call/known-call-handling.d.ts +13 -1
  64. package/dataflow/internal/process/functions/call/known-call-handling.js +29 -3
  65. package/dataflow/internal/process/functions/call/named-call-handling.js +2 -1
  66. package/dataflow/internal/process/functions/call/unnamed-call-handling.js +6 -4
  67. package/dataflow/internal/process/functions/process-argument.js +7 -6
  68. package/dataflow/internal/process/functions/process-parameter.js +2 -1
  69. package/dataflow/internal/process/process-named-call.d.ts +2 -2
  70. package/dataflow/internal/process/process-symbol.js +3 -2
  71. package/dataflow/internal/process/process-value.d.ts +3 -2
  72. package/dataflow/internal/process/process-value.js +8 -6
  73. package/dataflow/origin/dfg-get-origin.js +2 -1
  74. package/dataflow/origin/dfg-get-symbol-refs.js +1 -1
  75. package/documentation/doc-readme.d.ts +1 -1
  76. package/documentation/doc-readme.js +6 -6
  77. package/documentation/doc-util/doc-code.js +1 -1
  78. package/documentation/doc-util/doc-dfg.d.ts +1 -0
  79. package/documentation/doc-util/doc-dfg.js +7 -4
  80. package/documentation/doc-util/doc-query.d.ts +1 -0
  81. package/documentation/doc-util/doc-query.js +1 -1
  82. package/documentation/doc-util/doc-repl.d.ts +2 -1
  83. package/documentation/doc-util/doc-repl.js +11 -3
  84. package/documentation/wiki-analyzer.js +2 -0
  85. package/documentation/wiki-dataflow-graph.js +59 -16
  86. package/documentation/wiki-interface.js +33 -5
  87. package/documentation/wiki-mk/doc-context.d.ts +2 -1
  88. package/documentation/wiki-mk/doc-context.js +2 -2
  89. package/documentation/wiki-mk/doc-maker.js +4 -3
  90. package/documentation/wiki-normalized-ast.js +6 -0
  91. package/documentation/wiki-query.js +109 -1
  92. package/linter/linter-rules.d.ts +1 -1
  93. package/linter/rules/seeded-randomness.js +17 -12
  94. package/linter/rules/useless-loop.d.ts +1 -1
  95. package/package.json +9 -9
  96. package/project/cache/flowr-analyzer-cache.d.ts +11 -0
  97. package/project/cache/flowr-analyzer-cache.js +19 -0
  98. package/project/context/flowr-analyzer-dependencies-context.d.ts +6 -1
  99. package/project/context/flowr-analyzer-dependencies-context.js +6 -0
  100. package/project/context/flowr-analyzer-files-context.d.ts +5 -2
  101. package/project/context/flowr-analyzer-files-context.js +24 -17
  102. package/project/context/flowr-file.d.ts +9 -4
  103. package/project/context/flowr-file.js +20 -6
  104. package/project/flowr-analyzer.d.ts +11 -0
  105. package/project/flowr-analyzer.js +6 -0
  106. package/project/plugins/file-plugins/files/flowr-description-file.d.ts +8 -0
  107. package/project/plugins/file-plugins/files/flowr-description-file.js +36 -3
  108. package/project/plugins/file-plugins/files/flowr-jupyter-file.js +1 -1
  109. package/project/plugins/file-plugins/files/flowr-namespace-file.js +1 -1
  110. package/project/plugins/file-plugins/files/flowr-news-file.js +1 -1
  111. package/project/plugins/file-plugins/files/flowr-rmarkdown-file.js +1 -1
  112. package/project/plugins/file-plugins/flowr-analyzer-description-file-plugin.js +1 -1
  113. package/project/plugins/file-plugins/flowr-analyzer-file-plugin.d.ts +4 -1
  114. package/project/plugins/file-plugins/flowr-analyzer-file-plugin.js +3 -0
  115. package/project/plugins/file-plugins/{flowr-analyzer-namespace-file-plugin.d.ts → flowr-analyzer-namespace-files-plugin.d.ts} +1 -1
  116. package/project/plugins/file-plugins/{flowr-analyzer-namespace-file-plugin.js → flowr-analyzer-namespace-files-plugin.js} +4 -4
  117. package/project/plugins/file-plugins/flowr-analyzer-test-file-plugin.d.ts +26 -0
  118. package/project/plugins/file-plugins/flowr-analyzer-test-file-plugin.js +39 -0
  119. package/project/plugins/file-plugins/flowr-analyzer-vignette-file-plugin.d.ts +26 -0
  120. package/project/plugins/file-plugins/flowr-analyzer-vignette-file-plugin.js +39 -0
  121. package/project/plugins/flowr-analyzer-plugin-defaults.js +6 -2
  122. package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-description-file-plugin.js +3 -13
  123. package/project/plugins/package-version-plugins/package.d.ts +1 -1
  124. package/project/plugins/package-version-plugins/package.js +3 -3
  125. package/project/plugins/plugin-registry.d.ts +4 -2
  126. package/project/plugins/plugin-registry.js +6 -2
  127. package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.d.ts +11 -0
  128. package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +5 -2
  129. package/queries/catalog/call-context-query/call-context-query-format.d.ts +4 -12
  130. package/queries/catalog/call-graph-query/call-graph-query-executor.d.ts +6 -0
  131. package/queries/catalog/call-graph-query/call-graph-query-executor.js +21 -0
  132. package/queries/catalog/call-graph-query/call-graph-query-format.d.ts +21 -0
  133. package/queries/catalog/call-graph-query/call-graph-query-format.js +32 -0
  134. package/queries/catalog/dataflow-query/dataflow-query-executor.js +4 -3
  135. package/queries/catalog/dependencies-query/dependencies-query-executor.js +29 -3
  136. package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +1 -0
  137. package/queries/catalog/dependencies-query/function-info/function-info.d.ts +8 -1
  138. package/queries/catalog/dependencies-query/function-info/write-functions.js +13 -0
  139. package/queries/catalog/does-call-query/does-call-query-executor.d.ts +6 -0
  140. package/queries/catalog/does-call-query/does-call-query-executor.js +100 -0
  141. package/queries/catalog/does-call-query/does-call-query-format.d.ts +51 -0
  142. package/queries/catalog/does-call-query/does-call-query-format.js +102 -0
  143. package/queries/catalog/files-query/files-query-executor.js +4 -4
  144. package/queries/catalog/files-query/files-query-format.d.ts +2 -1
  145. package/queries/catalog/files-query/files-query-format.js +18 -2
  146. package/queries/catalog/id-map-query/id-map-query-executor.js +4 -3
  147. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-executor.d.ts +18 -0
  148. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-executor.js +56 -0
  149. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-format.d.ts +34 -0
  150. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-format.js +54 -0
  151. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.js +3 -28
  152. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.d.ts +6 -0
  153. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.js +12 -0
  154. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-executor.d.ts +6 -0
  155. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-executor.js +23 -0
  156. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-format.d.ts +28 -0
  157. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-format.js +44 -0
  158. package/queries/catalog/linter-query/linter-query-format.js +4 -1
  159. package/queries/catalog/location-map-query/location-map-query-executor.js +1 -1
  160. package/queries/catalog/normalized-ast-query/normalized-ast-query-executor.js +4 -3
  161. package/queries/catalog/project-query/project-query-executor.js +9 -3
  162. package/queries/catalog/project-query/project-query-format.d.ts +6 -1
  163. package/queries/catalog/project-query/project-query-format.js +35 -9
  164. package/queries/query.d.ts +34 -2
  165. package/queries/query.js +9 -0
  166. package/r-bridge/data/data.d.ts +10 -5
  167. package/r-bridge/data/data.js +11 -5
  168. package/r-bridge/lang-4.x/ast/model/model.d.ts +7 -7
  169. package/r-bridge/lang-4.x/ast/model/nodes/r-access.d.ts +2 -2
  170. package/r-bridge/lang-4.x/ast/model/nodes/r-argument.d.ts +2 -2
  171. package/r-bridge/lang-4.x/ast/model/nodes/r-binary-op.d.ts +2 -2
  172. package/r-bridge/lang-4.x/ast/model/nodes/r-comment.d.ts +5 -2
  173. package/r-bridge/lang-4.x/ast/model/nodes/r-comment.js +8 -0
  174. package/r-bridge/lang-4.x/ast/model/nodes/r-expression-list.d.ts +2 -2
  175. package/r-bridge/lang-4.x/ast/model/nodes/r-for-loop.d.ts +2 -2
  176. package/r-bridge/lang-4.x/ast/model/nodes/r-function-call.d.ts +3 -3
  177. package/r-bridge/lang-4.x/ast/model/nodes/r-function-definition.d.ts +2 -2
  178. package/r-bridge/lang-4.x/ast/model/nodes/r-if-then-else.d.ts +2 -2
  179. package/r-bridge/lang-4.x/ast/model/nodes/r-parameter.d.ts +2 -2
  180. package/r-bridge/lang-4.x/ast/model/nodes/r-pipe.d.ts +2 -2
  181. package/r-bridge/lang-4.x/ast/model/nodes/r-repeat-loop.d.ts +2 -2
  182. package/r-bridge/lang-4.x/ast/model/nodes/r-unary-op.d.ts +2 -2
  183. package/r-bridge/lang-4.x/ast/model/nodes/r-while-loop.d.ts +2 -2
  184. package/r-bridge/lang-4.x/ast/parser/main/internal/other/normalize-comment.js +0 -1
  185. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +0 -2
  186. package/r-bridge/roxygen2/roxygen-ast.d.ts +218 -0
  187. package/r-bridge/roxygen2/roxygen-ast.js +82 -0
  188. package/r-bridge/roxygen2/roxygen-parse.d.ts +24 -0
  189. package/r-bridge/roxygen2/roxygen-parse.js +214 -0
  190. package/reconstruct/auto-select/magic-comments.js +4 -4
  191. package/slicing/static/slice-call.js +3 -4
  192. package/slicing/static/static-slicer.js +2 -2
  193. package/statistics/features/supported/defined-functions/defined-functions.js +1 -1
  194. package/util/collections/defaultmap.d.ts +3 -3
  195. package/util/mermaid/dfg.js +5 -5
  196. package/util/objects.js +1 -1
  197. package/util/r-author.d.ts +5 -0
  198. package/util/r-author.js +110 -0
  199. package/util/r-license.d.ts +10 -1
  200. package/util/r-license.js +27 -6
  201. package/util/r-version.d.ts +19 -0
  202. package/util/r-version.js +106 -0
  203. package/util/range.d.ts +6 -0
  204. package/util/range.js +7 -0
  205. package/util/simple-df/dfg-ascii.js +2 -2
  206. package/util/text/args.d.ts +9 -0
  207. package/util/text/args.js +65 -0
  208. package/util/version.js +1 -1
@@ -35,6 +35,10 @@ const inspect_higher_order_query_executor_1 = require("../queries/catalog/inspec
35
35
  const doc_escape_1 = require("./doc-util/doc-escape");
36
36
  const doc_maker_1 = require("./wiki-mk/doc-maker");
37
37
  const files_query_executor_1 = require("../queries/catalog/files-query/files-query-executor");
38
+ const call_graph_query_executor_1 = require("../queries/catalog/call-graph-query/call-graph-query-executor");
39
+ const inspect_recursion_query_executor_1 = require("../queries/catalog/inspect-recursion-query/inspect-recursion-query-executor");
40
+ const does_call_query_executor_1 = require("../queries/catalog/does-call-query/does-call-query-executor");
41
+ const inspect_exception_query_executor_1 = require("../queries/catalog/inspect-exceptions-query/inspect-exception-query-executor");
38
42
  (0, doc_query_1.registerQueryDocumentation)('call-context', {
39
43
  name: 'Call-Context Query',
40
44
  type: 'active',
@@ -124,6 +128,47 @@ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
124
128
  `;
125
129
  }
126
130
  });
131
+ (0, doc_query_1.registerQueryDocumentation)('call-graph', {
132
+ name: 'Call-Graph Query',
133
+ type: 'active',
134
+ shortDescription: 'Returns the call graph of the given code.',
135
+ functionName: call_graph_query_executor_1.executeCallGraphQuery.name,
136
+ functionFile: '../queries/catalog/call-graph-query/call-graph-query-executor.ts',
137
+ buildExplanation: async (shell, ctx) => {
138
+ const exampleCode = 'x + 1';
139
+ return `
140
+ This query calculates and returns the ${ctx.linkPage('wiki/Dataflow Graph', 'call graph', 'perspectives-cg')} of the given code.
141
+
142
+ Using the example code \`${exampleCode}\`, the following query returns the dataflow graph of the code:
143
+ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
144
+ type: 'call-graph'
145
+ }], { showCode: true, collapseQuery: true })}
146
+ `;
147
+ }
148
+ });
149
+ (0, doc_query_1.registerQueryDocumentation)('does-call', {
150
+ name: 'Does-Call Query',
151
+ type: 'active',
152
+ shortDescription: 'Checks whether a function calls another function matching given constraints.',
153
+ functionName: does_call_query_executor_1.executeDoesCallQuery.name,
154
+ functionFile: '../queries/catalog/does-call-query/does-call-query-executor.ts',
155
+ buildExplanation: async (shell) => {
156
+ const exampleCode = 'f <- function(x) { eval(x) };\nf("1 + 1")';
157
+ return `
158
+ This query checks whether a function calls another function matching given constraints.
159
+
160
+ Using the example code:
161
+ ${(0, doc_code_1.codeBlock)('r', exampleCode)}
162
+ the following query checks whether the call to \`f\` calls \`eval\`:
163
+ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
164
+ type: 'does-call',
165
+ queryId: 'calls-eval',
166
+ call: '2@f',
167
+ calls: { type: 'name', name: 'eval', nameExact: true }
168
+ }], { showCode: true, collapseQuery: false, shorthand: '(2@f:"eval")' })}
169
+ `;
170
+ }
171
+ });
127
172
  (0, doc_query_1.registerQueryDocumentation)('files', {
128
173
  name: 'Files Query',
129
174
  type: 'active',
@@ -239,7 +284,67 @@ Please note, that functions that are just identities (e.g., \`function(x) x\`) a
239
284
  Using the example code \`${exampleCode}\` the following query returns the information for all identified function definitions whether they are higher-order functions:
240
285
  ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
241
286
  type: 'inspect-higher-order',
242
- }], { showCode: true })}
287
+ }], { showCode: true, collapseQuery: true })}
288
+
289
+ This query also supports a slicing criterion based query mode that only returns information for functions matching the given criteria:
290
+ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
291
+ type: 'inspect-higher-order',
292
+ filter: ['1@function']
293
+ }], { showCode: false, shorthand: (0, doc_query_1.sliceQueryShorthand)(['1@function'], (0, doc_escape_1.escapeNewline)(exampleCode)) })}
294
+ `;
295
+ }
296
+ });
297
+ (0, doc_query_1.registerQueryDocumentation)('inspect-recursion', {
298
+ name: 'Inspect Recursive Functions Query',
299
+ type: 'active',
300
+ shortDescription: 'Determine whether functions are recursive',
301
+ functionName: inspect_recursion_query_executor_1.executeRecursionQuery.name,
302
+ functionFile: '../queries/catalog/inspect-recursion-query/inspect-recursion-query-executor.ts',
303
+ buildExplanation: async (shell) => {
304
+ const exampleCode = 'fact <- function(n) { if(n <= 1) 1 else n * fact(n - 1) }';
305
+ return `
306
+ With this query you can identify which functions in the code are recursive.
307
+ Please note, that functions that *may* be recursive due to indirect calls are also considered recursive.
308
+
309
+ Using the example code \`${exampleCode}\` the following query returns the information for all identified function definitions whether they are recursive:
310
+ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
311
+ type: 'inspect-recursion',
312
+ }], { showCode: true, collapseQuery: true })}
313
+
314
+ This query also supports a slicing criterion based query mode that only returns information for functions matching the given criteria:
315
+ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
316
+ type: 'inspect-recursion',
317
+ filter: ['1@function']
318
+ }], { showCode: true, shorthand: (0, doc_query_1.sliceQueryShorthand)(['1@function'], (0, doc_escape_1.escapeNewline)(exampleCode)) })}
319
+ `;
320
+ }
321
+ });
322
+ (0, doc_query_1.registerQueryDocumentation)('inspect-exception', {
323
+ name: 'Inspect Exceptions of Functions Query',
324
+ type: 'active',
325
+ shortDescription: 'Determine whether functions throw exceptions (known to flowR)',
326
+ functionName: inspect_exception_query_executor_1.executeExceptionQuery.name,
327
+ functionFile: '../queries/catalog/inspect-exceptions-query/inspect-exception-query-executor.ts',
328
+ buildExplanation: async (shell) => {
329
+ const exampleCode = `mayFail <- function(x) {
330
+ if(x < 0) stop("Negative value!")
331
+ else sqrt(x)
332
+ }
333
+ safeFail <- function(x) {
334
+ tryCatch(
335
+ mayFail(x),
336
+ error = function(e) { NA }
337
+ )
338
+ }`;
339
+ return `
340
+ With this query you can identify which functions in the code throw exceptions (known to flowR).
341
+
342
+ Using the following example code:
343
+ ${(0, doc_code_1.codeBlock)('r', exampleCode)}
344
+ the following query returns the information for all identified function definitions whether they throw exceptions:
345
+ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
346
+ type: 'inspect-exception',
347
+ }], { showCode: true, collapseQuery: true })}
243
348
  `;
244
349
  }
245
350
  });
@@ -350,6 +455,9 @@ ${await (0, doc_repl_1.documentReplSession)(shell, [
350
455
  description: 'Set the slicing threshold to 10,000.'
351
456
  }
352
457
  ])}
458
+
459
+ One of the most useful options to change on-the-fly are probably those under \`repl\`. For example, setting \`repl.quickStats=true\`
460
+ enables quick statistics after each REPL command.
353
461
  `;
354
462
  }
355
463
  });
@@ -290,7 +290,7 @@ export declare const LintingRules: {
290
290
  readonly certainty: import("./linter-format").LintingRuleCertainty.BestEffort;
291
291
  readonly tags: readonly [import("./linter-tags").LintingRuleTag.Smell, import("./linter-tags").LintingRuleTag.Readability];
292
292
  readonly defaultConfig: {
293
- readonly loopyFunctions: Set<"builtin:default" | "builtin:eval" | "builtin:apply" | "builtin:expression-list" | "builtin:source" | "builtin:access" | "builtin:if-then-else" | "builtin:get" | "builtin:rm" | "builtin:library" | "builtin:assignment" | "builtin:special-bin-op" | "builtin:pipe" | "builtin:function-definition" | "builtin:quote" | "builtin:for-loop" | "builtin:repeat-loop" | "builtin:while-loop" | "builtin:replacement" | "builtin:list" | "builtin:vector">;
293
+ readonly loopyFunctions: Set<"builtin:access" | "builtin:apply" | "builtin:assignment" | "builtin:assignment-like" | "builtin:default" | "builtin:eval" | "builtin:expression-list" | "builtin:for-loop" | "builtin:function-definition" | "builtin:get" | "builtin:if-then-else" | "builtin:library" | "builtin:list" | "builtin:pipe" | "builtin:quote" | "builtin:register-hook" | "builtin:repeat-loop" | "builtin:replacement" | "builtin:rm" | "builtin:source" | "builtin:special-bin-op" | "builtin:stopifnot" | "builtin:try" | "builtin:vector" | "builtin:while-loop">;
294
294
  };
295
295
  };
296
296
  };
@@ -55,22 +55,24 @@ exports.SEEDED_RANDOMNESS = {
55
55
  // filter by calls that aren't preceded by a randomness producer
56
56
  .flatMap(element => {
57
57
  const dfgElement = dataflow.graph.getVertex(element.searchElement.node.info.id);
58
- const cds = dfgElement ? new Set(dfgElement.cds) : new Set();
58
+ const cds = dfgElement ? new Set(dfgElement.controlDependencies) : new Set();
59
59
  const producers = (0, search_enrichers_1.enrichmentContent)(element.searchElement, search_enrichers_1.Enrichment.LastCall).linkedIds
60
60
  .map(e => dataflow.graph.getVertex(e.node.info.id));
61
61
  const { assignment, func } = Object.groupBy(producers, f => assignmentArgIndexes.has(f.name) ? 'assignment' : 'func');
62
62
  let nonConstant = false;
63
- let otherBranch = false;
63
+ const cdsOfProduces = new Set();
64
64
  // function calls are already taken care of through the LastCall enrichment itself
65
65
  for (const f of func ?? []) {
66
66
  if (isConstantArgument(dataflow.graph, f, 0, analyzer.inspectContext())) {
67
- const fCds = new Set(f.cds).difference(cds);
67
+ const fCds = new Set(f.controlDependencies).difference(cds);
68
+ metadata.callsWithFunctionProducers++;
68
69
  if (fCds.size <= 0 || (0, info_1.happensInEveryBranchSet)(fCds)) {
69
- metadata.callsWithFunctionProducers++;
70
70
  return [];
71
71
  }
72
72
  else {
73
- otherBranch = true;
73
+ for (const f of fCds) {
74
+ cdsOfProduces.add(f);
75
+ }
74
76
  }
75
77
  }
76
78
  else {
@@ -84,28 +86,31 @@ exports.SEEDED_RANDOMNESS = {
84
86
  if (dest !== undefined && assignmentProducers.has((0, node_id_1.recoverName)(dest, dataflow.graph.idMap))) {
85
87
  // we either have arg index 0 or 1 for the assignmentProducers destination, so we select the assignment value as 1-argIdx here
86
88
  if (isConstantArgument(dataflow.graph, a, 1 - argIdx, analyzer.inspectContext())) {
87
- const aCds = new Set(a.cds).difference(cds);
89
+ const aCds = new Set(a.controlDependencies).difference(cds);
88
90
  if (aCds.size <= 0 || (0, info_1.happensInEveryBranchSet)(aCds)) {
89
91
  metadata.callsWithAssignmentProducers++;
90
92
  return [];
91
93
  }
92
94
  else {
93
- otherBranch = true;
95
+ for (const f of aCds) {
96
+ cdsOfProduces.add(f);
97
+ }
94
98
  }
95
99
  }
96
- else {
97
- nonConstant = true;
98
- }
99
100
  }
100
101
  }
102
+ if ((0, info_1.happensInEveryBranchSet)(cdsOfProduces)) {
103
+ // all producers happen in every branch, so we are good
104
+ return [];
105
+ }
101
106
  if (nonConstant) {
102
107
  metadata.callsWithNonConstantProducers++;
103
108
  }
104
- if (otherBranch) {
109
+ if (cdsOfProduces.size > 0) {
105
110
  metadata.callsWithOtherBranchProducers++;
106
111
  }
107
112
  return [{
108
- certainty: otherBranch ? linter_format_1.LintingResultCertainty.Uncertain : linter_format_1.LintingResultCertainty.Certain,
113
+ certainty: cdsOfProduces.size > 0 ? linter_format_1.LintingResultCertainty.Uncertain : linter_format_1.LintingResultCertainty.Certain,
109
114
  function: element.target,
110
115
  range: element.range
111
116
  }];
@@ -41,7 +41,7 @@ export declare const USELESS_LOOP: {
41
41
  readonly certainty: LintingRuleCertainty.BestEffort;
42
42
  readonly tags: readonly [LintingRuleTag.Smell, LintingRuleTag.Readability];
43
43
  readonly defaultConfig: {
44
- readonly loopyFunctions: Set<"builtin:default" | "builtin:eval" | "builtin:apply" | "builtin:expression-list" | "builtin:source" | "builtin:access" | "builtin:if-then-else" | "builtin:get" | "builtin:rm" | "builtin:library" | "builtin:assignment" | "builtin:special-bin-op" | "builtin:pipe" | "builtin:function-definition" | "builtin:quote" | "builtin:for-loop" | "builtin:repeat-loop" | "builtin:while-loop" | "builtin:replacement" | "builtin:list" | "builtin:vector">;
44
+ readonly loopyFunctions: Set<"builtin:access" | "builtin:apply" | "builtin:assignment" | "builtin:assignment-like" | "builtin:default" | "builtin:eval" | "builtin:expression-list" | "builtin:for-loop" | "builtin:function-definition" | "builtin:get" | "builtin:if-then-else" | "builtin:library" | "builtin:list" | "builtin:pipe" | "builtin:quote" | "builtin:register-hook" | "builtin:repeat-loop" | "builtin:replacement" | "builtin:rm" | "builtin:source" | "builtin:special-bin-op" | "builtin:stopifnot" | "builtin:try" | "builtin:vector" | "builtin:while-loop">;
45
45
  };
46
46
  };
47
47
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.7.6",
3
+ "version": "2.8.0",
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": {
@@ -15,7 +15,8 @@
15
15
  "setup:dev": "git lfs fetch && npm ci && git config --local core.hooksPath .githooks/ && git push --dry-run",
16
16
  "main": "npm run build:bundle-flowr && node dist/src/cli/flowr.min.js",
17
17
  "flowr": "npm run main --",
18
- "main-dev": "ts-node-dev src/cli/flowr.ts",
18
+ "flowr:dev": "npm run main-dev --",
19
+ "main-dev": "node -r ts-node/register --watch src/cli/flowr.ts",
19
20
  "publish-library": "cp .npmignore package.json README.md LICENSE dist/src/ && cd dist/src && npm publish --access public",
20
21
  "release": "npx release-it --ci",
21
22
  "stats": "ts-node src/cli/statistics-app.ts",
@@ -27,14 +28,14 @@
27
28
  "export-quads": "ts-node src/cli/export-quads-app.ts",
28
29
  "capabilities-markdown": "ts-node src/documentation/doc-capabilities.ts",
29
30
  "wiki": "ts-node src/cli/wiki.ts",
30
- "wiki:watch": "ts-node-dev src/cli/wiki.ts -- --keep-alive",
31
+ "wiki:watch": "node -r ts-node/register --watch src/cli/wiki.ts --keep-alive",
31
32
  "build": "tsc --project .",
32
33
  "build-dev": "npm run build && npm run build:copy-wasm",
33
34
  "build:bundle-flowr": "npm run build && esbuild --bundle dist/src/cli/flowr.js --platform=node --tree-shaking=true --bundle --minify --external:clipboardy --target=node22 --outfile=dist/src/cli/flowr.min.js && npm run build:copy-wasm",
34
35
  "build:copy-wasm": "mkdir -p dist/node_modules/@eagleoutice/tree-sitter-r/ && mkdir -p dist/node_modules/web-tree-sitter && cp node_modules/@eagleoutice/tree-sitter-r/tree-sitter-r.wasm dist/node_modules/@eagleoutice/tree-sitter-r/ && cp node_modules/web-tree-sitter/tree-sitter.wasm dist/node_modules/web-tree-sitter/",
35
36
  "lint-local": "npx eslint --version && npx eslint src/ test/ --rule \"no-warning-comments: off\"",
36
37
  "lint": "npm run license-compat -- --summary && npx eslint --version && npx eslint src/ test/",
37
- "license-compat": "license-checker --onlyAllow 'MIT;MIT OR X11;GPLv2;LGPL;GNUGPL;ISC;Apache-2.0;FreeBSD;BSD-2-Clause;clearbsd;ModifiedBSD;BSD-3-Clause;Python-2.0;Unlicense;WTFPL;BlueOak-1.0.0;CC-BY-4.0;CC-BY-3.0;CC0-1.0;0BSD'",
38
+ "license-compat": "license-checker-rseidelsohn --onlyAllow 'MIT;MIT OR X11;GPLv2;LGPL;GNUGPL;ISC;Apache-2.0;FreeBSD;BSD-2-Clause;clearbsd;ModifiedBSD;BSD-3-Clause;Python-2.0;Unlicense;WTFPL;BlueOak-1.0.0;CC-BY-4.0;CC-BY-3.0;CC0-1.0;0BSD'",
38
39
  "doc": "typedoc",
39
40
  "test": "vitest --exclude \"test/system-tests/**\" --config test/vitest.config.mts",
40
41
  "test:system": "vitest --dir test/system-tests --config test/system-tests/vitest.config.mts",
@@ -182,17 +183,16 @@
182
183
  "@types/ws": "^8.18.1",
183
184
  "@typescript-eslint/eslint-plugin": "^8.40.0",
184
185
  "@vitest/coverage-v8": "^3.2.4",
185
- "esbuild": "^0.25.9",
186
+ "esbuild": "^0.27.2",
186
187
  "eslint": "^9.34.0",
187
- "license-checker": "^25.0.1",
188
+ "license-checker-rseidelsohn": "^4.4.2",
188
189
  "npm-run-all": "^4.1.5",
189
- "release-it": "^19.0.2",
190
+ "release-it": "^19.2.2",
190
191
  "ts-node": "^10.9.2",
191
- "ts-node-dev": "^2.0.0",
192
192
  "typedoc": "^0.27.7",
193
193
  "typedoc-plugin-missing-exports": "^3.1.0",
194
194
  "typedoc-theme-hierarchy": "^5.0.4",
195
- "typedoc-umlclass": "^0.10.1",
195
+ "typedoc-umlclass": "^0.10.2",
196
196
  "typescript": "^5.7.3",
197
197
  "vitest": "^3.2.4"
198
198
  },
@@ -9,6 +9,7 @@ import type { CfgSimplificationPassName } from '../../control-flow/cfg-simplific
9
9
  import type { ControlFlowInformation } from '../../control-flow/control-flow-graph';
10
10
  import type { CfgKind } from '../cfg-kind';
11
11
  import type { FlowrAnalyzerContext } from '../context/flowr-analyzer-context';
12
+ import type { CallGraph } from '../../dataflow/graph/call-graph';
12
13
  interface FlowrAnalyzerCacheOptions<Parser extends KnownParser> {
13
14
  parser: Parser;
14
15
  context: FlowrAnalyzerContext;
@@ -23,6 +24,7 @@ export declare class FlowrAnalyzerCache<Parser extends KnownParser> extends Flow
23
24
  private args;
24
25
  private pipeline;
25
26
  private controlFlowCache;
27
+ private callGraphCache;
26
28
  protected constructor(args: FlowrAnalyzerCacheOptions<Parser>);
27
29
  private initCacheProviders;
28
30
  static create<Parser extends KnownParser>(data: FlowrAnalyzerCacheOptions<Parser>): FlowrAnalyzerCache<Parser>;
@@ -80,5 +82,14 @@ export declare class FlowrAnalyzerCache<Parser extends KnownParser> extends Flow
80
82
  * @see {@link FlowrAnalyzerCache#controlflow} - to get the control flow graph, computing if necessary.
81
83
  */
82
84
  peekControlflow(kind: CfgKind, simplifications: readonly CfgSimplificationPassName[] | undefined): ControlFlowInformation | undefined;
85
+ /**
86
+ * Get the call graph for the request, computing if necessary.
87
+ * @param force - Do not use the cache, instead force new analyses.
88
+ */
89
+ callGraph(force?: boolean): Promise<CallGraph>;
90
+ /**
91
+ * Get the call graph for the request if already available, otherwise return `undefined`.
92
+ */
93
+ peekCallGraph(): CallGraph | undefined;
83
94
  }
84
95
  export {};
@@ -5,6 +5,7 @@ const flowr_cache_1 = require("./flowr-cache");
5
5
  const default_pipelines_1 = require("../../core/steps/pipeline/default-pipelines");
6
6
  const assert_1 = require("../../util/assert");
7
7
  const flowr_analyzer_controlflow_cache_1 = require("./flowr-analyzer-controlflow-cache");
8
+ const call_graph_1 = require("../../dataflow/graph/call-graph");
8
9
  /**
9
10
  * This provides the full analyzer caching layer, please avoid using this directly
10
11
  * and prefer the {@link FlowrAnalyzer}.
@@ -13,6 +14,7 @@ class FlowrAnalyzerCache extends flowr_cache_1.FlowrCache {
13
14
  args;
14
15
  pipeline = undefined;
15
16
  controlFlowCache = undefined;
17
+ callGraphCache = undefined;
16
18
  constructor(args) {
17
19
  super();
18
20
  this.args = args;
@@ -24,6 +26,7 @@ class FlowrAnalyzerCache extends flowr_cache_1.FlowrCache {
24
26
  getId: this.args.getId
25
27
  });
26
28
  this.controlFlowCache = new flowr_analyzer_controlflow_cache_1.FlowrAnalyzerControlFlowCache();
29
+ this.callGraphCache = undefined;
27
30
  }
28
31
  static create(data) {
29
32
  return new FlowrAnalyzerCache(data);
@@ -132,6 +135,22 @@ class FlowrAnalyzerCache extends flowr_cache_1.FlowrCache {
132
135
  peekControlflow(kind, simplifications) {
133
136
  return this.controlFlowCache.peek(kind, simplifications);
134
137
  }
138
+ /**
139
+ * Get the call graph for the request, computing if necessary.
140
+ * @param force - Do not use the cache, instead force new analyses.
141
+ */
142
+ async callGraph(force) {
143
+ if (!this.callGraphCache || force) {
144
+ this.callGraphCache = (0, call_graph_1.computeCallGraph)((await this.dataflow(force)).graph);
145
+ }
146
+ return this.callGraphCache;
147
+ }
148
+ /**
149
+ * Get the call graph for the request if already available, otherwise return `undefined`.
150
+ */
151
+ peekCallGraph() {
152
+ return this.callGraphCache;
153
+ }
135
154
  }
136
155
  exports.FlowrAnalyzerCache = FlowrAnalyzerCache;
137
156
  //# sourceMappingURL=flowr-analyzer-cache.js.map
@@ -23,7 +23,11 @@ export interface ReadOnlyFlowrAnalyzerDependenciesContext {
23
23
  * @param name - The name of the dependency to get.
24
24
  * @returns The dependency with the given name, or undefined if it does not exist.
25
25
  */
26
- getDependency(name: string): Package | undefined;
26
+ getDependency(name: string): Readonly<Package> | undefined;
27
+ /**
28
+ * Get all dependencies known to this context.
29
+ */
30
+ getDependencies(): readonly Readonly<Package>[];
27
31
  }
28
32
  /**
29
33
  * This context is responsible for managing the dependencies of the project, including their versions and interplays with {@link FlowrAnalyzerPackageVersionsPlugin}s.
@@ -40,4 +44,5 @@ export declare class FlowrAnalyzerDependenciesContext extends AbstractFlowrAnaly
40
44
  resolveStaticDependencies(): void;
41
45
  addDependency(pkg: Package): void;
42
46
  getDependency(name: string): Package | undefined;
47
+ getDependencies(): Package[];
43
48
  }
@@ -40,6 +40,12 @@ class FlowrAnalyzerDependenciesContext extends abstract_flowr_analyzer_context_1
40
40
  }
41
41
  return this.dependencies.get(name);
42
42
  }
43
+ getDependencies() {
44
+ if (!this.staticsLoaded) {
45
+ this.resolveStaticDependencies();
46
+ }
47
+ return Array.from(this.dependencies.values());
48
+ }
43
49
  }
44
50
  exports.FlowrAnalyzerDependenciesContext = FlowrAnalyzerDependenciesContext;
45
51
  //# sourceMappingURL=flowr-analyzer-dependencies-context.js.map
@@ -6,6 +6,7 @@ import { FlowrAnalyzerFilePlugin } from '../plugins/file-plugins/flowr-analyzer-
6
6
  import { FlowrFile, type FlowrFileProvider, FileRole } from './flowr-file';
7
7
  import type { FlowrDescriptionFile } from '../plugins/file-plugins/files/flowr-description-file';
8
8
  import type { FlowrNewsFile } from '../plugins/file-plugins/files/flowr-news-file';
9
+ import type { FlowrNamespaceFile } from '../plugins/file-plugins/files/flowr-namespace-file';
9
10
  /**
10
11
  * This is a request to process a folder as a project, which will be expanded by the registered {@link FlowrAnalyzerProjectDiscoveryPlugin}s.
11
12
  */
@@ -20,7 +21,9 @@ export type RAnalysisRequest = RParseRequest | RProjectAnalysisRequest;
20
21
  export type RoleBasedFiles = {
21
22
  [FileRole.Description]: FlowrDescriptionFile[];
22
23
  [FileRole.News]: FlowrNewsFile[];
23
- [FileRole.Namespace]: FlowrFileProvider[];
24
+ [FileRole.Namespace]: FlowrNamespaceFile[];
25
+ [FileRole.Vignette]: FlowrFileProvider[];
26
+ [FileRole.Test]: FlowrFileProvider[];
24
27
  [FileRole.Source]: FlowrFileProvider[];
25
28
  [FileRole.Data]: FlowrFileProvider[];
26
29
  [FileRole.Other]: FlowrFileProvider[];
@@ -123,7 +126,7 @@ export declare class FlowrAnalyzerFilesContext extends AbstractFlowrAnalyzerCont
123
126
  * Add a file to the context. If the file has a special role, it will be added to the corresponding list of special files.
124
127
  * This method also applies any registered {@link FlowrAnalyzerFilePlugin}s to the file before adding it to the context.
125
128
  */
126
- addFile(file: string | FlowrFileProvider | RParseRequestFromFile, role?: FileRole): FlowrFileProvider<{
129
+ addFile(file: string | FlowrFileProvider | RParseRequestFromFile, roles?: readonly FileRole[]): FlowrFileProvider<{
127
130
  toString(): string;
128
131
  }>;
129
132
  hasFile(path: string): boolean;
@@ -14,9 +14,9 @@ const log_1 = require("../../util/log");
14
14
  const fs_1 = __importDefault(require("fs"));
15
15
  const path_1 = __importDefault(require("path"));
16
16
  const fileLog = log_1.log.getSubLogger({ name: 'flowr-analyzer-files-context' });
17
- function wrapFile(file, role) {
17
+ function wrapFile(file, roles) {
18
18
  if (typeof file === 'string') {
19
- return new flowr_file_1.FlowrTextFile(file, role);
19
+ return new flowr_file_1.FlowrTextFile(file, roles);
20
20
  }
21
21
  else if ('request' in file) {
22
22
  return flowr_file_1.FlowrFile.fromRequest(file);
@@ -40,14 +40,7 @@ class FlowrAnalyzerFilesContext extends abstract_flowr_analyzer_context_1.Abstra
40
40
  /** these are all the paths of files that have been considered by the dataflow graph (even if not added) */
41
41
  consideredFiles = [];
42
42
  /* files that are part of the analysis, e.g. source files */
43
- byRole = {
44
- [flowr_file_1.FileRole.Description]: [],
45
- [flowr_file_1.FileRole.News]: [],
46
- [flowr_file_1.FileRole.Namespace]: [],
47
- [flowr_file_1.FileRole.Source]: [],
48
- [flowr_file_1.FileRole.Data]: [],
49
- [flowr_file_1.FileRole.Other]: []
50
- };
43
+ byRole = Object.fromEntries(Object.values(flowr_file_1.FileRole).map(k => [k, []]));
51
44
  constructor(loadingOrder, plugins, fileLoaders) {
52
45
  super(loadingOrder.getAttachedContext(), flowr_analyzer_project_discovery_plugin_1.FlowrAnalyzerProjectDiscoveryPlugin.defaultPlugin(), plugins);
53
46
  this.fileLoaders = [...fileLoaders, flowr_analyzer_file_plugin_1.FlowrAnalyzerFilePlugin.defaultPlugin()];
@@ -56,6 +49,9 @@ class FlowrAnalyzerFilesContext extends abstract_flowr_analyzer_context_1.Abstra
56
49
  reset() {
57
50
  this.loadingOrder.reset();
58
51
  this.files = new Map();
52
+ this.consideredFiles.length = 0;
53
+ this.inlineFiles.length = 0;
54
+ this.byRole = Object.fromEntries(Object.values(flowr_file_1.FileRole).map(k => [k, []]));
59
55
  }
60
56
  /**
61
57
  * Record that a file has been considered during dataflow analysis.
@@ -91,7 +87,7 @@ class FlowrAnalyzerFilesContext extends abstract_flowr_analyzer_context_1.Abstra
91
87
  this.addRequest(req);
92
88
  }
93
89
  else {
94
- this.addFile(req, req.role);
90
+ this.addFile(req, req.roles);
95
91
  }
96
92
  }
97
93
  }
@@ -107,8 +103,8 @@ class FlowrAnalyzerFilesContext extends abstract_flowr_analyzer_context_1.Abstra
107
103
  * Add a file to the context. If the file has a special role, it will be added to the corresponding list of special files.
108
104
  * This method also applies any registered {@link FlowrAnalyzerFilePlugin}s to the file before adding it to the context.
109
105
  */
110
- addFile(file, role) {
111
- const f = this.fileLoadPlugins(wrapFile(file, role));
106
+ addFile(file, roles) {
107
+ const f = this.fileLoadPlugins(wrapFile(file, roles));
112
108
  if (f.path() === flowr_file_1.FlowrFile.INLINE_PATH) {
113
109
  this.inlineFiles.push(f);
114
110
  }
@@ -117,8 +113,10 @@ class FlowrAnalyzerFilesContext extends abstract_flowr_analyzer_context_1.Abstra
117
113
  (0, assert_1.guard)(exist === undefined || exist === f, `File ${f.path()} already added to the context.`);
118
114
  this.files.set(f.path(), f);
119
115
  }
120
- if (f.role) {
121
- this.byRole[f.role].push(f);
116
+ if (f.roles) {
117
+ for (const r of f.roles) {
118
+ this.byRole[r].push(f);
119
+ }
122
120
  }
123
121
  return f;
124
122
  }
@@ -157,8 +155,17 @@ class FlowrAnalyzerFilesContext extends abstract_flowr_analyzer_context_1.Abstra
157
155
  for (const loader of this.fileLoaders) {
158
156
  if (loader.applies(f.path())) {
159
157
  fileLog.debug(`Applying file loader ${loader.name} to file ${f.path()}`);
160
- fFinal = loader.processor(this.ctx, f);
161
- break;
158
+ const res = loader.processor(this.ctx, fFinal);
159
+ if (Array.isArray(res)) {
160
+ fFinal = res[0];
161
+ if (!res[1]) {
162
+ break;
163
+ }
164
+ }
165
+ else {
166
+ fFinal = res;
167
+ break;
168
+ }
162
169
  }
163
170
  }
164
171
  return fFinal;
@@ -17,6 +17,10 @@ export declare enum FileRole {
17
17
  Namespace = "namespace",
18
18
  /** The `NEWS` file in R packages */
19
19
  News = "news",
20
+ /** Vignette files, e.g., R Markdown files in the `vignettes/` folder */
21
+ Vignette = "vignette",
22
+ /** Test source files, e.g., files in the `tests/` folder */
23
+ Test = "test",
20
24
  /** Data files, e.g., `R/sysdata.rda`, currently not specially supported. */
21
25
  Data = "data",
22
26
  /**
@@ -49,11 +53,11 @@ export interface FlowrFileProvider<Content extends {
49
53
  toString(): string;
50
54
  }> {
51
55
  /**
52
- * The role of this file, if any, in general your file should _not_ decide for itself what role it has in the project context,
56
+ * The role(s) of this file, if any, in general your file should _not_ decide for itself what role it has in the project context,
53
57
  * this is for the loaders plugins to decide (cf. {@link PluginType}) as they can, e.g., respect ignore files, updated mappings, etc.
54
58
  * However, they will 1) set this role as soon as they decide on it (using {@link assignRole}) and 2) try to respect an already assigned role (however, user configurations may override this).
55
59
  */
56
- role?: FileRole;
60
+ roles?: readonly FileRole[];
57
61
  /**
58
62
  * The path to the file, this is used for identification and logging purposes.
59
63
  * If the file does not exist on disk, this can be a virtual path (e.g. for inline files).
@@ -80,9 +84,10 @@ export interface FlowrFileProvider<Content extends {
80
84
  export declare abstract class FlowrFile<Content extends StringableContent = StringableContent> implements FlowrFileProvider<Content> {
81
85
  private contentCache;
82
86
  protected filePath: PathLike;
83
- readonly role?: FileRole;
87
+ private _roles?;
84
88
  static readonly INLINE_PATH = "@inline";
85
- constructor(filePath: PathLike, role?: FileRole);
89
+ constructor(filePath: PathLike, roles?: readonly FileRole[]);
90
+ get roles(): readonly FileRole[] | undefined;
86
91
  path(): string;
87
92
  content(): Content;
88
93
  protected abstract loadContent(): Content;
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.FlowrInlineTextFile = exports.FlowrTextFile = exports.FlowrFile = exports.FileRole = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
- const assert_1 = require("../../util/assert");
9
8
  /**
10
9
  * Some files have a special meaning in R projects, e.g., the `DESCRIPTION` file in R packages.
11
10
  * This list may be extended in the future and reflects files that the {@link FlowrAnalyzer} can do something interesting with.
@@ -19,6 +18,10 @@ var FileRole;
19
18
  FileRole["Namespace"] = "namespace";
20
19
  /** The `NEWS` file in R packages */
21
20
  FileRole["News"] = "news";
21
+ /** Vignette files, e.g., R Markdown files in the `vignettes/` folder */
22
+ FileRole["Vignette"] = "vignette";
23
+ /** Test source files, e.g., files in the `tests/` folder */
24
+ FileRole["Test"] = "test";
22
25
  /** Data files, e.g., `R/sysdata.rda`, currently not specially supported. */
23
26
  FileRole["Data"] = "data";
24
27
  /**
@@ -39,11 +42,14 @@ var FileRole;
39
42
  class FlowrFile {
40
43
  contentCache;
41
44
  filePath;
42
- role;
45
+ _roles;
43
46
  static INLINE_PATH = '@inline';
44
- constructor(filePath, role) {
47
+ constructor(filePath, roles) {
45
48
  this.filePath = filePath;
46
- this.role = role;
49
+ this._roles = roles ? Array.from(roles) : undefined;
50
+ }
51
+ get roles() {
52
+ return this._roles;
47
53
  }
48
54
  path() {
49
55
  return this.filePath.toString();
@@ -55,8 +61,16 @@ class FlowrFile {
55
61
  return this.contentCache;
56
62
  }
57
63
  assignRole(role) {
58
- (0, assert_1.guard)(this.role === undefined || this.role === role, `File ${this.filePath.toString()} already has a role assigned: ${this.role}`);
59
- this.role = role;
64
+ if (this._roles === undefined) {
65
+ this._roles = [role];
66
+ }
67
+ else if (this._roles.includes(role)) {
68
+ // already assigned
69
+ return;
70
+ }
71
+ else {
72
+ this._roles.push(role);
73
+ }
60
74
  }
61
75
  /**
62
76
  * Creates a {@link FlowrFile} from a given {@link RParseRequest}.
@@ -15,6 +15,7 @@ import type { RAnalysisRequest } from './context/flowr-analyzer-files-context';
15
15
  import type { RParseRequestFromFile } from '../r-bridge/retriever';
16
16
  import { fileProtocol } from '../r-bridge/retriever';
17
17
  import type { FlowrFileProvider } from './context/flowr-file';
18
+ import type { CallGraph } from '../dataflow/graph/call-graph';
18
19
  /**
19
20
  * Extends the {@link ReadonlyFlowrAnalysisProvider} with methods that allow modifying the analyzer state.
20
21
  */
@@ -102,6 +103,14 @@ export interface ReadonlyFlowrAnalysisProvider<Parser extends KnownParser = Know
102
103
  * Peek at the control flow graph (CFG) for the request, if it was already computed.
103
104
  */
104
105
  peekControlflow(simplifications?: readonly CfgSimplificationPassName[], kind?: CfgKind): ControlFlowInformation | undefined;
106
+ /**
107
+ * Calculate the call graph for the request.
108
+ */
109
+ callGraph(force?: boolean): Promise<CallGraph>;
110
+ /**
111
+ * Peek at the call graph for the request, if it was already computed.
112
+ */
113
+ peekCallGraph(): CallGraph | undefined;
105
114
  /**
106
115
  * Access the query API for the request.
107
116
  * @param query - The list of queries.
@@ -167,6 +176,8 @@ export declare class FlowrAnalyzer<Parser extends KnownParser = KnownParser> imp
167
176
  runFull(force?: boolean): Promise<void>;
168
177
  controlflow(simplifications?: readonly CfgSimplificationPassName[], kind?: CfgKind, force?: boolean): Promise<ControlFlowInformation>;
169
178
  peekControlflow(simplifications?: readonly CfgSimplificationPassName[], kind?: CfgKind): ControlFlowInformation | undefined;
179
+ callGraph(force?: boolean): Promise<CallGraph>;
180
+ peekCallGraph(): CallGraph | undefined;
170
181
  query<Types extends SupportedQueryTypes = SupportedQueryTypes>(query: Queries<Types>): Promise<QueryResults<Types>>;
171
182
  runSearch<Search extends FlowrSearchLike>(search: Search): Promise<GetSearchElements<SearchOutput<Search>>>;
172
183
  /**