@eagleoutice/flowr 2.7.2 → 2.7.4

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 (31) hide show
  1. package/README.md +14 -14
  2. package/abstract-interpretation/data-frame/mappers/arguments.d.ts +5 -0
  3. package/abstract-interpretation/data-frame/mappers/arguments.js +20 -0
  4. package/abstract-interpretation/data-frame/mappers/function-mapper.js +11 -18
  5. package/abstract-interpretation/data-frame/resolve-args.d.ts +1 -1
  6. package/abstract-interpretation/data-frame/resolve-args.js +1 -1
  7. package/abstract-interpretation/data-frame/semantics.js +3 -3
  8. package/dataflow/extractor.js +13 -0
  9. package/package.json +1 -1
  10. package/project/context/flowr-analyzer-context.js +3 -1
  11. package/project/context/flowr-analyzer-dependencies-context.d.ts +3 -2
  12. package/project/context/flowr-analyzer-dependencies-context.js +4 -2
  13. package/project/context/flowr-analyzer-functions-context.d.ts +29 -0
  14. package/project/context/flowr-analyzer-functions-context.js +68 -0
  15. package/project/plugins/file-plugins/files/flowr-namespace-file.d.ts +32 -0
  16. package/project/plugins/file-plugins/files/flowr-namespace-file.js +102 -0
  17. package/project/plugins/file-plugins/flowr-analyzer-namespace-file-plugin.d.ts +22 -0
  18. package/project/plugins/file-plugins/flowr-analyzer-namespace-file-plugin.js +34 -0
  19. package/project/plugins/flowr-analyzer-plugin-defaults.js +2 -0
  20. package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-description-file-plugin.js +5 -1
  21. package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-namespace-file-plugin.d.ts +10 -0
  22. package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-namespace-file-plugin.js +56 -0
  23. package/project/plugins/package-version-plugins/package.d.ts +15 -2
  24. package/project/plugins/package-version-plugins/package.js +33 -5
  25. package/project/plugins/plugin-registry.d.ts +2 -1
  26. package/project/plugins/plugin-registry.js +2 -0
  27. package/queries/catalog/call-context-query/call-context-query-executor.js +2 -2
  28. package/queries/catalog/dependencies-query/dependencies-query-executor.js +2 -1
  29. package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +2 -0
  30. package/queries/catalog/dependencies-query/dependencies-query-format.js +2 -1
  31. 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.7.0, R grammar v14 (tree-sitter engine)
27
+ flowR repl using flowR v2.7.3, R grammar v14 (tree-sitter engine)
28
28
  R> :query @linter "read.csv(\"/root/x.txt\")"
29
29
  ```
30
30
 
@@ -47,7 +47,7 @@ It offers a wide variety of features, for example:
47
47
  ╰ Path `/root/x.txt` at 1.1-23
48
48
  ╰ _Metadata_: <code>{"totalConsidered":1,"totalUnknown":0,"searchTimeMs":0,"processTimeMs":0}</code>
49
49
  ╰ **Unused Definitions** (unused-definitions):
50
- ╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":0}</code>
50
+ ╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":1}</code>
51
51
  ╰ **Naming Convention** (naming-convention):
52
52
  ╰ _Metadata_: <code>{"numMatches":0,"numBreak":0,"searchTimeMs":0,"processTimeMs":0}</code>
53
53
  ╰ **Network Functions** (network-functions):
@@ -55,7 +55,7 @@ It offers a wide variety of features, for example:
55
55
  ╰ **Dataframe Access Validation** (dataframe-access-validation):
56
56
  ╰ _Metadata_: <code>{"numOperations":0,"numAccesses":0,"totalAccessed":0,"searchTimeMs":0,"processTimeMs":0}</code>
57
57
  ╰ **Dead Code** (dead-code):
58
- ╰ _Metadata_: <code>{"consideredNodes":5,"searchTimeMs":1,"processTimeMs":0}</code>
58
+ ╰ _Metadata_: <code>{"consideredNodes":5,"searchTimeMs":0,"processTimeMs":0}</code>
59
59
  ╰ **Useless Loops** (useless-loop):
60
60
  ╰ _Metadata_: <code>{"numOfUselessLoops":0,"searchTimeMs":0,"processTimeMs":0}</code>
61
61
  All queries together required ≈2 ms (1ms accuracy, total 3 ms)
@@ -88,11 +88,11 @@ It offers a wide variety of features, for example:
88
88
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
89
89
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"totalReads":1,"totalUnknown":0,"totalWritesBeforeAlways":0,"totalValid":0,"searchTimeMs":1,"processTimeMs":0}</code>\
90
90
  &nbsp;&nbsp;&nbsp;╰ **Seeded Randomness** (seeded-randomness):\
91
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"consumerCalls":0,"callsWithFunctionProducers":0,"callsWithAssignmentProducers":0,"callsWithNonConstantProducers":0,"callsWithOtherBranchProducers":0,"searchTimeMs":0,"processTimeMs":1}</code>\
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):\
@@ -105,11 +105,11 @@ It offers a wide variety of features, for example:
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 ≈3 ms (1ms accuracy, total 3 ms)_
108
+ _All queries together required ≈3 ms (1ms accuracy, total 4 ms)_
109
109
 
110
110
  <details> <summary style="color:gray">Show Detailed Results as Json</summary>
111
111
 
112
- The analysis required _3.0 ms_ (including parsing and normalization and the query) within the generation environment.
112
+ The analysis required _3.5 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.
@@ -161,7 +161,7 @@ It offers a wide variety of features, for example:
161
161
  "callsWithNonConstantProducers": 0,
162
162
  "callsWithOtherBranchProducers": 0,
163
163
  "searchTimeMs": 0,
164
- "processTimeMs": 1
164
+ "processTimeMs": 0
165
165
  }
166
166
  },
167
167
  "absolute-file-paths": {
@@ -180,7 +180,7 @@ It offers a wide variety of features, for example:
180
180
  ".meta": {
181
181
  "totalConsidered": 1,
182
182
  "totalUnknown": 0,
183
- "searchTimeMs": 0,
183
+ "searchTimeMs": 1,
184
184
  "processTimeMs": 0
185
185
  }
186
186
  },
@@ -307,7 +307,7 @@ It offers a wide variety of features, for example:
307
307
 
308
308
  ```shell
309
309
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
310
- flowR repl using flowR v2.7.0, R grammar v14 (tree-sitter engine)
310
+ flowR repl using flowR v2.7.3, R grammar v14 (tree-sitter engine)
311
311
  R> :query @static-slice (11@sum) file://test/testfiles/example.R
312
312
  ```
313
313
 
@@ -321,7 +321,7 @@ It offers a wide variety of features, for example:
321
321
  N <- 10
322
322
  for(i in 1:(N-1)) sum <- sum + i + w
323
323
  sum
324
- All queries together required ≈3 ms (1ms accuracy, total 3 ms)
324
+ All queries together required ≈2 ms (1ms accuracy, total 2 ms)
325
325
  ```
326
326
 
327
327
 
@@ -355,7 +355,7 @@ It offers a wide variety of features, for example:
355
355
 
356
356
 
357
357
  * 🚀 **fast data- and control-flow graphs**\
358
- Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">119.6 ms</span></i> (as of Dec 3, 2025),
358
+ Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">120.3 ms</span></i> (as of Dec 21, 2025),
359
359
  _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,
360
360
  and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/Dataflow-Graph) for more details on the dataflow graph.
