@eagleoutice/flowr 2.9.2 → 2.9.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 (73) hide show
  1. package/README.md +18 -18
  2. package/cli/flowr.js +4 -2
  3. package/control-flow/basic-cfg-guided-visitor.js +10 -4
  4. package/control-flow/cfg-dead-code.js +6 -3
  5. package/control-flow/control-flow-graph.js +3 -2
  6. package/control-flow/simple-visitor.d.ts +2 -1
  7. package/control-flow/simple-visitor.js +6 -7
  8. package/core/print/slice-diff-ansi.js +3 -3
  9. package/dataflow/environments/built-in.d.ts +10 -1
  10. package/dataflow/environments/environment.js +12 -7
  11. package/dataflow/eval/resolve/alias-tracking.d.ts +8 -8
  12. package/dataflow/eval/resolve/alias-tracking.js +33 -34
  13. package/dataflow/eval/resolve/resolve.d.ts +7 -41
  14. package/dataflow/eval/resolve/resolve.js +24 -54
  15. package/dataflow/extractor.js +2 -2
  16. package/dataflow/fn/higher-order-function.d.ts +2 -1
  17. package/dataflow/fn/higher-order-function.js +5 -4
  18. package/dataflow/graph/graph.js +7 -12
  19. package/dataflow/internal/process/functions/call/argument/make-argument.js +1 -2
  20. package/dataflow/internal/process/functions/call/built-in/built-in-register-hook.js +2 -2
  21. package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +2 -2
  22. package/dataflow/internal/process/functions/call/built-in/built-in-s-seven-new-generic.js +2 -2
  23. package/dataflow/internal/process/functions/call/built-in/built-in-s-three-dispatch.js +1 -1
  24. package/documentation/doc-util/doc-search.js +2 -2
  25. package/linter/linter-executor.js +1 -2
  26. package/linter/linter-format.d.ts +37 -11
  27. package/linter/linter-format.js +59 -16
  28. package/linter/linter-rules.d.ts +8 -23
  29. package/linter/rules/absolute-path.d.ts +2 -2
  30. package/linter/rules/absolute-path.js +6 -7
  31. package/linter/rules/dataframe-access-validation.d.ts +1 -1
  32. package/linter/rules/dataframe-access-validation.js +3 -4
  33. package/linter/rules/dead-code.d.ts +2 -2
  34. package/linter/rules/dead-code.js +5 -6
  35. package/linter/rules/deprecated-functions.d.ts +4 -7
  36. package/linter/rules/file-path-validity.d.ts +2 -2
  37. package/linter/rules/file-path-validity.js +9 -6
  38. package/linter/rules/function-finder-util.d.ts +8 -11
  39. package/linter/rules/function-finder-util.js +21 -12
  40. package/linter/rules/naming-convention.d.ts +4 -11
  41. package/linter/rules/naming-convention.js +10 -10
  42. package/linter/rules/network-functions.d.ts +5 -8
  43. package/linter/rules/network-functions.js +14 -1
  44. package/linter/rules/seeded-randomness.d.ts +3 -3
  45. package/linter/rules/seeded-randomness.js +5 -5
  46. package/linter/rules/unused-definition.d.ts +2 -2
  47. package/linter/rules/unused-definition.js +13 -14
  48. package/linter/rules/useless-loop.d.ts +3 -3
  49. package/linter/rules/useless-loop.js +4 -4
  50. package/package.json +1 -1
  51. package/project/plugins/file-plugins/files/flowr-namespace-file.js +2 -2
  52. package/queries/catalog/does-call-query/does-call-query-format.js +2 -2
  53. package/queries/catalog/inspect-exceptions-query/inspect-exception-query-format.js +5 -4
  54. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.js +6 -1
  55. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.d.ts +1 -1
  56. package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.js +5 -4
  57. package/queries/catalog/inspect-recursion-query/inspect-recursion-query-format.js +4 -3
  58. package/queries/catalog/linter-query/linter-query-format.d.ts +1 -1
  59. package/queries/catalog/linter-query/linter-query-format.js +13 -9
  60. package/queries/query.d.ts +1 -1
  61. package/r-bridge/lang-4.x/ast/parser/main/normalize-meta.d.ts +1 -1
  62. package/r-bridge/lang-4.x/ast/parser/main/normalize-meta.js +2 -2
  63. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +1 -1
  64. package/r-bridge/roxygen2/roxygen-parse.js +1 -1
  65. package/statistics/features/supported/defined-functions/defined-functions.d.ts +1 -1
  66. package/statistics/features/supported/defined-functions/defined-functions.js +4 -4
  67. package/statistics/features/supported/used-functions/used-functions.js +3 -3
  68. package/statistics/features/supported/variables/variables.js +4 -4
  69. package/util/mermaid/dfg.d.ts +0 -5
  70. package/util/mermaid/dfg.js +2 -19
  71. package/util/range.d.ts +137 -54
  72. package/util/range.js +249 -88
  73. package/util/version.js +1 -1
@@ -2,6 +2,8 @@ import { LintingRuleCertainty } from '../linter-format';
2
2
  import { type FunctionsMetadata, type FunctionsResult } from './function-finder-util';
3
3
  import { LintingRuleTag } from '../linter-tags';
4
4
  import type { MergeableRecord } from '../../util/objects';
5
+ import type { FlowrSearchElement } from '../../search/flowr-search';
6
+ import type { ParentInformation } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
5
7
  export interface NetworkFunctionsConfig extends MergeableRecord {
6
8
  /** The list of function names that should be marked in the given context if their arguments match. */
7
9
  fns: readonly string[];
@@ -9,19 +11,14 @@ export interface NetworkFunctionsConfig extends MergeableRecord {
9
11
  onlyTriggerWithArgument?: RegExp | string;
10
12
  }
11
13
  export declare const NETWORK_FUNCTIONS: {
12
- readonly createSearch: (config: NetworkFunctionsConfig) => import("../../search/flowr-search-builder").FlowrSearchBuilder<"all", ["filter", "with", "filter"], import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, Promise<import("../../search/flowr-search").FlowrSearchElements<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, [] | import("../../search/flowr-search").FlowrSearchElement<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>>>;
13
- readonly processSearchResult: (e: import("../../search/flowr-search").FlowrSearchElements<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, import("../../search/flowr-search").FlowrSearchElement<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>, c: NetworkFunctionsConfig, d: {
14
+ readonly createSearch: (config: NetworkFunctionsConfig) => import("../../search/flowr-search-builder").FlowrSearchBuilder<"all", ["filter", "with", "filter"], ParentInformation, Promise<import("../../search/flowr-search").FlowrSearchElements<ParentInformation, [] | FlowrSearchElement<ParentInformation>[]>>>;
15
+ readonly processSearchResult: (e: import("../../search/flowr-search").FlowrSearchElements<ParentInformation, FlowrSearchElement<ParentInformation>[]>, c: NetworkFunctionsConfig, d: {
14
16
  normalize: import("../../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst;
15
17
  dataflow: import("../../dataflow/info").DataflowInformation;
16
18
  cfg: import("../../control-flow/control-flow-graph").ControlFlowInformation;
17
19
  analyzer: import("../../project/flowr-analyzer").ReadonlyFlowrAnalysisProvider;
18
20
  }) => {
19
- results: {
20
- certainty: import("../linter-format").LintingResultCertainty;
21
- involvedId: import("../../r-bridge/lang-4.x/ast/model/processing/node-id").NodeId;
22
- function: import("../../dataflow/environments/identifier").BrandedIdentifier;
23
- range: import("../../util/range").SourceRange;
24
- }[];
21
+ results: FunctionsResult[];
25
22
  '.meta': FunctionsMetadata;
26
23
  };
27
24
  readonly prettyPrint: {
@@ -5,9 +5,22 @@ const linter_format_1 = require("../linter-format");
5
5
  const function_finder_util_1 = require("./function-finder-util");
6
6
  const linter_tags_1 = require("../linter-tags");
7
7
  const read_functions_1 = require("../../queries/catalog/dependencies-query/function-info/read-functions");
8
+ const logic_1 = require("../../util/logic");
8
9
  exports.NETWORK_FUNCTIONS = {
9
10
  createSearch: (config) => function_finder_util_1.functionFinderUtil.createSearch(config.fns),
10
- processSearchResult: (e, c, d) => function_finder_util_1.functionFinderUtil.processSearchResult(e, c, d, es => es.filter(e => function_finder_util_1.functionFinderUtil.requireArgumentValue(e, read_functions_1.ReadFunctions, { analyzer: d.analyzer, dataflow: d.dataflow, normalize: d.normalize }, c.onlyTriggerWithArgument))),
11
+ processSearchResult: (e, c, d) => function_finder_util_1.functionFinderUtil.processSearchResult(e, c, d, es => {
12
+ const res = [];
13
+ for (const e of es) {
14
+ const val = function_finder_util_1.functionFinderUtil.requireArgumentValue(e, read_functions_1.ReadFunctions, d, c.onlyTriggerWithArgument);
15
+ if (val === logic_1.Ternary.Never) {
16
+ continue;
17
+ }
18
+ const x = e;
19
+ x.certainty = val === logic_1.Ternary.Always ? linter_format_1.LintingResultCertainty.Certain : linter_format_1.LintingResultCertainty.Uncertain;
20
+ res.push(x);
21
+ }
22
+ return res;
23
+ }),
11
24
  prettyPrint: function_finder_util_1.functionFinderUtil.prettyPrint('network operations'),
12
25
  info: {
13
26
  name: 'Network Functions',
@@ -1,11 +1,11 @@
1
1
  import { type LintingResult, LintingResultCertainty, LintingRuleCertainty } from '../linter-format';
2
- import type { SourceRange } from '../../util/range';
2
+ import { SourceLocation } from '../../util/range';
3
3
  import type { MergeableRecord } from '../../util/objects';
4
4
  import type { BrandedIdentifier } from '../../dataflow/environments/identifier';
5
5
  import { LintingRuleTag } from '../linter-tags';
6
6
  export interface SeededRandomnessResult extends LintingResult {
7
7
  function: string;
8
- range: SourceRange;
8
+ loc: SourceLocation;
9
9
  }
10
10
  export interface SeededRandomnessConfig extends MergeableRecord {
11
11
  /**
@@ -41,7 +41,7 @@ export declare const SEEDED_RANDOMNESS: {
41
41
  involvedId: import("../../r-bridge/lang-4.x/ast/model/processing/node-id").NodeId;
42
42
  certainty: LintingResultCertainty;
43
43
  function: BrandedIdentifier;
44
- range: SourceRange;
44
+ loc: [startLine: number, startColumn: number, endLine: number, endColumn: number, f?: string | undefined];
45
45
  }[];
46
46
  '.meta': SeededRandomnessMeta;
47
47
  };
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SEEDED_RANDOMNESS = void 0;
4
4
  const linter_format_1 = require("../linter-format");
5
+ const range_1 = require("../../util/range");
5
6
  const flowr_search_builder_1 = require("../../search/flowr-search-builder");
6
- const dfg_1 = require("../../util/mermaid/dfg");
7
7
  const search_enrichers_1 = require("../../search/search-executor/search-enrichers");
8
8
  const identifier_1 = require("../../dataflow/environments/identifier");
9
9
  const flowr_search_filters_1 = require("../../search/flowr-search-filters");
@@ -51,7 +51,7 @@ exports.SEEDED_RANDOMNESS = {
51
51
  metadata.consumerCalls++;
52
52
  return {
53
53
  involvedId: element.node.info.id,
54
- range: element.node.info.fullRange,
54
+ loc: range_1.SourceLocation.fromNode(element.node) ?? range_1.SourceLocation.invalid(),
55
55
  target: target,
56
56
  searchElement: element
57
57
  };
@@ -117,7 +117,7 @@ exports.SEEDED_RANDOMNESS = {
117
117
  involvedId: element.involvedId,
118
118
  certainty: cdsOfProduces.size > 0 ? linter_format_1.LintingResultCertainty.Uncertain : linter_format_1.LintingResultCertainty.Certain,
119
119
  function: element.target,
120
- range: element.range
120
+ loc: element.loc
121
121
  }];
122
122
  }),
123
123
  '.meta': metadata
@@ -135,8 +135,8 @@ exports.SEEDED_RANDOMNESS = {
135
135
  description: 'Checks whether randomness-based function calls are preceded by a random seed generation function. For consistent reproducibility, functions that use randomness should only be called after a constant random seed is set using a function like `set.seed`.'
136
136
  },
137
137
  prettyPrint: {
138
- [linter_format_1.LintingPrettyPrintContext.Query]: (result, _meta) => `Function \`${result.function}\` at ${(0, dfg_1.formatRange)(result.range)}`,
139
- [linter_format_1.LintingPrettyPrintContext.Full]: (result, _meta) => `Function \`${result.function}\` at ${(0, dfg_1.formatRange)(result.range)} is called without a preceding random seed function like \`set.seed\``
138
+ [linter_format_1.LintingPrettyPrintContext.Query]: (result, _meta) => `Function \`${result.function}\` at ${range_1.SourceLocation.format(result.loc)}`,
139
+ [linter_format_1.LintingPrettyPrintContext.Full]: (result, _meta) => `Function \`${result.function}\` at ${range_1.SourceLocation.format(result.loc)} is called without a preceding random seed function like \`set.seed\``
140
140
  }
141
141
  };
142
142
  function getDefaultAssignments() {
@@ -1,11 +1,11 @@
1
1
  import { type LintingResult, LintingRuleCertainty } from '../linter-format';
2
2
  import type { MergeableRecord } from '../../util/objects';
3
- import { type SourceRange } from '../../util/range';
3
+ import { SourceLocation } from '../../util/range';
4
4
  import { LintingRuleTag } from '../linter-tags';
5
5
  import type { NormalizedAst, ParentInformation } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
6
6
  export interface UnusedDefinitionResult extends LintingResult {
7
7
  variableName?: string;
8
- range: SourceRange;
8
+ loc: SourceLocation;
9
9
  }
10
10
  export interface UnusedDefinitionConfig extends MergeableRecord {
11
11
  /**
@@ -4,7 +4,6 @@ exports.UNUSED_DEFINITION = void 0;
4
4
  const linter_format_1 = require("../linter-format");
5
5
  const flowr_search_builder_1 = require("../../search/flowr-search-builder");
6
6
  const range_1 = require("../../util/range");
7
- const dfg_1 = require("../../util/mermaid/dfg");
8
7
  const linter_tags_1 = require("../linter-tags");
9
8
  const assert_1 = require("../../util/assert");
10
9
  const vertex_1 = require("../../dataflow/graph/vertex");
@@ -14,8 +13,8 @@ const InterestingEdgesVariable = edge_1.EdgeType.Reads | edge_1.EdgeType.Calls |
14
13
  const InterestingEdgesFunction = edge_1.EdgeType.Reads | edge_1.EdgeType.Calls; // include read as this could print the function definition
15
14
  const InterestingEdgesTargets = edge_1.EdgeType.SideEffectOnCall;
16
15
  function getDefinitionArguments(def, dfg) {
17
- return [...dfg.outgoingEdges(def) ?? []].filter(([, e]) => edge_1.DfEdge.includesType(e, edge_1.EdgeType.DefinedBy))
18
- .map(([target]) => target);
16
+ return dfg.outgoingEdges(def)?.entries().filter(([, e]) => edge_1.DfEdge.includesType(e, edge_1.EdgeType.DefinedBy))
17
+ .map(([target]) => target).toArray() ?? [];
19
18
  }
20
19
  function buildQuickFix(variable, dfg, ast) {
21
20
  // first we check whether any of the 'Defined by' targets have any obligations - if so, we can not remove the definition
@@ -26,21 +25,21 @@ function buildQuickFix(variable, dfg, ast) {
26
25
  }
27
26
  const definedBys = getDefinitionArguments(variable.info.id, dfg);
28
27
  const hasImportantArgs = definedBys.some(d => dfg.unknownSideEffects.has(d))
29
- || definedBys.flatMap(e => [...dfg.outgoingEdges(e) ?? []])
28
+ || definedBys.flatMap(e => Array.from(dfg.outgoingEdges(e) ?? []))
30
29
  .some(([target, e]) => {
31
30
  return edge_1.DfEdge.includesType(e, InterestingEdgesTargets) || dfg.unknownSideEffects.has(target);
32
31
  });
33
32
  if (hasImportantArgs) {
34
33
  return undefined; // we can not remove this definition, it has important arguments
35
34
  }
36
- const totalRangeToRemove = (0, range_1.mergeRanges)([...definedBys.map(d => {
35
+ const totalRangeToRemove = range_1.SourceLocation.merge([...definedBys.map(d => {
37
36
  const vertex = ast.idMap.get(d);
38
- return vertex?.info.fullRange ?? vertex?.location;
37
+ return vertex ? range_1.SourceLocation.fromNode(vertex) : undefined;
39
38
  }),
40
39
  variable.info.fullRange ?? variable.location]);
41
40
  return [{
42
41
  type: 'remove',
43
- range: totalRangeToRemove,
42
+ loc: totalRangeToRemove ?? range_1.SourceLocation.invalid(),
44
43
  description: `Remove unused definition of \`${variable.lexeme}\``
45
44
  }];
46
45
  }
@@ -48,13 +47,13 @@ function buildQuickFix(variable, dfg, ast) {
48
47
  * consider `x <- function() ...` if we say `x` is unused and propose to remove everything, there should be no separate quick fix for the function definition
49
48
  */
50
49
  function onlyKeepSupersetOfUnused(elements) {
51
- const ranges = elements.flatMap(e => e.quickFix?.map(q => q.range) ?? [e.range]);
52
- if (ranges.length <= 1) {
50
+ const locs = elements.flatMap(e => e.quickFix?.map(q => q.loc) ?? [e.loc]);
51
+ if (locs.length <= 1) {
53
52
  return elements; // nothing to filter, only one element
54
53
  }
55
54
  return elements.filter(e => {
56
- const otherRange = (0, range_1.mergeRanges)((e.quickFix?.map(q => q.range) ?? [e.range]));
57
- return !ranges.some(r => (0, range_1.rangeCompare)(r, otherRange) !== 0 && (0, range_1.rangeIsSubsetOf)(otherRange, r)); // there is no smaller remove
55
+ const otherLoc = range_1.SourceLocation.merge((e.quickFix?.map(q => q.loc) ?? [e.loc])) ?? range_1.SourceLocation.invalid();
56
+ return !locs.some(r => range_1.SourceLocation.compare(r, otherLoc) !== 0 && range_1.SourceLocation.isSubsetOf(otherLoc, r)); // there is no smaller remove
58
57
  });
59
58
  }
60
59
  exports.UNUSED_DEFINITION = {
@@ -84,7 +83,7 @@ exports.UNUSED_DEFINITION = {
84
83
  certainty: linter_format_1.LintingResultCertainty.Uncertain,
85
84
  variableName,
86
85
  involvedId: element.node.info.id,
87
- range: element.node.info.fullRange ?? element.node.location ?? (0, range_1.rangeFrom)(-1, -1, -1, -1),
86
+ loc: range_1.SourceLocation.fromNode(element.node) ?? range_1.SourceLocation.invalid(),
88
87
  quickFix: buildQuickFix(element.node, data.dataflow.graph, data.normalize)
89
88
  }];
90
89
  }).filter(assert_1.isNotUndefined)),
@@ -92,8 +91,8 @@ exports.UNUSED_DEFINITION = {
92
91
  };
93
92
  },
94
93
  prettyPrint: {
95
- [linter_format_1.LintingPrettyPrintContext.Query]: result => `Definition of \`${result.variableName}\` at ${(0, dfg_1.formatRange)(result.range)}`,
96
- [linter_format_1.LintingPrettyPrintContext.Full]: result => `Definition of \`${result.variableName}\` at ${(0, dfg_1.formatRange)(result.range)} is unused`
94
+ [linter_format_1.LintingPrettyPrintContext.Query]: result => `Definition of \`${result.variableName}\` at ${range_1.SourceLocation.format(result.loc)}`,
95
+ [linter_format_1.LintingPrettyPrintContext.Full]: result => `Definition of \`${result.variableName}\` at ${range_1.SourceLocation.format(result.loc)} is unused`
97
96
  },
98
97
  info: {
99
98
  name: 'Unused Definitions',
@@ -1,11 +1,11 @@
1
1
  import type { BuiltInProcName } from '../../dataflow/environments/built-in';
2
2
  import type { MergeableRecord } from '../../util/objects';
3
- import type { SourceRange } from '../../util/range';
3
+ import { SourceLocation } from '../../util/range';
4
4
  import { type LintingResult, LintingResultCertainty, LintingRuleCertainty } from '../linter-format';
5
5
  import { LintingRuleTag } from '../linter-tags';
6
6
  export interface UselessLoopResult extends LintingResult {
7
7
  name: string;
8
- range: SourceRange;
8
+ loc: SourceLocation;
9
9
  }
10
10
  export interface UselessLoopConfig extends MergeableRecord {
11
11
  /** Function origins that are considered loops */
@@ -25,7 +25,7 @@ export declare const USELESS_LOOP: {
25
25
  results: {
26
26
  certainty: LintingResultCertainty.Certain;
27
27
  name: string;
28
- range: SourceRange;
28
+ loc: [startLine: number, startColumn: number, endLine: number, endColumn: number, f?: string | undefined];
29
29
  involvedId: import("../../r-bridge/lang-4.x/ast/model/processing/node-id").NodeId;
30
30
  }[];
31
31
  '.meta': {
@@ -4,7 +4,7 @@ exports.USELESS_LOOP = void 0;
4
4
  const useless_loop_1 = require("../../control-flow/useless-loop");
5
5
  const vertex_1 = require("../../dataflow/graph/vertex");
6
6
  const flowr_search_builder_1 = require("../../search/flowr-search-builder");
7
- const dfg_1 = require("../../util/mermaid/dfg");
7
+ const range_1 = require("../../util/range");
8
8
  const linter_format_1 = require("../linter-format");
9
9
  const linter_tags_1 = require("../linter-tags");
10
10
  exports.USELESS_LOOP = {
@@ -19,7 +19,7 @@ exports.USELESS_LOOP = {
19
19
  }).filter(loop => (0, useless_loop_1.onlyLoopsOnce)(loop.node.info.id, dataflow.graph, cfg, normalize, analyzer.inspectContext())).map(res => ({
20
20
  certainty: linter_format_1.LintingResultCertainty.Certain,
21
21
  name: res.node.lexeme,
22
- range: res.node.info.fullRange,
22
+ loc: range_1.SourceLocation.fromNode(res.node) ?? range_1.SourceLocation.invalid(),
23
23
  involvedId: res.node.info.id
24
24
  }));
25
25
  return {
@@ -30,8 +30,8 @@ exports.USELESS_LOOP = {
30
30
  };
31
31
  },
32
32
  prettyPrint: {
33
- [linter_format_1.LintingPrettyPrintContext.Query]: result => `${result.name}-loop at ${(0, dfg_1.formatRange)(result.range)} only loops once`,
34
- [linter_format_1.LintingPrettyPrintContext.Full]: result => `${result.name}-loop at ${(0, dfg_1.formatRange)(result.range)} only loops once`
33
+ [linter_format_1.LintingPrettyPrintContext.Query]: result => `${result.name}-loop at ${range_1.SourceLocation.format(result.loc)} only loops once`,
34
+ [linter_format_1.LintingPrettyPrintContext.Full]: result => `${result.name}-loop at ${range_1.SourceLocation.format(result.loc)} only loops once`
35
35
  },
36
36
  info: {
37
37
  name: 'Useless Loops',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.9.2",
3
+ "version": "2.9.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": {
@@ -207,9 +207,9 @@ function wrapRNodeInNotCall(node, idMap) {
207
207
  info: node.info,
208
208
  lexeme: 'not',
209
209
  content: identifier_1.Identifier.make('not'),
210
- location: node.location ?? (0, range_1.invalidRange)(),
210
+ location: node.location ?? range_1.SourceRange.invalid(),
211
211
  },
212
- location: node.location ?? (0, range_1.invalidRange)(),
212
+ location: node.location ?? range_1.SourceRange.invalid(),
213
213
  lexeme: 'not',
214
214
  arguments: [(0, make_argument_1.toUnnamedArgument)(node, idMap)]
215
215
  };
@@ -7,9 +7,9 @@ exports.DoesCallQueryDefinition = void 0;
7
7
  const ansi_1 = require("../../../util/text/ansi");
8
8
  const joi_1 = __importDefault(require("joi"));
9
9
  const does_call_query_executor_1 = require("./does-call-query-executor");
10
- const dfg_1 = require("../../../util/mermaid/dfg");
11
10
  const args_1 = require("../../../util/text/args");
12
11
  const strings_1 = require("../../../util/text/strings");
12
+ const range_1 = require("../../../util/range");
13
13
  const FormatError = 'Invalid constraint format, expected format "(left:$id/"regex")"';
14
14
  /**
15
15
  * Parses a constraint from a string argument.
@@ -80,7 +80,7 @@ exports.DoesCallQueryDefinition = {
80
80
  }
81
81
  else {
82
82
  const loc = idMap.get(v.call)?.location ?? undefined;
83
- result.push(` - Call with id ${(0, ansi_1.bold)(String(v.call), formatter)} (${(0, dfg_1.formatRange)(loc)})`);
83
+ result.push(` - Call with id ${(0, ansi_1.bold)(String(v.call), formatter)} (${range_1.SourceRange.format(loc)})`);
84
84
  }
85
85
  }
86
86
  return true;
@@ -8,8 +8,8 @@ const ansi_1 = require("../../../util/text/ansi");
8
8
  const joi_1 = __importDefault(require("joi"));
9
9
  const inspect_exception_query_executor_1 = require("./inspect-exception-query-executor");
10
10
  const node_id_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/node-id");
11
- const dfg_1 = require("../../../util/mermaid/dfg");
12
11
  const slice_query_parser_1 = require("../../../cli/repl/parser/slice-query-parser");
12
+ const range_1 = require("../../../util/range");
13
13
  const info_1 = require("../../../dataflow/info");
14
14
  function inspectExceptionLineParser(_output, line, _config) {
15
15
  const criteria = (0, slice_query_parser_1.sliceCriteriaParser)(line[0]);
@@ -28,15 +28,16 @@ exports.InspectExceptionQueryDefinition = {
28
28
  result.push(`Query: ${(0, ansi_1.bold)('inspect-exception', formatter)} (${out['.meta'].timing.toFixed(0)}ms)`);
29
29
  const n = await processed.normalize();
30
30
  function getLoc(r) {
31
- return n.idMap.get((0, node_id_1.normalizeIdToNumberIfPossible)(r))?.location ?? undefined;
31
+ const node = n.idMap.get((0, node_id_1.normalizeIdToNumberIfPossible)(r));
32
+ return node ? range_1.SourceLocation.fromNode(node) : undefined;
32
33
  }
33
34
  function getLexeme(r) {
34
35
  return n.idMap.get((0, node_id_1.normalizeIdToNumberIfPossible)(r))?.lexeme ?? String(r);
35
36
  }
36
37
  for (const [r, v] of Object.entries(out.exceptions)) {
37
- result.push(` - Function ${(0, ansi_1.bold)(r, formatter)} (${(0, dfg_1.formatRange)(getLoc(r))}) ${v.length > 0 ? 'throws exceptions:' : 'does not throw exceptions.'}`);
38
+ result.push(` - Function ${(0, ansi_1.bold)(r, formatter)} (${range_1.SourceLocation.format(getLoc(r))}) ${v.length > 0 ? 'throws exceptions:' : 'does not throw exceptions.'}`);
38
39
  for (const { id: ex, cds } of v) {
39
- result.push(` - Exception ${(0, info_1.happensInEveryBranch)(cds) ? 'always ' : 'maybe '}thrown at id ${(0, ansi_1.bold)(String(ex), formatter)} "${getLexeme(ex)}" (${(0, dfg_1.formatRange)(getLoc(ex))}, cds: ${cds?.map(c => c.when + ':' + (0, dfg_1.formatRange)(getLoc(c.id))).join(', ') ?? 'none'})`);
40
+ result.push(` - Exception ${(0, info_1.happensInEveryBranch)(cds) ? 'always ' : 'maybe '}thrown at id ${(0, ansi_1.bold)(String(ex), formatter)} "${getLexeme(ex)}" (${range_1.SourceLocation.format(getLoc(ex))}, cds: ${cds?.map(c => c.when + ':' + range_1.SourceLocation.format(getLoc(c.id))).join(', ') ?? 'none'})`);
40
41
  }
41
42
  }
42
43
  return true;
@@ -4,6 +4,7 @@ exports.executeHigherOrderQuery = executeHigherOrderQuery;
4
4
  const higher_order_function_1 = require("../../../dataflow/fn/higher-order-function");
5
5
  const parse_1 = require("../../../slicing/criterion/parse");
6
6
  const vertex_1 = require("../../../dataflow/graph/vertex");
7
+ const invert_dfg_1 = require("../../../dataflow/graph/invert-dfg");
7
8
  /**
8
9
  * Execute higher-order function inspection queries on the given analyzer.
9
10
  */
@@ -34,9 +35,13 @@ async function executeHigherOrderQuery({ analyzer }, queries) {
34
35
  const graph = (await analyzer.dataflow()).graph;
35
36
  const fns = graph.verticesOfType(vertex_1.VertexType.FunctionDefinition)
36
37
  .filter(([, v]) => filterFor.size === 0 || filterFor.has(v.id));
38
+ let invertedGraph;
39
+ if (filterFor.size === 0 || filterFor.size > 10) {
40
+ invertedGraph = (0, invert_dfg_1.invertDfg)(graph, analyzer.inspectContext().env.makeCleanEnv());
41
+ }
37
42
  const result = {};
38
43
  for (const [id] of fns) {
39
- result[id] = (0, higher_order_function_1.isFunctionHigherOrder)(id, graph, analyzer.inspectContext());
44
+ result[id] = (0, higher_order_function_1.isFunctionHigherOrder)(id, graph, analyzer.inspectContext(), invertedGraph);
40
45
  }
41
46
  return {
42
47
  '.meta': {
@@ -17,7 +17,7 @@ export interface InspectHigherOrderQuery extends BaseQueryFormat {
17
17
  export interface InspectHigherOrderQueryResult extends BaseQueryResult {
18
18
  readonly higherOrder: Record<NodeId, boolean>;
19
19
  }
20
- declare function inspectHoLineParser(output: ReplOutput, line: readonly string[], _config: FlowrConfigOptions): ParsedQueryLine<'inspect-higher-order'>;
20
+ declare function inspectHoLineParser(_output: ReplOutput, line: readonly string[], _config: FlowrConfigOptions): ParsedQueryLine<'inspect-higher-order'>;
21
21
  export declare const InspectHigherOrderQueryDefinition: {
22
22
  readonly executor: typeof executeHigherOrderQuery;
23
23
  readonly asciiSummarizer: (formatter: import("../../../util/text/ansi").OutputFormatter, processed: import("../../../project/flowr-analyzer").ReadonlyFlowrAnalysisProvider<import("../../../r-bridge/parser").KnownParser>, queryResults: BaseQueryResult, result: string[]) => Promise<boolean>;
@@ -8,9 +8,9 @@ const ansi_1 = require("../../../util/text/ansi");
8
8
  const joi_1 = __importDefault(require("joi"));
9
9
  const inspect_higher_order_query_executor_1 = require("./inspect-higher-order-query-executor");
10
10
  const node_id_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/node-id");
11
- const dfg_1 = require("../../../util/mermaid/dfg");
12
11
  const slice_query_parser_1 = require("../../../cli/repl/parser/slice-query-parser");
13
- function inspectHoLineParser(output, line, _config) {
12
+ const range_1 = require("../../../util/range");
13
+ function inspectHoLineParser(_output, line, _config) {
14
14
  const criteria = (0, slice_query_parser_1.sliceCriteriaParser)(line[0]);
15
15
  return {
16
16
  query: {
@@ -26,8 +26,9 @@ exports.InspectHigherOrderQueryDefinition = {
26
26
  const out = queryResults;
27
27
  result.push(`Query: ${(0, ansi_1.bold)('inspect-higher-order', formatter)} (${out['.meta'].timing.toFixed(0)}ms)`);
28
28
  for (const [r, v] of Object.entries(out.higherOrder)) {
29
- const loc = (await processed.normalize()).idMap.get((0, node_id_1.normalizeIdToNumberIfPossible)(r))?.location ?? undefined;
30
- result.push(` - Function ${(0, ansi_1.bold)(r, formatter)} (${(0, dfg_1.formatRange)(loc)}) is ${v ? '' : 'not '}a higher-order function`);
29
+ const node = (await processed.normalize()).idMap.get((0, node_id_1.normalizeIdToNumberIfPossible)(r));
30
+ const loc = node ? range_1.SourceLocation.fromNode(node) : undefined;
31
+ result.push(` - Function ${(0, ansi_1.bold)(r, formatter)} (${range_1.SourceLocation.format(loc)}) is ${v ? '' : 'not '}a higher-order function`);
31
32
  }
32
33
  return true;
33
34
  },
@@ -8,8 +8,8 @@ const ansi_1 = require("../../../util/text/ansi");
8
8
  const joi_1 = __importDefault(require("joi"));
9
9
  const inspect_recursion_query_executor_1 = require("./inspect-recursion-query-executor");
10
10
  const node_id_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/node-id");
11
- const dfg_1 = require("../../../util/mermaid/dfg");
12
11
  const slice_query_parser_1 = require("../../../cli/repl/parser/slice-query-parser");
12
+ const range_1 = require("../../../util/range");
13
13
  function inspectRecLineParser(output, line, _config) {
14
14
  const criteria = (0, slice_query_parser_1.sliceCriteriaParser)(line[0]);
15
15
  return {
@@ -26,8 +26,9 @@ exports.InspectRecursionQueryDefinition = {
26
26
  const out = queryResults;
27
27
  result.push(`Query: ${(0, ansi_1.bold)('inspect-recursion', formatter)} (${out['.meta'].timing.toFixed(0)}ms)`);
28
28
  for (const [r, v] of Object.entries(out.recursive)) {
29
- const loc = (await processed.normalize()).idMap.get((0, node_id_1.normalizeIdToNumberIfPossible)(r))?.location ?? undefined;
30
- result.push(` - Function ${(0, ansi_1.bold)(r, formatter)} (${(0, dfg_1.formatRange)(loc)}) is ${v ? '' : 'not '}recursive`);
29
+ const node = (await processed.normalize()).idMap.get((0, node_id_1.normalizeIdToNumberIfPossible)(r));
30
+ const loc = node ? range_1.SourceLocation.fromNode(node) : undefined;
31
+ result.push(` - Function ${(0, ansi_1.bold)(r, formatter)} (${range_1.SourceLocation.format(loc)}) is ${v ? '' : 'not '}recursive`);
31
32
  }
32
33
  return true;
33
34
  },
@@ -3,7 +3,7 @@ import type { ParsedQueryLine } from '../../query';
3
3
  import Joi from 'joi';
4
4
  import { executeLinterQuery } from './linter-query-executor';
5
5
  import { type LintingRuleNames } from '../../../linter/linter-rules';
6
- import { type ConfiguredLintingRule, type LintingResults } from '../../../linter/linter-format';
6
+ import { type ConfiguredLintingRule, LintingResults } from '../../../linter/linter-format';
7
7
  import type { FlowrConfigOptions } from '../../../config';
8
8
  import type { ReplOutput } from '../../../cli/repl/commands/repl-main';
9
9
  import type { CommandCompletions } from '../../../cli/repl/core';
@@ -82,13 +82,22 @@ exports.LinterQueryDefinition = {
82
82
  asciiSummarizer: (formatter, analyzer, queryResults, result) => {
83
83
  const out = queryResults;
84
84
  result.push(`Query: ${(0, ansi_1.bold)('linter', formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
85
- const allDidFail = Object.values(out.results).every(r => (0, linter_format_1.isLintingResultsError)(r));
85
+ const allDidFail = Object.values(out.results).every(linter_format_1.LintingResults.isError);
86
86
  if (allDidFail) {
87
87
  result.push('All linting rules failed to execute.');
88
88
  if (analyzer.inspectContext().files.loadingOrder.getUnorderedRequests().length === 0) {
89
89
  result.push(formatter.format('No requests to lint for were found in the analysis.', { color: 1 /* Colors.Red */, effect: ansi_1.ColorEffect.Foreground, style: 1 /* FontStyles.Bold */ }));
90
90
  result.push('If you consider this an error, please report a bug: ' + (0, assert_1.getGuardIssueUrl)('analyzer found no requests to lint for'));
91
91
  }
92
+ else if (Object.values(out.results).length === 1) {
93
+ const fst = Object.values(out.results)[0];
94
+ result.push('Error: ' + linter_format_1.LintingResults.stringifyError(fst));
95
+ if (fst.error instanceof Error) {
96
+ // print stack
97
+ result.push('Stack Trace:\n' + fst.error.stack);
98
+ }
99
+ }
100
+ result.push('If you consider this an error that should be fixed, please report a bug: ' + (0, assert_1.getGuardIssueUrl)('linting rule threw an error'));
92
101
  return true;
93
102
  }
94
103
  for (const [ruleName, results] of Object.entries(out.results)) {
@@ -107,19 +116,14 @@ exports.LinterQueryDefinition = {
107
116
  }).description('The linter query lints for the given set of rules and returns the result.'),
108
117
  flattenInvolvedNodes: (queryResults) => {
109
118
  const out = queryResults;
110
- return Object.values(out.results).flatMap(v => {
111
- if ((0, linter_format_1.isLintingResultsError)(v)) {
112
- return [];
113
- }
114
- return v.results.flatMap(v => Array.isArray(v.involvedId) ? v.involvedId : [v.involvedId]);
115
- }).filter(assert_1.isNotUndefined);
119
+ return Object.values(out.results).flatMap(v => Array.from(linter_format_1.LintingResults.allInvolvedIds(v))).filter(assert_1.isNotUndefined);
116
120
  }
117
121
  };
118
122
  function addLintingRuleResult(ruleName, results, result) {
119
123
  const rule = linter_rules_1.LintingRules[ruleName];
120
124
  result.push(` ╰ **${rule.info.name}** (${ruleName}):`);
121
- if ((0, linter_format_1.isLintingResultsError)(results)) {
122
- const error = results.error.includes('At least one request must be set') ? 'No requests to lint for were found in the analysis.' : 'Error during execution of rule: ' + results.error;
125
+ if (linter_format_1.LintingResults.isError(results)) {
126
+ const error = linter_format_1.LintingResults.stringifyError(results).includes('At least one request must be set') ? 'No requests to lint for were found in the analysis.' : 'Error during execution of rule: ' + linter_format_1.LintingResults.stringifyError(results);
123
127
  result.push(` ╰ ${error}`);
124
128
  return;
125
129
  }
@@ -191,7 +191,7 @@ export declare const SupportedQueries: {
191
191
  readonly 'inspect-higher-order': {
192
192
  readonly executor: typeof import("./catalog/inspect-higher-order-query/inspect-higher-order-query-executor").executeHigherOrderQuery;
193
193
  readonly asciiSummarizer: (formatter: OutputFormatter, processed: ReadonlyFlowrAnalysisProvider<import("../r-bridge/parser").KnownParser>, queryResults: BaseQueryResult, result: string[]) => Promise<boolean>;
194
- readonly fromLine: (output: ReplOutput, line: readonly string[], _config: FlowrConfigOptions) => ParsedQueryLine<"inspect-higher-order">;
194
+ readonly fromLine: (_output: ReplOutput, line: readonly string[], _config: FlowrConfigOptions) => ParsedQueryLine<"inspect-higher-order">;
195
195
  readonly schema: Joi.ObjectSchema<any>;
196
196
  readonly flattenInvolvedNodes: (queryResults: BaseQueryResult) => NodeId[];
197
197
  };
@@ -1,5 +1,5 @@
1
1
  import type { JsonEntry, NamedJsonEntry } from '../json/format';
2
- import { type SourceRange } from '../../../../../util/range';
2
+ import { SourceRange } from '../../../../../util/range';
3
3
  import { type RawRType } from '../../model/type';
4
4
  import type { RNode } from '../../model/model';
5
5
  import type { RExpressionList } from '../../model/nodes/r-expression-list';
@@ -15,7 +15,7 @@ const type_1 = require("../../model/type");
15
15
  * Given a JSON element, extract the source location of the corresponding element in the R-ast
16
16
  */
17
17
  function extractLocation(ast) {
18
- return (0, range_1.rangeFrom)(ast.line1, ast.col1, ast.line2, ast.col2);
18
+ return range_1.SourceRange.from(ast.line1, ast.col1, ast.line2, ast.col2);
19
19
  }
20
20
  /**
21
21
  * The JSON object that represents the input contains various meta-information.
@@ -69,7 +69,7 @@ function retrieveOpName(operator) {
69
69
  function ensureChildrenAreLhsAndRhsOrdered(first, second) {
70
70
  const firstOtherLoc = extractLocation(first);
71
71
  const secondOtherLoc = extractLocation(second);
72
- if (!(0, range_1.rangeStartsCompletelyBefore)(firstOtherLoc, secondOtherLoc)) {
72
+ if (!range_1.SourceRange.startsCompletelyBefore(firstOtherLoc, secondOtherLoc)) {
73
73
  throw new normalizer_data_1.ParseError(`expected the first child to be the lhs, yet received ${JSON.stringify(first)} & ${JSON.stringify(second)}`);
74
74
  }
75
75
  }
@@ -594,7 +594,7 @@ function convertTreeNode(node) {
594
594
  }
595
595
  function makeSourceRange(node) {
596
596
  if (!node) {
597
- return (0, range_1.invalidRange)();
597
+ return range_1.SourceRange.invalid();
598
598
  }
599
599
  if (node.startPosition && node.endPosition) {
600
600
  return [
@@ -49,7 +49,7 @@ function parseRoxygenCommentsOfNode(node, idMap) {
49
49
  attachedTo: cur?.info.id,
50
50
  requestNode: node.info.id,
51
51
  range: [
52
- ...(0, range_1.mergeRanges)(comments.map(c => c.location)),
52
+ ...range_1.SourceRange.merge(comments.map(c => c.location)),
53
53
  comments.find(c => c.info.file)?.info.file
54
54
  ]
55
55
  };
@@ -1,7 +1,7 @@
1
1
  import type { Feature } from '../../feature';
2
2
  import type { Writable } from 'ts-essentials';
3
3
  import type { MergeableRecord } from '../../../../util/objects';
4
- import { type SourcePosition } from '../../../../util/range';
4
+ import { SourcePosition } from '../../../../util/range';
5
5
  declare const initialFunctionDefinitionInfo: {
6
6
  /** all, anonymous, assigned, non-assigned, ... */
7
7
  total: number;
@@ -31,13 +31,13 @@ function retrieveAllCallsites(input, node, recursiveCalls) {
31
31
  }
32
32
  const loc = input.normalizedRAst.idMap.get(target)?.location;
33
33
  if (loc) {
34
- callsites.push((0, range_1.getRangeStart)(loc));
34
+ callsites.push(range_1.SourceRange.getStart(loc));
35
35
  }
36
36
  }
37
37
  for (const call of recursiveCalls) {
38
38
  const loc = call.location;
39
39
  if (loc) {
40
- callsites.push((0, range_1.getRangeStart)(loc));
40
+ callsites.push(range_1.SourceRange.getStart(loc));
41
41
  }
42
42
  }
43
43
  return callsites;
@@ -61,7 +61,7 @@ function visitDefinitions(info, input) {
61
61
  .map(([vertex]) => {
62
62
  const l = graph.idMap?.get(vertex.id)?.location;
63
63
  return {
64
- location: l ? (0, range_1.getRangeStart)(l) : [-1, -1]
64
+ location: l ? range_1.SourceRange.getStart(l) : range_1.SourcePosition.invalid()
65
65
  };
66
66
  });
67
67
  if (definitionStack.length > 0) {
@@ -112,7 +112,7 @@ function visitDefinitions(info, input) {
112
112
  const lexeme = node.info.fullLexeme;
113
113
  const lexemeSplit = lexeme?.split('\n');
114
114
  allDefinitions.push({
115
- location: (0, range_1.getRangeStart)(node.location),
115
+ location: range_1.SourceRange.getStart(node.location),
116
116
  callsites: retrieveAllCallsites(input, node, recursiveCalls),
117
117
  numberOfParameters: node.parameters.length,
118
118
  returns: returnTypes,