361
361
 
@@ -391,7 +391,7 @@ It offers a wide variety of features, for example:
391
391
 
392
392
  ```shell
393
393
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
394
- flowR repl using flowR v2.7.0, R grammar v14 (tree-sitter engine)
394
+ flowR repl using flowR v2.7.3, R grammar v14 (tree-sitter engine)
395
395
  R> :dataflow* test/testfiles/example.R
396
396
  ```
397
397
 
@@ -696,7 +696,7 @@ It offers a wide variety of features, for example:
696
696
  ```
697
697
 
698
698
 
699
- (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.)
699
+ (The analysis required _2.9 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
700
700
 
701
701
 
702
702
 
@@ -6,6 +6,7 @@ import { type RFunctionArgument, type RFunctionCall, EmptyArgument } from '../..
6
6
  import type { RSymbol } from '../../../r-bridge/lang-4.x/ast/model/nodes/r-symbol';
7
7
  import type { ParentInformation } from '../../../r-bridge/lang-4.x/ast/model/processing/decorate';
8
8
  import { RNull } from '../../../r-bridge/lang-4.x/convert-values';
9
+ import type { RParseRequest } from '../../../r-bridge/retriever';
9
10
  import type { DataFrameShapeInferenceVisitor } from '../shape-inference';
10
11
  /**
11
12
  * The location of a function parameter for mapping function call arguments to function parameters.
@@ -107,3 +108,7 @@ export declare function isRNull(node: RFunctionArgument<ParentInformation> | und
107
108
  * Checks whether a string is a valid columns name according to the flag `check.names` in R.
108
109
  */
109
110
  export declare function isValidColName(colname: string | undefined): boolean;
111
+ /**
112
+ * Parses a text of file parse request using the provided parser function.
113
+ */
114
+ export declare function parseRequestContent(request: RParseRequest, parser: (line: Buffer | string, lineNumber: number) => void, maxLines?: number): boolean;
@@ -12,15 +12,20 @@ exports.isDataFrameArgument = isDataFrameArgument;
12
12
  exports.isNamedArgument = isNamedArgument;
13
13
  exports.isRNull = isRNull;
14
14
  exports.isValidColName = isValidColName;
15
+ exports.parseRequestContent = parseRequestContent;
15
16
  const vertex_1 = require("../../../dataflow/graph/vertex");
16
17
  const make_argument_1 = require("../../../dataflow/internal/process/functions/call/argument/make-argument");
17
18
  const r_function_call_1 = require("../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
18
19
  const visitor_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/visitor");
19
20
  const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type");
20
21
  const convert_values_1 = require("../../../r-bridge/lang-4.x/convert-values");
22
+ const assert_1 = require("../../../util/assert");
23
+ const files_1 = require("../../../util/files");
21
24
  const resolve_args_1 = require("../resolve-args");
22
25
  /** Regular expression representing valid columns names, e.g. for `data.frame` */
23
26
  const ColNamesRegex = /^[A-Za-z.][A-Za-z0-9_.]*$/;
27
+ /** Regular expression representing line terminations (LF, CRLF, CR) */
28
+ const LineTerminationRegex = /\r\n|\r|\n/;
24
29
  /**
25
30
  * Escapes a regular expression given as string by escaping all special regular expression characters.
26
31
  * @param text - The text to escape
@@ -181,4 +186,19 @@ function isRNull(node) {
181
186
  function isValidColName(colname) {
182
187
  return colname !== undefined && ColNamesRegex.test(colname);
183
188
  }
189
+ /**
190
+ * Parses a text of file parse request using the provided parser function.
191
+ */
192
+ function parseRequestContent(request, parser, maxLines) {
193
+ const requestType = request.request;
194
+ switch (requestType) {
195
+ case 'text':
196
+ (0, resolve_args_1.unescapeSpecialChars)(request.content).split(LineTerminationRegex).forEach(parser);
197
+ return true;
198
+ case 'file':
199
+ return (0, files_1.readLineByLineSync)(request.content, parser, maxLines);
200
+ default:
201
+ (0, assert_1.assertUnreachable)(requestType);
202
+ }
203
+ }
184
204
  //# sourceMappingURL=arguments.js.map
@@ -8,7 +8,6 @@ const r_function_call_1 = require("../../../r-bridge/lang-4.x/ast/model/nodes/r-
8
8
  const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type");
9
9
  const retriever_1 = require("../../../r-bridge/retriever");
10
10
  const assert_1 = require("../../../util/assert");
11
- const files_1 = require("../../../util/files");
12
11
  const dataframe_domain_1 = require("../dataframe-domain");
13
12
  const resolve_args_1 = require("../resolve-args");
14
13
  const arguments_1 = require("./arguments");
@@ -638,7 +637,7 @@ function mapDataFrameRead(args, params, inference, info) {
638
637
  }
639
638
  }
640
639
  };
641
- const allLines = parseRequestContent(request, parseLine, info.ctx.config.abstractInterpretation.dataFrame.readLoadedData.maxReadLines);
640
+ const allLines = (0, arguments_1.parseRequestContent)(request, parseLine, info.ctx.config.abstractInterpretation.dataFrame.readLoadedData.maxReadLines);
642
641
  let colnames;
643
642
  if (header) {
644
643
  colnames = (0, arguments_1.filterValidNames)(firstLine, checkNames, noDupNames, params.noEmptyNames);
@@ -1105,6 +1104,7 @@ function getRequestFromRead(fileNameArg, textArg, params, info) {
1105
1104
  if (fileNameArg !== undefined && fileNameArg !== r_function_call_1.EmptyArgument) {
1106
1105
  const fileName = (0, resolve_args_1.resolveIdToArgValue)(fileNameArg, info);
1107
1106
  if (typeof fileName === 'string') {
1107
+ const text = (0, resolve_args_1.unescapeSpecialChars)(fileName);
1108
1108
  source = fileName;
1109
1109
  const referenceChain = fileNameArg.info.file ? [fileNameArg.info.file] : [];
1110
1110
  const sources = (0, built_in_source_1.findSource)(info.ctx.config.solver.resolveSource, fileName, { referenceChain, ctx: info.ctx });
@@ -1113,9 +1113,9 @@ function getRequestFromRead(fileNameArg, textArg, params, info) {
1113
1113
  // create request from resolved source file path
1114
1114
  request = { request: 'file', content: sources[0] };
1115
1115
  }
1116
- else if (params.text === undefined && (0, resolve_args_1.unescapeSpecialChars)(fileName).includes('\n')) {
1116
+ else if (params.text === undefined && text.includes('\n')) {
1117
1117
  // create request from string if file name argument contains newline
1118
- request = (0, retriever_1.requestFromInput)((0, resolve_args_1.unescapeSpecialChars)(fileName));
1118
+ request = (0, retriever_1.requestFromInput)(text);
1119
1119
  }
1120
1120
  }
1121
1121
  }
@@ -1129,25 +1129,13 @@ function getRequestFromRead(fileNameArg, textArg, params, info) {
1129
1129
  request = request ? info.ctx.files.resolveRequest(request).r : undefined;
1130
1130
  return { source, request };
1131
1131
  }
1132
- function parseRequestContent(request, parser, maxLines) {
1133
- const requestType = request.request;
1134
- switch (requestType) {
1135
- case 'text':
1136
- request.content.split('\n').forEach(parser);
1137
- return true;
1138
- case 'file':
1139
- return (0, files_1.readLineByLineSync)(request.content, parser, maxLines);
1140
- default:
1141
- (0, assert_1.assertUnreachable)(requestType);
1142
- }
1143
- }
1144
1132
  /**
1145
1133
  * Gets all entries from a line of a CSV file using a custom separator char, quote char, and comment char
1146
1134
  */
1147
1135
  function getEntriesFromCsvLine(line, sep = ',', quote = '"', comment = '', trim = true) {
1148
1136
  sep = (0, arguments_1.escapeRegExp)(sep, true); // only allow tokens like `\s`, `\t`, or `\n` in separator, quote, and comment chars
1149
- quote = (0, arguments_1.escapeRegExp)(quote, true);
1150
- comment = (0, arguments_1.escapeRegExp)(comment, true);
1137
+ quote = (0, arguments_1.escapeRegExp)(quote);
1138
+ comment = (0, arguments_1.escapeRegExp)(comment);
1151
1139
  const quantifier = sep === '\\s' ? '+' : '*'; // do not allow unquoted empty entries in whitespace-sparated files
1152
1140
  const LineCommentRegex = new RegExp(`[${comment}].*`);
1153
1141
  const CsvEntryRegex = new RegExp(`(?<=^|[${sep}])(?:[${quote}]((?:[^${quote}]|[${quote}]{2})*)[${quote}]|([^${sep}]${quantifier}))`, 'g');
@@ -1172,6 +1160,11 @@ function getSelectedColumns(args, info) {
1172
1160
  selectedCols = joinColumns(selectedCols, result.selectedCols);
1173
1161
  unselectedCols = joinColumns(unselectedCols, result.unselectedCols);
1174
1162
  }
1163
+ else if (arg.value?.type === type_1.RType.UnaryOp && arg.value.operator === '+' && info.idMap !== undefined) {
1164
+ const result = getSelectedColumns([(0, make_argument_1.toUnnamedArgument)(arg.value.operand, info.idMap)], info);
1165
+ selectedCols = joinColumns(selectedCols, result.selectedCols);
1166
+ unselectedCols = joinColumns(unselectedCols, result.unselectedCols);
1167
+ }
1175
1168
  else if (arg.value?.type === type_1.RType.UnaryOp && arg.value.operator === '-' && info.idMap !== undefined) {
1176
1169
  const result = getSelectedColumns([(0, make_argument_1.toUnnamedArgument)(arg.value.operand, info.idMap)], info);
1177
1170
  selectedCols = joinColumns(selectedCols, result.unselectedCols);
@@ -35,7 +35,7 @@ export declare function unescapeQuotes(argument: undefined): undefined;
35
35
  export declare function unescapeQuotes(argument: string): string;
36
36
  export declare function unescapeQuotes(argument: string | undefined): string | undefined;
37
37
  /**
38
- * Unescapes escape sequences like `\n`, `\t`, `\'`, `\"`, `\\` back into actual newlines, tabs, quotes, and backslashes
38
+ * Unescapes escape sequences like `\r`, `\n`, `\t`, `\'`, `\"`, `\\` back into actual newlines, tabs, quotes, and backslashes
39
39
  */
40
40
  export declare function unescapeSpecialChars(argument: undefined): undefined;
41
41
  export declare function unescapeSpecialChars(argument: string): string;
@@ -113,6 +113,6 @@ function unescapeSpecialChars(argument) {
113
113
  if (argument === undefined) {
114
114
  return undefined;
115
115
  }
116
- return unescapeQuotes(argument).replaceAll('\\n', '\n').replaceAll('\\t', '\t').replaceAll('\\\\', '\\');
116
+ return unescapeQuotes(argument).replaceAll('\\r', '\r').replaceAll('\\n', '\n').replaceAll('\\t', '\t').replaceAll('\\\\', '\\');
117
117
  }
118
118
  //# sourceMappingURL=resolve-args.js.map
@@ -346,13 +346,13 @@ function applyJoinSemantics(value, { other, by }, options) {
346
346
  let rows;
347
347
  switch (joinType) {
348
348
  case 'inner':
349
- rows = value.rows.min(other.rows).widenDown();
349
+ rows = value.rows.max(other.rows).widenDown();
350
350
  break;
351
351
  case 'left':
352
- rows = value.rows;
352
+ rows = value.rows.max(other.rows.isValue() ? [0, other.rows.value[1]] : lattice_1.Bottom);
353
353
  break;
354
354
  case 'right':
355
- rows = other.rows;
355
+ rows = other.rows.max(value.rows.isValue() ? [0, value.rows.value[1]] : lattice_1.Bottom);
356
356
  break;
357
357
  case 'full':
358
358
  rows = mergeInterval(value.rows, other.rows);
@@ -60,6 +60,8 @@ exports.processors = {
60
60
  function resolveLinkToSideEffects(ast, graph) {
61
61
  let cf = undefined;
62
62
  let knownCalls;
63
+ let allCallNames = [];
64
+ const killedRegexes = new Set();
63
65
  const handled = new Set();
64
66
  for (const s of graph.unknownSideEffects) {
65
67
  if (typeof s !== 'object') {
@@ -69,12 +71,23 @@ function resolveLinkToSideEffects(ast, graph) {
69
71
  cf = (0, extract_cfg_1.extractCfgQuick)(ast);
70
72
  if (graph.unknownSideEffects.size > 20) {
71
73
  knownCalls = (0, extract_cfg_1.getCallsInCfg)(cf, graph);
74
+ allCallNames = Array.from(new Set(knownCalls.values().map(c => c.name)));
72
75
  }
73
76
  }
74
77
  else if (handled.has(s.id)) {
75
78
  continue;
76
79
  }
77
80
  handled.add(s.id);
81
+ const regexKey = s.linkTo.callName.source + '//' + s.linkTo.callName.flags;
82
+ if (killedRegexes.has(regexKey)) {
83
+ // we already know we will not find it!
84
+ continue;
85
+ }
86
+ else if (allCallNames.length > 0 && !allCallNames.some(name => s.linkTo.callName.test(name))) {
87
+ // we know no call matches the regex
88
+ killedRegexes.add(regexKey);
89
+ continue;
90
+ }
78
91
  /* this has to change whenever we add a new link to relations because we currently offer no abstraction for the type */
79
92
  const potentials = (0, identify_link_to_last_call_relation_1.identifyLinkToLastCallRelation)(s.id, cf.graph, graph, s.linkTo, knownCalls);
80
93
  for (const pot of potentials) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.7.2",
3
+ "version": "2.7.4",
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": {
@@ -7,6 +7,7 @@ const flowr_analyzer_files_context_1 = require("./flowr-analyzer-files-context")
7
7
  const flowr_analyzer_dependencies_context_1 = require("./flowr-analyzer-dependencies-context");
8
8
  const flowr_analyzer_plugin_1 = require("../plugins/flowr-analyzer-plugin");
9
9
  const flowr_analyzer_loading_order_context_1 = require("./flowr-analyzer-loading-order-context");
10
+ const flowr_analyzer_functions_context_1 = require("./flowr-analyzer-functions-context");
10
11
  const arrays_1 = require("../../util/collections/arrays");
11
12
  const retriever_1 = require("../../r-bridge/retriever");
12
13
  const config_1 = require("../../config");
@@ -33,8 +34,9 @@ class FlowrAnalyzerContext {
33
34
  this.config = config;
34
35
  const loadingOrder = new flowr_analyzer_loading_order_context_1.FlowrAnalyzerLoadingOrderContext(this, plugins.get(flowr_analyzer_plugin_1.PluginType.LoadingOrder));
35
36
  this.files = new flowr_analyzer_files_context_1.FlowrAnalyzerFilesContext(loadingOrder, (plugins.get(flowr_analyzer_plugin_1.PluginType.ProjectDiscovery) ?? []), (plugins.get(flowr_analyzer_plugin_1.PluginType.FileLoad) ?? []));
36
- this.deps = new flowr_analyzer_dependencies_context_1.FlowrAnalyzerDependenciesContext(this, (plugins.get(flowr_analyzer_plugin_1.PluginType.DependencyIdentification) ?? []));
37
37
  this.env = new flowr_analyzer_environment_context_1.FlowrAnalyzerEnvironmentContext(this);
38
+ const functions = new flowr_analyzer_functions_context_1.FlowrAnalyzerFunctionsContext(this);
39
+ this.deps = new flowr_analyzer_dependencies_context_1.FlowrAnalyzerDependenciesContext(functions, (plugins.get(flowr_analyzer_plugin_1.PluginType.DependencyIdentification) ?? []));
38
40
  }
39
41
  /** delegate request addition */
40
42
  addRequests(requests) {
@@ -1,7 +1,7 @@
1
1
  import { AbstractFlowrAnalyzerContext } from './abstract-flowr-analyzer-context';
2
2
  import { FlowrAnalyzerPackageVersionsPlugin } from '../plugins/package-version-plugins/flowr-analyzer-package-versions-plugin';
3
3
  import type { Package } from '../plugins/package-version-plugins/package';
4
- import type { FlowrAnalyzerContext } from './flowr-analyzer-context';
4
+ import type { FlowrAnalyzerFunctionsContext } from './flowr-analyzer-functions-context';
5
5
  /**
6
6
  * This is a read-only interface to the {@link FlowrAnalyzerDependenciesContext}.
7
7
  * It prevents you from modifying the dependencies, but allows you to inspect them (which is probably what you want when using the {@link FlowrAnalyzer}).
@@ -28,10 +28,11 @@ export interface ReadOnlyFlowrAnalyzerDependenciesContext {
28
28
  */
29
29
  export declare class FlowrAnalyzerDependenciesContext extends AbstractFlowrAnalyzerContext<undefined, void, FlowrAnalyzerPackageVersionsPlugin> implements ReadOnlyFlowrAnalyzerDependenciesContext {
30
30
  readonly name = "flowr-analyzer-dependencies-context";
31
+ readonly functionsContext: FlowrAnalyzerFunctionsContext;
31
32
  private dependencies;
32
33
  private staticsLoaded;
33
34
  reset(): void;
34
- constructor(ctx: FlowrAnalyzerContext, plugins?: readonly FlowrAnalyzerPackageVersionsPlugin[]);
35
+ constructor(functionsContext: FlowrAnalyzerFunctionsContext, plugins?: readonly FlowrAnalyzerPackageVersionsPlugin[]);
35
36
  resolveStaticDependencies(): void;
36
37
  addDependency(pkg: Package): void;
37
38
  getDependency(name: string): Package | undefined;
@@ -10,14 +10,16 @@ const flowr_analyzer_package_versions_plugin_1 = require("../plugins/package-ver
10
10
  */
11
11
  class FlowrAnalyzerDependenciesContext extends abstract_flowr_analyzer_context_1.AbstractFlowrAnalyzerContext {
12
12
  name = 'flowr-analyzer-dependencies-context';
13
+ functionsContext;
13
14
  dependencies = new Map();
14
15
  staticsLoaded = false;
15
16
  reset() {
16
17
  this.dependencies = new Map();
17
18
  this.staticsLoaded = false;
18
19
  }
19
- constructor(ctx, plugins) {
20
- super(ctx, flowr_analyzer_package_versions_plugin_1.FlowrAnalyzerPackageVersionsPlugin.defaultPlugin(), plugins);
20
+ constructor(functionsContext, plugins) {
21
+ super(functionsContext.getAttachedContext(), flowr_analyzer_package_versions_plugin_1.FlowrAnalyzerPackageVersionsPlugin.defaultPlugin(), plugins);
22
+ this.functionsContext = functionsContext;
21
23
  }
22
24
  resolveStaticDependencies() {
23
25
  this.applyPlugins(undefined);
@@ -0,0 +1,29 @@
1
+ import { AbstractFlowrAnalyzerContext } from './abstract-flowr-analyzer-context';
2
+ import type { FlowrAnalyzerContext } from './flowr-analyzer-context';
3
+ import { FlowrAnalyzerPackageVersionsPlugin } from '../plugins/package-version-plugins/flowr-analyzer-package-versions-plugin';
4
+ export declare enum FunctionTypes {
5
+ Function = "function",
6
+ ExportTypes = "exportTypes",
7
+ S3 = "S3"
8
+ }
9
+ export interface FunctionInfo {
10
+ name: string;
11
+ packageOrigin: string;
12
+ isExported: boolean;
13
+ isS3Generic: boolean;
14
+ s3TypeDispatch?: string;
15
+ inferredType?: string;
16
+ }
17
+ export interface ReadOnlyFlowrAnalyzerFunctionsContext {
18
+ readonly name: string;
19
+ getFunctionInfo(name: string, className?: string): FunctionInfo | FunctionInfo[] | undefined;
20
+ }
21
+ export declare class FlowrAnalyzerFunctionsContext extends AbstractFlowrAnalyzerContext<undefined, void, FlowrAnalyzerPackageVersionsPlugin> implements ReadOnlyFlowrAnalyzerFunctionsContext {
22
+ readonly name = "flowr-analyzer-functions-context";
23
+ private functionInfo;
24
+ constructor(ctx: FlowrAnalyzerContext, plugins?: readonly FlowrAnalyzerPackageVersionsPlugin[]);
25
+ addFunctionInfo(info: FunctionInfo): void;
26
+ private mergeFunctionInfo;
27
+ getFunctionInfo(pkg: string, name: string, s3TypeDispatch?: string): FunctionInfo | FunctionInfo[] | undefined;
28
+ reset(): void;
29
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlowrAnalyzerFunctionsContext = exports.FunctionTypes = void 0;
4
+ const abstract_flowr_analyzer_context_1 = require("./abstract-flowr-analyzer-context");
5
+ const flowr_analyzer_package_versions_plugin_1 = require("../plugins/package-version-plugins/flowr-analyzer-package-versions-plugin");
6
+ const flowr_analyzer_package_versions_namespace_file_plugin_1 = require("../plugins/package-version-plugins/flowr-analyzer-package-versions-namespace-file-plugin");
7
+ var FunctionTypes;
8
+ (function (FunctionTypes) {
9
+ FunctionTypes["Function"] = "function";
10
+ FunctionTypes["ExportTypes"] = "exportTypes";
11
+ FunctionTypes["S3"] = "S3";
12
+ })(FunctionTypes || (exports.FunctionTypes = FunctionTypes = {}));
13
+ class FlowrAnalyzerFunctionsContext extends abstract_flowr_analyzer_context_1.AbstractFlowrAnalyzerContext {
14
+ name = 'flowr-analyzer-functions-context';
15
+ functionInfo = new Map();
16
+ constructor(ctx, plugins) {
17
+ super(ctx, flowr_analyzer_package_versions_plugin_1.FlowrAnalyzerPackageVersionsPlugin.defaultPlugin(), plugins);
18
+ }
19
+ addFunctionInfo(info) {
20
+ const list = this.functionInfo.get(info.name);
21
+ if (!list) {
22
+ this.functionInfo.set(info.name, [info]);
23
+ return;
24
+ }
25
+ const other = list.find(e => e.name === info.name &&
26
+ e.packageOrigin === info.packageOrigin &&
27
+ e.s3TypeDispatch === info.s3TypeDispatch);
28
+ if (other) {
29
+ flowr_analyzer_package_versions_namespace_file_plugin_1.namespaceFileLog.warn('Namespace information is being merged!');
30
+ this.mergeFunctionInfo(other, info);
31
+ }
32
+ else {
33
+ list.push(info);
34
+ }
35
+ }
36
+ mergeFunctionInfo(functionInfo, other) {
37
+ if (functionInfo.name !== other.name || functionInfo.packageOrigin !== other.packageOrigin) {
38
+ throw new Error(`Cannot merge FunctionInfo for ${functionInfo.name} and ${other.name}`);
39
+ }
40
+ if (functionInfo.s3TypeDispatch !== other.s3TypeDispatch) {
41
+ throw new Error(`Cannot merge FunctionInfo with different S3 dispatch for ${functionInfo.name}`);
42
+ }
43
+ if (!functionInfo.inferredType && other.inferredType) {
44
+ functionInfo.inferredType = other.inferredType;
45
+ }
46
+ functionInfo.isExported ||= other.isExported;
47
+ functionInfo.isS3Generic ||= other.isS3Generic;
48
+ }
49
+ getFunctionInfo(pkg, name, s3TypeDispatch) {
50
+ if (s3TypeDispatch) {
51
+ return this.functionInfo.get(`${name}`)?.find(e => e.packageOrigin === pkg && e.s3TypeDispatch === s3TypeDispatch);
52
+ }
53
+ else if (name.includes('.')) {
54
+ const parts = name.split('.');
55
+ s3TypeDispatch = parts.pop();
56
+ const splitName = parts.join('.');
57
+ if (this.functionInfo.has(splitName)) {
58
+ return this.functionInfo.get(splitName)?.find(e => e.packageOrigin === pkg && e.s3TypeDispatch === s3TypeDispatch);
59
+ }
60
+ }
61
+ return this.functionInfo.get(name)?.filter(e => e.packageOrigin === pkg);
62
+ }
63
+ reset() {
64
+ this.functionInfo = new Map();
65
+ }
66
+ }
67
+ exports.FlowrAnalyzerFunctionsContext = FlowrAnalyzerFunctionsContext;
68
+ //# sourceMappingURL=flowr-analyzer-functions-context.js.map
@@ -0,0 +1,32 @@
1
+ import type { FileRole, FlowrFileProvider } from '../../../context/flowr-file';
2
+ import { FlowrFile } from '../../../context/flowr-file';
3
+ export interface NamespaceInfo {
4
+ exportedSymbols: string[];
5
+ exportedFunctions: string[];
6
+ exportS3Generics: Map<string, string[]>;
7
+ loadsWithSideEffects: boolean;
8
+ }
9
+ export interface NamespaceFormat {
10
+ current: NamespaceInfo;
11
+ [packageName: string]: NamespaceInfo;
12
+ }
13
+ /**
14
+ * This decorates a text file and provides access to its content in the {@link NamespaceFormat}.
15
+ */
16
+ export declare class FlowrNamespaceFile extends FlowrFile<NamespaceFormat> {
17
+ private readonly wrapped;
18
+ /**
19
+ * Prefer the static {@link FlowrNamespaceFile.from} method to create instances of this class as it will not re-create if already a namespace file
20
+ * and handle role assignments.
21
+ */
22
+ constructor(file: FlowrFileProvider);
23
+ /**
24
+ * Loads and parses the content of the wrapped file in the {@link NamespaceFormat}.
25
+ * @see {@link parseNamespace} for details on the parsing logic.
26
+ */
27
+ protected loadContent(): NamespaceFormat;
28
+ /**
29
+ * Namespace file lifter, this does not re-create if already a namespace file
30
+ */
31
+ static from(file: FlowrFileProvider | FlowrNamespaceFile, role?: FileRole): FlowrNamespaceFile;
32
+ }
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlowrNamespaceFile = void 0;
4
+ const flowr_file_1 = require("../../../context/flowr-file");
5
+ /**
6
+ * This decorates a text file and provides access to its content in the {@link NamespaceFormat}.
7
+ */
8
+ class FlowrNamespaceFile extends flowr_file_1.FlowrFile {
9
+ wrapped;
10
+ /**
11
+ * Prefer the static {@link FlowrNamespaceFile.from} method to create instances of this class as it will not re-create if already a namespace file
12
+ * and handle role assignments.
13
+ */
14
+ constructor(file) {
15
+ super(file.path(), file.role);
16
+ this.wrapped = file;
17
+ }
18
+ /**
19
+ * Loads and parses the content of the wrapped file in the {@link NamespaceFormat}.
20
+ * @see {@link parseNamespace} for details on the parsing logic.
21
+ */
22
+ loadContent() {
23
+ return parseNamespace(this.wrapped);
24
+ }
25
+ /**
26
+ * Namespace file lifter, this does not re-create if already a namespace file
27
+ */
28
+ static from(file, role) {
29
+ if (role) {
30
+ file.assignRole(role);
31
+ }
32
+ return file instanceof FlowrNamespaceFile ? file : new FlowrNamespaceFile(file);
33
+ }
34
+ }
35
+ exports.FlowrNamespaceFile = FlowrNamespaceFile;
36
+ const cleanLineCommentRegex = /^#.*$/gm;
37
+ /**
38
+ * Parses the given NAMESPACE file
39
+ */
40
+ function parseNamespace(file) {
41
+ const result = {
42
+ current: {
43
+ exportedSymbols: [],
44
+ exportedFunctions: [],
45
+ exportS3Generics: new Map(),
46
+ loadsWithSideEffects: false,
47
+ },
48
+ };
49
+ const fileContent = file.content().toString().replaceAll(cleanLineCommentRegex, '').trim()
50
+ .split(/\r?\n/).filter(Boolean);
51
+ for (const line of fileContent) {
52
+ const match = line.trim().match(/^(\w+)\(([^)]*)\)$/);
53
+ if (!match) {
54
+ continue;
55
+ }
56
+ const [, type, args] = match;
57
+ switch (type) {
58
+ case 'exportClasses':
59
+ case 'exportMethods':
60
+ result.current.exportedFunctions.push(args);
61
+ break;
62
+ case 'S3method':
63
+ {
64
+ const parts = args.split(',').map(s => s.trim());
65
+ if (parts.length !== 2) {
66
+ continue;
67
+ }
68
+ const [pkg, func] = parts;
69
+ let arr = result.current.exportS3Generics.get(pkg);
70
+ if (!arr) {
71
+ arr = [];
72
+ result.current.exportS3Generics.set(pkg, arr);
73
+ }
74
+ arr.push(func);
75
+ break;
76
+ }
77
+ case 'export':
78
+ result.current.exportedSymbols.push(args);
79
+ break;
80
+ case 'useDynLib':
81
+ {
82
+ const parts = args.split(',').map(s => s.trim());
83
+ if (parts.length !== 2) {
84
+ continue;
85
+ }
86
+ const [pkg] = parts;
87
+ if (!result[pkg]) {
88
+ result[pkg] = {
89
+ exportedSymbols: [],
90
+ exportedFunctions: [],
91
+ exportS3Generics: new Map(),
92
+ loadsWithSideEffects: false,
93
+ };
94
+ }
95
+ result[pkg].loadsWithSideEffects = true;
96
+ break;
97
+ }
98
+ }
99
+ }
100
+ return result;
101
+ }
102
+ //# sourceMappingURL=flowr-namespace-file.js.map
@@ -0,0 +1,22 @@
1
+ import { FlowrAnalyzerFilePlugin } from './flowr-analyzer-file-plugin';
2
+ import { SemVer } from 'semver';
3
+ import type { PathLike } from 'fs';
4
+ import type { FlowrAnalyzerContext } from '../../context/flowr-analyzer-context';
5
+ import type { FlowrFileProvider } from '../../context/flowr-file';
6
+ import { FlowrNamespaceFile } from './files/flowr-namespace-file';
7
+ /**
8
+ * This plugin provides support for R `NAMESPACE` files.
9
+ */
10
+ export declare class FlowrAnalyzerNamespaceFilePlugin extends FlowrAnalyzerFilePlugin {
11
+ readonly name = "flowr-analyzer-namespace-file-plugin";
12
+ readonly description = "This plugin provides support for NAMESPACE files and extracts their content into the NAMESPACEFormat.";
13
+ readonly version: SemVer;
14
+ private readonly pattern;
15
+ /**
16
+ * Creates a new instance of the NAMESPACE file plugin.
17
+ * @param filePattern - The pattern to identify NAMESPACE files, see {@link NamespaceFilePattern} for the default pattern.
18
+ */
19
+ constructor(filePattern?: RegExp);
20
+ applies(file: PathLike): boolean;
21
+ process(_ctx: FlowrAnalyzerContext, file: FlowrFileProvider): FlowrNamespaceFile;
22
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlowrAnalyzerNamespaceFilePlugin = void 0;
4
+ const flowr_analyzer_file_plugin_1 = require("./flowr-analyzer-file-plugin");
5
+ const semver_1 = require("semver");
6
+ const flowr_file_1 = require("../../context/flowr-file");
7
+ const flowr_namespace_file_1 = require("./files/flowr-namespace-file");
8
+ const built_in_source_1 = require("../../../dataflow/internal/process/functions/call/built-in/built-in-source");
9
+ const NamespaceFilePattern = /^NAMESPACE(\.txt)?$/i;
10
+ /**
11
+ * This plugin provides support for R `NAMESPACE` files.
12
+ */
13
+ class FlowrAnalyzerNamespaceFilePlugin extends flowr_analyzer_file_plugin_1.FlowrAnalyzerFilePlugin {
14
+ name = 'flowr-analyzer-namespace-file-plugin';
15
+ description = 'This plugin provides support for NAMESPACE files and extracts their content into the NAMESPACEFormat.';
16
+ version = new semver_1.SemVer('0.1.0');
17
+ pattern;
18
+ /**
19
+ * Creates a new instance of the NAMESPACE file plugin.
20
+ * @param filePattern - The pattern to identify NAMESPACE files, see {@link NamespaceFilePattern} for the default pattern.
21
+ */
22
+ constructor(filePattern = NamespaceFilePattern) {
23
+ super();
24
+ this.pattern = filePattern;
25
+ }
26
+ applies(file) {
27
+ return this.pattern.test((0, built_in_source_1.platformBasename)(file.toString()));
28
+ }
29
+ process(_ctx, file) {
30
+ return flowr_namespace_file_1.FlowrNamespaceFile.from(file, flowr_file_1.FileRole.Namespace);
31
+ }
32
+ }
33
+ exports.FlowrAnalyzerNamespaceFilePlugin = FlowrAnalyzerNamespaceFilePlugin;
34
+ //# sourceMappingURL=flowr-analyzer-namespace-file-plugin.js.map
@@ -7,6 +7,7 @@ const flowr_analyzer_loading_order_description_file_plugin_1 = require("./loadin
7
7
  const flowr_analyzer_rmd_file_plugin_1 = require("./file-plugins/notebooks/flowr-analyzer-rmd-file-plugin");
8
8
  const flowr_analyzer_qmd_file_plugin_1 = require("./file-plugins/notebooks/flowr-analyzer-qmd-file-plugin");
9
9
  const flowr_analyzer_jupyter_file_plugin_1 = require("./file-plugins/notebooks/flowr-analyzer-jupyter-file-plugin");
10
+ const flowr_analyzer_namespace_file_plugin_1 = require("./file-plugins/flowr-analyzer-namespace-file-plugin");
10
11
  const flowr_analyzer_news_file_plugin_1 = require("./file-plugins/flowr-analyzer-news-file-plugin");
11
12
  /**
12
13
  * Provides the default set of Flowr Analyzer plugins.
@@ -19,6 +20,7 @@ function FlowrAnalyzerPluginDefaults() {
19
20
  new flowr_analyzer_rmd_file_plugin_1.FlowrAnalyzerRmdFilePlugin(),
20
21
  new flowr_analyzer_qmd_file_plugin_1.FlowrAnalyzerQmdFilePlugin(),
21
22
  new flowr_analyzer_jupyter_file_plugin_1.FlowrAnalyzerJupyterFilePlugin(),
23
+ new flowr_analyzer_namespace_file_plugin_1.FlowrAnalyzerNamespaceFilePlugin(),
22
24
  new flowr_analyzer_news_file_plugin_1.FlowrAnalyzerNewsFilePlugin()
23
25
  ];
24
26
  }
@@ -32,7 +32,11 @@ class FlowrAnalyzerPackageVersionsDescriptionFilePlugin extends flowr_analyzer_p
32
32
  if (match) {
33
33
  const [, name, operator, version] = match;
34
34
  const range = package_1.Package.parsePackageVersionRange(operator, version);
35
- ctx.deps.addDependency(new package_1.Package(name, type, undefined, range));
35
+ ctx.deps.addDependency(new package_1.Package({
36
+ name: name,
37
+ type: type,
38
+ versionConstraints: range ? [range] : undefined
39
+ }));
36
40
  }
37
41
  }
38
42
  }
@@ -0,0 +1,10 @@
1
+ import { FlowrAnalyzerPackageVersionsPlugin } from './flowr-analyzer-package-versions-plugin';
2
+ import { SemVer } from 'semver';
3
+ import type { FlowrAnalyzerContext } from '../../context/flowr-analyzer-context';
4
+ export declare const namespaceFileLog: import("tslog").Logger<import("tslog").ILogObj>;
5
+ export declare class FlowrAnalyzerPackageVersionsNamespaceFilePlugin extends FlowrAnalyzerPackageVersionsPlugin {
6
+ readonly name = "flowr-analyzer-package-version-namespace-file-plugin";
7
+ readonly description = "This plugin does...";
8
+ readonly version: SemVer;
9
+ process(ctx: FlowrAnalyzerContext): void;
10
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlowrAnalyzerPackageVersionsNamespaceFilePlugin = exports.namespaceFileLog = void 0;
4
+ const flowr_analyzer_package_versions_plugin_1 = require("./flowr-analyzer-package-versions-plugin");
5
+ const semver_1 = require("semver");
6
+ const package_1 = require("./package");
7
+ const flowr_file_1 = require("../../context/flowr-file");
8
+ const log_1 = require("../../../util/log");
9
+ exports.namespaceFileLog = log_1.log.getSubLogger({ name: 'flowr-analyzer-package-versions-namespace-file-plugin' });
10
+ class FlowrAnalyzerPackageVersionsNamespaceFilePlugin extends flowr_analyzer_package_versions_plugin_1.FlowrAnalyzerPackageVersionsPlugin {
11
+ name = 'flowr-analyzer-package-version-namespace-file-plugin';
12
+ description = 'This plugin does...';
13
+ version = new semver_1.SemVer('0.1.0');
14
+ process(ctx) {
15
+ const nmspcFiles = ctx.files.getFilesByRole(flowr_file_1.FileRole.Namespace);
16
+ exports.namespaceFileLog.info(`Found ${nmspcFiles.length} namespace files!`);
17
+ /** this will do the caching etc. for me */
18
+ const deps = nmspcFiles[0].content();
19
+ for (const pkg in deps) {
20
+ const info = deps[pkg];
21
+ ctx.deps.addDependency(new package_1.Package({
22
+ name: pkg,
23
+ namespaceInfo: info
24
+ }));
25
+ for (const exportedSymbol of info.exportedSymbols) {
26
+ ctx.deps.functionsContext.addFunctionInfo({
27
+ name: exportedSymbol,
28
+ packageOrigin: pkg,
29
+ isExported: true,
30
+ isS3Generic: false,
31
+ });
32
+ }
33
+ for (const exportedFunction of info.exportedFunctions) {
34
+ ctx.deps.functionsContext.addFunctionInfo({
35
+ name: exportedFunction,
36
+ packageOrigin: pkg,
37
+ isExported: true,
38
+ isS3Generic: false,
39
+ });
40
+ }
41
+ for (const [genericName, classes] of info.exportS3Generics.entries()) {
42
+ for (const s3TypeDispatch of classes) {
43
+ ctx.deps.functionsContext.addFunctionInfo({
44
+ name: genericName,
45
+ packageOrigin: pkg,
46
+ isExported: true,
47
+ isS3Generic: true,
48
+ s3TypeDispatch: s3TypeDispatch,
49
+ });
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ exports.FlowrAnalyzerPackageVersionsNamespaceFilePlugin = FlowrAnalyzerPackageVersionsNamespaceFilePlugin;
56
+ //# sourceMappingURL=flowr-analyzer-package-versions-namespace-file-plugin.js.map
@@ -1,14 +1,27 @@
1
1
  import { Range } from 'semver';
2
+ import type { NamespaceInfo } from '../file-plugins/files/flowr-namespace-file';
2
3
  export type PackageType = 'package' | 'system' | 'r';
4
+ export type PackageOptions = {
5
+ derivedVersion?: Range;
6
+ type?: PackageType;
7
+ dependencies?: Package[];
8
+ namespaceInfo?: NamespaceInfo;
9
+ versionConstraints?: Range[];
10
+ };
3
11
  export declare class Package {
4
12
  name: string;
5
13
  derivedVersion?: Range;
6
14
  type?: PackageType;
7
15
  dependencies?: Package[];
16
+ namespaceInfo?: NamespaceInfo;
8
17
  versionConstraints: Range[];
9
- constructor(name: string, type?: PackageType, dependencies?: Package[], ...versionConstraints: readonly (Range | undefined)[]);
18
+ constructor(info: {
19
+ name: string;
20
+ } & PackageOptions);
21
+ has(name: string, className?: string): boolean;
22
+ s3For(generic: string): string[];
10
23
  mergeInPlace(other: Package): void;
11
- addInfo(type?: PackageType, dependencies?: Package[], ...versionConstraints: readonly Range[]): void;
24
+ addInfo(info: PackageOptions): void;
12
25
  getInfo(): this;
13
26
  deriveVersion(): Range | undefined;
14
27
  static parsePackageVersionRange(constraint?: string, version?: string): Range | undefined;
@@ -8,22 +8,50 @@ class Package {
8
8
  derivedVersion;
9
9
  type;
10
10
  dependencies;
11
+ namespaceInfo;
11
12
  versionConstraints = [];
12
- constructor(name, type, dependencies, ...versionConstraints) {
13
- this.name = name;
14
- this.addInfo(type, dependencies, ...(versionConstraints ?? []).filter(assert_1.isNotUndefined));
13
+ constructor(info) {
14
+ this.name = info.name;
15
+ this.addInfo(info);
16
+ }
17
+ has(name, className) {
18
+ if (!this.namespaceInfo) {
19
+ return false;
20
+ }
21
+ if (name.includes('.')) {
22
+ const [genericSplit, classSplit] = name.split('.');
23
+ const classes = this.namespaceInfo.exportS3Generics.get(genericSplit);
24
+ return classes ? classes.includes(classSplit) : false;
25
+ }
26
+ if (className) {
27
+ const classes = this.namespaceInfo.exportS3Generics.get(name);
28
+ return classes ? classes.includes(className) : false;
29
+ }
30
+ return this.namespaceInfo.exportedFunctions.includes(name) || this.namespaceInfo.exportedSymbols.includes(name);
31
+ }
32
+ s3For(generic) {
33
+ return this.namespaceInfo?.exportS3Generics.get(generic) ?? [];
15
34
  }
16
35
  mergeInPlace(other) {
17
36
  (0, assert_1.guard)(this.name === other.name, 'Can only merge packages with the same name');
18
- this.addInfo(other.type, other.dependencies, ...other.versionConstraints);
37
+ this.addInfo({
38
+ type: other.type,
39
+ dependencies: other.dependencies,
40
+ namespaceInfo: other.namespaceInfo,
41
+ versionConstraints: other.versionConstraints
42
+ });
19
43
  }
20
- addInfo(type, dependencies, ...versionConstraints) {
44
+ addInfo(info) {
45
+ const { type, dependencies, namespaceInfo, versionConstraints } = info;
21
46
  if (type !== undefined) {
22
47
  this.type = type;
23
48
  }
24
49
  if (dependencies !== undefined) {
25
50
  this.dependencies = dependencies;
26
51
  }
52
+ if (namespaceInfo !== undefined) {
53
+ this.namespaceInfo = namespaceInfo;
54
+ }
27
55
  if (versionConstraints !== undefined) {
28
56
  this.derivedVersion ??= versionConstraints[0];
29
57
  for (const constraint of versionConstraints) {
@@ -5,11 +5,12 @@ import { FlowrAnalyzerLoadingOrderDescriptionFilePlugin } from './loading-order-
5
5
  import { FlowrAnalyzerRmdFilePlugin } from './file-plugins/notebooks/flowr-analyzer-rmd-file-plugin';
6
6
  import { FlowrAnalyzerQmdFilePlugin } from './file-plugins/notebooks/flowr-analyzer-qmd-file-plugin';
7
7
  import { FlowrAnalyzerJupyterFilePlugin } from './file-plugins/notebooks/flowr-analyzer-jupyter-file-plugin';
8
+ import { FlowrAnalyzerNamespaceFilePlugin } from './file-plugins/flowr-analyzer-namespace-file-plugin';
8
9
  import { FlowrAnalyzerNewsFilePlugin } from './file-plugins/flowr-analyzer-news-file-plugin';
9
10
  /**
10
11
  * The built-in Flowr Analyzer plugins that are always available.
11
12
  */
12
- export declare const BuiltInPlugins: [["file:description", typeof FlowrAnalyzerDescriptionFilePlugin], ["versions:description", typeof FlowrAnalyzerPackageVersionsDescriptionFilePlugin], ["loading-order:description", typeof FlowrAnalyzerLoadingOrderDescriptionFilePlugin], ["file:rmd", typeof FlowrAnalyzerRmdFilePlugin], ["file:qmd", typeof FlowrAnalyzerQmdFilePlugin], ["file:ipynb", typeof FlowrAnalyzerJupyterFilePlugin], ["file:news", typeof FlowrAnalyzerNewsFilePlugin]];
13
+ export declare const BuiltInPlugins: [["file:description", typeof FlowrAnalyzerDescriptionFilePlugin], ["versions:description", typeof FlowrAnalyzerPackageVersionsDescriptionFilePlugin], ["loading-order:description", typeof FlowrAnalyzerLoadingOrderDescriptionFilePlugin], ["file:rmd", typeof FlowrAnalyzerRmdFilePlugin], ["file:qmd", typeof FlowrAnalyzerQmdFilePlugin], ["file:ipynb", typeof FlowrAnalyzerJupyterFilePlugin], ["file:namespace", typeof FlowrAnalyzerNamespaceFilePlugin], ["file:news", typeof FlowrAnalyzerNewsFilePlugin]];
13
14
  export type BuiltInFlowrPluginName = typeof BuiltInPlugins[number][0];
14
15
  export type BuiltInFlowrPluginArgs<N extends BuiltInFlowrPluginName> = N extends typeof BuiltInPlugins[number][0] ? ConstructorParameters<Extract<typeof BuiltInPlugins[number], [N, PluginProducer]>[1]> : never;
15
16
  type PluginProducer = new (...args: never[]) => FlowrAnalyzerPlugin;
@@ -11,6 +11,7 @@ const flowr_analyzer_rmd_file_plugin_1 = require("./file-plugins/notebooks/flowr
11
11
  const flowr_analyzer_qmd_file_plugin_1 = require("./file-plugins/notebooks/flowr-analyzer-qmd-file-plugin");
12
12
  const assert_1 = require("../../util/assert");
13
13
  const flowr_analyzer_jupyter_file_plugin_1 = require("./file-plugins/notebooks/flowr-analyzer-jupyter-file-plugin");
14
+ const flowr_analyzer_namespace_file_plugin_1 = require("./file-plugins/flowr-analyzer-namespace-file-plugin");
14
15
  const flowr_analyzer_news_file_plugin_1 = require("./file-plugins/flowr-analyzer-news-file-plugin");
15
16
  /**
16
17
  * The built-in Flowr Analyzer plugins that are always available.
@@ -22,6 +23,7 @@ exports.BuiltInPlugins = [
22
23
  ['file:rmd', flowr_analyzer_rmd_file_plugin_1.FlowrAnalyzerRmdFilePlugin],
23
24
  ['file:qmd', flowr_analyzer_qmd_file_plugin_1.FlowrAnalyzerQmdFilePlugin],
24
25
  ['file:ipynb', flowr_analyzer_jupyter_file_plugin_1.FlowrAnalyzerJupyterFilePlugin],
26
+ ['file:namespace', flowr_analyzer_namespace_file_plugin_1.FlowrAnalyzerNamespaceFilePlugin],
25
27
  ['file:news', flowr_analyzer_news_file_plugin_1.FlowrAnalyzerNewsFilePlugin]
26
28
  ];
27
29
  /**
@@ -191,6 +191,7 @@ async function executeCallContextQueries({ analyzer }, queries) {
191
191
  if (requiresCfg) {
192
192
  cfg = await analyzer.controlflow([], cfg_kind_1.CfgKind.WithDataflow);
193
193
  }
194
+ const calls = cfg ? (0, extract_cfg_1.getCallsInCfg)(cfg, dataflow.graph) : undefined;
194
195
  const queriesWhichWantAliases = promotedQueries.filter(q => q.includeAliases);
195
196
  for (const [nodeId, info] of dataflow.graph.verticesOfType(vertex_1.VertexType.FunctionCall)) {
196
197
  /* if we have a vertex, and we check for aliased calls, we want to know if we define this as desired! */
@@ -209,7 +210,6 @@ async function executeCallContextQueries({ analyzer }, queries) {
209
210
  }
210
211
  }
211
212
  }
212
- const calls = cfg ? (0, extract_cfg_1.getCallsInCfg)(cfg, dataflow.graph) : undefined;
213
213
  for (const query of promotedQueries.filter(q => !q.includeAliases && (q.callName instanceof RegExp ? q.callName.test(info.name) : q.callName.has(info.name)))) {
214
214
  const file = ast.idMap.get(nodeId)?.info.file;
215
215
  if (!doesFilepathMatch(file, query.fileFilter)) {
@@ -252,7 +252,7 @@ async function executeCallContextQueries({ analyzer }, queries) {
252
252
  id: nodeId,
253
253
  name: info.name,
254
254
  calls: targets,
255
- linkedIds: linkedIds ? [...linkedIds] : undefined
255
+ linkedIds: linkedIds ? Array.from(linkedIds) : undefined
256
256
  }));
257
257
  }
258
258
  }
@@ -119,7 +119,8 @@ function getResults(queries, { dataflow, config, normalize }, results, kind, fun
119
119
  linkedIds: linked?.length ? linked : undefined,
120
120
  value: value ?? defaultValue,
121
121
  versionConstraints: dep?.versionConstraints,
122
- derivedVersion: dep?.derivedVersion
122
+ derivedVersion: dep?.derivedVersion,
123
+ namespaceInfo: dep?.namespaceInfo
123
124
  });
124
125
  if (result) {
125
126
  results.push(result);
@@ -6,6 +6,7 @@ import type { FunctionInfo } from './function-info/function-info';
6
6
  import type { CallContextQueryResult } from '../call-context-query/call-context-query-format';
7
7
  import type { Range } from 'semver';
8
8
  import type { AsyncOrSync } from 'ts-essentials';
9
+ import type { NamespaceInfo } from '../../../project/plugins/file-plugins/files/flowr-namespace-file';
9
10
  export declare const Unknown = "unknown";
10
11
  export interface DependencyCategorySettings {
11
12
  queryDisplayName?: string;
@@ -63,6 +64,7 @@ export interface DependencyInfo extends Record<string, unknown> {
63
64
  value?: string;
64
65
  versionConstraints?: Range[];
65
66
  derivedVersion?: Range;
67
+ namespaceInfo?: NamespaceInfo;
66
68
  }
67
69
  /**
68
70
  * Gets all dependency categories, including user-defined additional categories.
@@ -34,7 +34,8 @@ exports.DefaultDependencyCategories = {
34
34
  functionName: (n.info.fullLexeme ?? n.lexeme).includes(':::') ? ':::' : '::',
35
35
  value: n.namespace,
36
36
  versionConstraints: dep?.versionConstraints,
37
- derivedVersion: dep?.derivedVersion
37
+ derivedVersion: dep?.derivedVersion,
38
+ namespaceInfo: dep?.namespaceInfo
38
39
  });
39
40
  }
40
41
  });
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.7.2';
9
+ const version = '2.7.4';
10
10
  /**
11
11
  * Retrieves the current flowR version as a new {@link SemVer} object.
12
12
  */