@eagleoutice/flowr 2.4.6 → 2.4.8

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 (107) hide show
  1. package/README.md +37 -33
  2. package/abstract-interpretation/data-frame/absint-visitor.js +3 -2
  3. package/abstract-interpretation/data-frame/dataframe-domain.d.ts +40 -0
  4. package/abstract-interpretation/data-frame/dataframe-domain.js +62 -0
  5. package/abstract-interpretation/data-frame/domain.js +1 -1
  6. package/abstract-interpretation/domains/abstract-domain.d.ts +56 -0
  7. package/abstract-interpretation/domains/abstract-domain.js +19 -0
  8. package/abstract-interpretation/domains/bounded-set-domain.d.ts +43 -0
  9. package/abstract-interpretation/domains/bounded-set-domain.js +121 -0
  10. package/abstract-interpretation/domains/interval-domain.d.ts +61 -0
  11. package/abstract-interpretation/domains/interval-domain.js +208 -0
  12. package/abstract-interpretation/domains/lattice.d.ts +62 -0
  13. package/abstract-interpretation/domains/lattice.js +12 -0
  14. package/abstract-interpretation/domains/positive-interval-domain.d.ts +32 -0
  15. package/abstract-interpretation/domains/positive-interval-domain.js +91 -0
  16. package/abstract-interpretation/domains/product-domain.d.ts +37 -0
  17. package/abstract-interpretation/domains/product-domain.js +133 -0
  18. package/abstract-interpretation/domains/set-bounded-set-domain.d.ts +43 -0
  19. package/abstract-interpretation/domains/set-bounded-set-domain.js +164 -0
  20. package/abstract-interpretation/domains/singleton-domain.d.ts +38 -0
  21. package/abstract-interpretation/domains/singleton-domain.js +115 -0
  22. package/abstract-interpretation/domains/state-abstract-domain.d.ts +32 -0
  23. package/abstract-interpretation/domains/state-abstract-domain.js +179 -0
  24. package/benchmark/slicer.js +1 -1
  25. package/benchmark/summarizer/first-phase/process.js +1 -1
  26. package/cli/repl/commands/repl-query.js +11 -2
  27. package/cli/repl/core.d.ts +2 -2
  28. package/cli/repl/core.js +26 -7
  29. package/cli/repl/server/connection.js +3 -1
  30. package/cli/repl/server/messages/message-slice.d.ts +3 -0
  31. package/cli/repl/server/messages/message-slice.js +2 -0
  32. package/control-flow/extract-cfg.d.ts +3 -3
  33. package/control-flow/extract-cfg.js +4 -4
  34. package/control-flow/useless-loop.js +30 -21
  35. package/dataflow/environments/built-in.d.ts +1 -1
  36. package/dataflow/environments/default-builtin-config.d.ts +9 -0
  37. package/dataflow/environments/default-builtin-config.js +21 -21
  38. package/dataflow/environments/environment.js +18 -9
  39. package/dataflow/environments/overwrite.js +2 -2
  40. package/dataflow/extractor.js +1 -1
  41. package/dataflow/graph/diff-dataflow-graph.js +4 -4
  42. package/dataflow/graph/graph.d.ts +3 -3
  43. package/dataflow/graph/graph.js +4 -1
  44. package/dataflow/graph/quads.js +4 -4
  45. package/dataflow/info.js +1 -1
  46. package/dataflow/internal/linker.d.ts +2 -0
  47. package/dataflow/internal/linker.js +18 -1
  48. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +3 -1
  49. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +68 -21
  50. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +1 -2
  51. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +4 -4
  52. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +5 -18
  53. package/dataflow/internal/process/functions/call/built-in/built-in-repeat-loop.js +1 -0
  54. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +4 -5
  55. package/dataflow/internal/process/functions/call/common.js +4 -3
  56. package/documentation/doc-util/doc-query.js +6 -2
  57. package/documentation/doc-util/doc-types.d.ts +7 -2
  58. package/documentation/doc-util/doc-types.js +20 -4
  59. package/documentation/print-core-wiki.js +5 -1
  60. package/documentation/print-dataflow-graph-wiki.js +21 -12
  61. package/documentation/print-faq-wiki.js +5 -0
  62. package/documentation/print-interface-wiki.js +2 -0
  63. package/documentation/print-linter-wiki.js +2 -3
  64. package/documentation/print-query-wiki.js +22 -7
  65. package/linter/linter-executor.js +25 -17
  66. package/linter/linter-format.d.ts +10 -1
  67. package/linter/linter-format.js +8 -0
  68. package/linter/linter-rules.d.ts +1 -0
  69. package/linter/rules/absolute-path.js +8 -8
  70. package/linter/rules/dataframe-access-validation.js +1 -1
  71. package/linter/rules/file-path-validity.js +8 -11
  72. package/linter/rules/naming-convention.d.ts +5 -1
  73. package/linter/rules/naming-convention.js +24 -8
  74. package/linter/rules/seeded-randomness.js +2 -2
  75. package/linter/rules/unused-definition.js +1 -1
  76. package/package.json +17 -15
  77. package/queries/catalog/call-context-query/call-context-query-executor.d.ts +5 -1
  78. package/queries/catalog/call-context-query/call-context-query-executor.js +14 -12
  79. package/queries/catalog/call-context-query/call-context-query-format.d.ts +6 -5
  80. package/queries/catalog/call-context-query/call-context-query-format.js +1 -1
  81. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +2 -1
  82. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +1 -1
  83. package/queries/catalog/config-query/config-query-executor.js +7 -1
  84. package/queries/catalog/config-query/config-query-format.d.ts +7 -0
  85. package/queries/catalog/config-query/config-query-format.js +72 -1
  86. package/queries/catalog/dependencies-query/dependencies-query-executor.js +50 -75
  87. package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +50 -26
  88. package/queries/catalog/dependencies-query/dependencies-query-format.js +75 -20
  89. package/queries/catalog/dependencies-query/function-info/function-info.d.ts +2 -2
  90. package/queries/catalog/dependencies-query/function-info/visualize-functions.d.ts +2 -0
  91. package/queries/catalog/dependencies-query/function-info/visualize-functions.js +13 -0
  92. package/queries/catalog/happens-before-query/happens-before-query-executor.js +1 -1
  93. package/queries/catalog/linter-query/linter-query-format.js +4 -0
  94. package/queries/query-print.d.ts +2 -2
  95. package/queries/query-print.js +3 -2
  96. package/queries/query.d.ts +28 -21
  97. package/search/flowr-search-builder.d.ts +1 -1
  98. package/search/flowr-search-builder.js +1 -1
  99. package/search/flowr-search-filters.d.ts +20 -10
  100. package/search/flowr-search-filters.js +19 -3
  101. package/search/search-executor/search-enrichers.d.ts +1 -1
  102. package/search/search-executor/search-enrichers.js +3 -2
  103. package/search/search-executor/search-generators.js +1 -1
  104. package/search/search-executor/search-transformer.js +1 -1
  105. package/util/objects.d.ts +11 -0
  106. package/util/objects.js +26 -0
  107. package/util/version.js +1 -1
@@ -5,22 +5,30 @@ const linter_rules_1 = require("./linter-rules");
5
5
  const flowr_search_executor_1 = require("../search/flowr-search-executor");
6
6
  const objects_1 = require("../util/objects");
7
7
  function executeLintingRule(ruleName, input, lintingRuleConfig) {
8
- const rule = linter_rules_1.LintingRules[ruleName];
9
- const fullConfig = (0, objects_1.deepMergeObject)(rule.info.defaultConfig, lintingRuleConfig);
10
- const ruleSearch = rule.createSearch(fullConfig, input);
11
- const searchStart = Date.now();
12
- const searchResult = (0, flowr_search_executor_1.runSearch)(ruleSearch, input);
13
- const searchTime = Date.now() - searchStart;
14
- const processStart = Date.now();
15
- const result = rule.processSearchResult(searchResult, fullConfig, input);
16
- const processTime = Date.now() - processStart;
17
- return {
18
- ...result,
19
- '.meta': {
20
- ...result['.meta'],
21
- searchTimeMs: searchTime,
22
- processTimeMs: processTime
23
- }
24
- };
8
+ try {
9
+ const rule = linter_rules_1.LintingRules[ruleName];
10
+ const fullConfig = (0, objects_1.deepMergeObject)(rule.info.defaultConfig, lintingRuleConfig);
11
+ const ruleSearch = rule.createSearch(fullConfig, input);
12
+ const searchStart = Date.now();
13
+ const searchResult = (0, flowr_search_executor_1.runSearch)(ruleSearch, input);
14
+ const searchTime = Date.now() - searchStart;
15
+ const processStart = Date.now();
16
+ const result = rule.processSearchResult(searchResult, fullConfig, input);
17
+ const processTime = Date.now() - processStart;
18
+ return {
19
+ ...result,
20
+ '.meta': {
21
+ ...result['.meta'],
22
+ searchTimeMs: searchTime,
23
+ processTimeMs: processTime
24
+ }
25
+ };
26
+ }
27
+ catch (e) {
28
+ const msg = typeof e === 'string' ? e : e instanceof Error ? e.message : JSON.stringify(e);
29
+ return {
30
+ error: msg
31
+ };
32
+ }
25
33
  }
26
34
  //# sourceMappingURL=linter-executor.js.map
@@ -105,13 +105,22 @@ export interface ConfiguredLintingRule<Name extends LintingRuleNames = LintingRu
105
105
  readonly name: Name;
106
106
  readonly config: DeepPartial<LintingRuleConfig<Name>>;
107
107
  }
108
- export interface LintingResults<Name extends LintingRuleNames> {
108
+ /**
109
+ * For when a linting rule throws an error during execution
110
+ */
111
+ export interface LintingResultsError {
112
+ readonly error: string;
113
+ }
114
+ export interface LintingResultsSuccess<Name extends LintingRuleNames> {
109
115
  results: LintingRuleResult<Name>[];
110
116
  '.meta': LintingRuleMetadata<Name> & {
111
117
  readonly searchTimeMs: number;
112
118
  readonly processTimeMs: number;
113
119
  };
114
120
  }
121
+ export declare function isLintingResultsError<Name extends LintingRuleNames>(o: LintingResults<Name>): o is LintingResultsError;
122
+ export declare function isLintingResultsSuccess<Name extends LintingRuleNames>(o: LintingResults<Name>): o is LintingResultsSuccess<Name>;
123
+ export type LintingResults<Name extends LintingRuleNames> = LintingResultsSuccess<Name> | LintingResultsError;
115
124
  export declare enum LintingResultCertainty {
116
125
  /**
117
126
  * The linting rule cannot say for sure whether the result is correct or not.
@@ -1,6 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LintingPrettyPrintContext = exports.LintingRuleCertainty = exports.LintingResultCertainty = void 0;
4
+ exports.isLintingResultsError = isLintingResultsError;
5
+ exports.isLintingResultsSuccess = isLintingResultsSuccess;
6
+ function isLintingResultsError(o) {
7
+ return 'error' in o;
8
+ }
9
+ function isLintingResultsSuccess(o) {
10
+ return 'results' in o;
11
+ }
4
12
  var LintingResultCertainty;
5
13
  (function (LintingResultCertainty) {
6
14
  /**
@@ -173,6 +173,7 @@ export declare const LintingRules: {
173
173
  readonly tags: readonly [import("./linter-tags").LintingRuleTag.Style, import("./linter-tags").LintingRuleTag.QuickFix];
174
174
  readonly defaultConfig: {
175
175
  readonly caseing: "auto";
176
+ readonly ignoreNonAlpha: true;
176
177
  };
177
178
  };
178
179
  };
@@ -52,8 +52,7 @@ const PathFunctions = {
52
52
  'file.path': (df, vtx, config) => {
53
53
  const fsep = (0, resolve_argument_1.getArgumentStringValue)(config.solver.variables, df, vtx, undefined, 'fsep', true);
54
54
  // in the future we can access `.Platform$file.sep` here
55
- const v = fsep?.values();
56
- const sepValues = v ? [...v].flatMap(s => [...s].filter(assert_1.isNotUndefined)) : [path_1.default.sep];
55
+ const sepValues = new Array(...fsep?.values()?.flatMap(s => [...s].filter(assert_1.isNotUndefined)) ?? [path_1.default.sep]);
57
56
  if (sepValues.some(s => s === dependencies_query_format_1.Unknown || (0, assert_1.isUndefined)(s))) {
58
57
  // if we have no fsep, we cannot construct a path
59
58
  return undefined;
@@ -119,19 +118,19 @@ exports.ABSOLUTE_PATH = {
119
118
  }
120
119
  else if ((0, search_enrichers_1.enrichmentContent)(element, search_enrichers_1.Enrichment.QueryData)) {
121
120
  const result = queryResults[(0, search_enrichers_1.enrichmentContent)(element, search_enrichers_1.Enrichment.QueryData).query];
122
- const mappedStrings = result.readData.filter(r => r.source !== dependencies_query_format_1.Unknown && (0, strings_1.isAbsolutePath)(r.source, regex)).map(r => {
121
+ const mappedStrings = result.read.filter(r => r.value !== undefined && r.value !== dependencies_query_format_1.Unknown && (0, strings_1.isAbsolutePath)(r.value, regex)).map(r => {
123
122
  const elem = data.normalize.idMap.get(r.nodeId);
124
123
  return {
125
124
  certainty: linter_format_1.LintingResultCertainty.Certain,
126
- filePath: r.source,
125
+ filePath: r.value,
127
126
  range: elem?.info.fullRange ?? elem?.location ?? (0, range_1.rangeFrom)(-1, -1, -1, -1),
128
- quickFix: buildQuickFix(elem, r.source, wd)
127
+ quickFix: buildQuickFix(elem, r.value, wd)
129
128
  };
130
129
  });
131
130
  if (mappedStrings.length > 0) {
132
131
  return mappedStrings;
133
132
  }
134
- else if (result.readData.every(r => r.source !== dependencies_query_format_1.Unknown)) {
133
+ else if (result.read.every(r => r.value !== dependencies_query_format_1.Unknown)) {
135
134
  // if we have no absolute paths, but all paths are known, we can return an empty array
136
135
  return [];
137
136
  }
@@ -145,7 +144,8 @@ exports.ABSOLUTE_PATH = {
145
144
  return strings.filter(s => (0, strings_1.isAbsolutePath)(s, regex)).map(str => ({
146
145
  certainty: linter_format_1.LintingResultCertainty.Uncertain,
147
146
  filePath: str,
148
- range: node.info.fullRange ?? node.location ?? (0, range_1.rangeFrom)(-1, -1, -1, -1)
147
+ range: node.info.fullRange ?? node.location ?? (0, range_1.rangeFrom)(-1, -1, -1, -1),
148
+ quickFix: undefined
149
149
  }));
150
150
  }
151
151
  }
@@ -159,7 +159,7 @@ exports.ABSOLUTE_PATH = {
159
159
  },
160
160
  prettyPrint: {
161
161
  [linter_format_1.LintingPrettyPrintContext.Query]: result => `Path \`${result.filePath}\` at ${(0, dfg_1.formatRange)(result.range)}`,
162
- [linter_format_1.LintingPrettyPrintContext.Full]: result => `Path \`${result.filePath}\` at ${(0, dfg_1.formatRange)(result.range)} is not absolute`
162
+ [linter_format_1.LintingPrettyPrintContext.Full]: result => `Path \`${result.filePath}\` at ${(0, dfg_1.formatRange)(result.range)} is absolute`
163
163
  },
164
164
  info: {
165
165
  name: 'Absolute Paths',
@@ -42,7 +42,7 @@ exports.DATA_FRAME_ACCESS_VALIDATION = {
42
42
  }
43
43
  accesses.push(access);
44
44
  }
45
- const operations = [...accessOperations.entries()].flatMap(([, operations]) => operations);
45
+ const operations = accessOperations.entries().flatMap(([, operations]) => operations).toArray();
46
46
  const metadata = {
47
47
  numOperations: accessOperations.size,
48
48
  numAccesses: operations.length,
@@ -8,18 +8,15 @@ const dependencies_query_format_1 = require("../../queries/catalog/dependencies-
8
8
  const built_in_source_1 = require("../../dataflow/internal/process/functions/call/built-in/built-in-source");
9
9
  const logic_1 = require("../../util/logic");
10
10
  const retriever_1 = require("../../r-bridge/retriever");
11
- const read_functions_1 = require("../../queries/catalog/dependencies-query/function-info/read-functions");
12
- const write_functions_1 = require("../../queries/catalog/dependencies-query/function-info/write-functions");
13
11
  const happens_before_1 = require("../../control-flow/happens-before");
14
12
  const linter_tags_1 = require("../linter-tags");
15
13
  const search_enrichers_1 = require("../../search/search-executor/search-enrichers");
16
14
  exports.FILE_PATH_VALIDITY = {
17
15
  createSearch: (config) => flowr_search_builder_1.Q.fromQuery({
18
16
  type: 'dependencies',
19
- // we only want to check read and write functions, so we explicitly clear all others
20
- ignoreDefaultFunctions: true,
21
- readFunctions: read_functions_1.ReadFunctions.concat(config.additionalReadFunctions),
22
- writeFunctions: write_functions_1.WriteFunctions.concat(config.additionalWriteFunctions)
17
+ enabledCategories: ['read', 'write'],
18
+ readFunctions: config.additionalReadFunctions,
19
+ writeFunctions: config.additionalWriteFunctions
23
20
  }).with(search_enrichers_1.Enrichment.CfgInformation),
24
21
  processSearchResult: (elements, config, data) => {
25
22
  const cfg = elements.enrichmentContent(search_enrichers_1.Enrichment.CfgInformation).cfg.graph;
@@ -32,14 +29,14 @@ exports.FILE_PATH_VALIDITY = {
32
29
  return {
33
30
  results: elements.getElements().flatMap(element => {
34
31
  const results = elements.enrichmentContent(search_enrichers_1.Enrichment.QueryData).queries['dependencies'];
35
- const matchingRead = results.readData.find(r => r.nodeId == element.node.info.id);
32
+ const matchingRead = results.read.find(r => r.nodeId == element.node.info.id);
36
33
  if (!matchingRead) {
37
34
  return [];
38
35
  }
39
36
  metadata.totalReads++;
40
37
  const range = element.node.info.fullRange;
41
38
  // check if we can't parse the file path statically
42
- if (matchingRead.source === dependencies_query_format_1.Unknown) {
39
+ if (matchingRead.value === dependencies_query_format_1.Unknown) {
43
40
  metadata.totalUnknown++;
44
41
  if (config.includeUnknown) {
45
42
  return [{
@@ -53,14 +50,14 @@ exports.FILE_PATH_VALIDITY = {
53
50
  }
54
51
  }
55
52
  // check if any write to the same file happens before the read, and exclude this case if so
56
- const writesToFile = results.writtenData.filter(r => samePath(r.destination, matchingRead.source, data.config.solver.resolveSource?.ignoreCapitalization));
53
+ const writesToFile = results.write.filter(r => samePath(r.value, matchingRead.value, data.config.solver.resolveSource?.ignoreCapitalization));
57
54
  const writesBefore = writesToFile.map(w => (0, happens_before_1.happensBefore)(cfg, w.nodeId, element.node.info.id));
58
55
  if (writesBefore.some(w => w === logic_1.Ternary.Always)) {
59
56
  metadata.totalWritesBeforeAlways++;
60
57
  return [];
61
58
  }
62
59
  // check if the file exists!
63
- const paths = (0, built_in_source_1.findSource)(data.config.solver.resolveSource, matchingRead.source, {
60
+ const paths = (0, built_in_source_1.findSource)(data.config.solver.resolveSource, matchingRead.value, {
64
61
  referenceChain: element.node.info.file ? [(0, retriever_1.requestFromInput)(`file://${element.node.info.file}`)] : []
65
62
  });
66
63
  if (paths && paths.length) {
@@ -69,7 +66,7 @@ exports.FILE_PATH_VALIDITY = {
69
66
  }
70
67
  return [{
71
68
  range,
72
- filePath: matchingRead.source,
69
+ filePath: matchingRead.value,
73
70
  certainty: writesBefore && writesBefore.length && writesBefore.every(w => w === logic_1.Ternary.Maybe) ? linter_format_1.LintingResultCertainty.Uncertain : linter_format_1.LintingResultCertainty.Certain
74
71
  }];
75
72
  }),
@@ -23,7 +23,10 @@ export interface NamingConventionResult extends LintingResult {
23
23
  * It is planned to have a config like ESLint
24
24
  */
25
25
  export interface NamingConventionConfig extends MergeableRecord {
26
+ /** which casing convention to enforce */
26
27
  caseing: CasingConvention | 'auto';
28
+ /** if true non alphabetic characters are ignored */
29
+ ignoreNonAlpha: boolean;
27
30
  }
28
31
  export interface NamingConventionMetadata extends MergeableRecord {
29
32
  /** number of symbols matching the casing convetion */
@@ -35,7 +38,7 @@ export declare function detectCasing(identifier: string): CasingConvention;
35
38
  export declare function getMostUsedCasing(symbols: {
36
39
  detectedCasing: CasingConvention;
37
40
  }[]): CasingConvention;
38
- export declare function fixCasing(identifier: string, convention: CasingConvention): string;
41
+ export declare function fixCasing(identifier: string, convention: CasingConvention): string | undefined;
39
42
  export declare function createNamingConventionQuickFixes(graph: DataflowGraph, nodeId: NodeId, replacement: string, conv: CasingConvention): LintQuickFixReplacement[] | undefined;
40
43
  export declare const NAMING_CONVENTION: {
41
44
  readonly createSearch: (_config: NamingConventionConfig) => import("../../search/flowr-search-builder").FlowrSearchBuilder<"all", ["filter"], import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation, 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>[]>>;
@@ -67,6 +70,7 @@ export declare const NAMING_CONVENTION: {
67
70
  readonly tags: readonly [LintingRuleTag.Style, LintingRuleTag.QuickFix];
68
71
  readonly defaultConfig: {
69
72
  readonly caseing: "auto";
73
+ readonly ignoreNonAlpha: true;
70
74
  };
71
75
  };
72
76
  };
@@ -22,8 +22,11 @@ var CasingConvention;
22
22
  CasingConvention["PascalSnakeCase"] = "Pascal_Snake_Case";
23
23
  CasingConvention["Unknown"] = "unknown";
24
24
  })(CasingConvention || (exports.CasingConvention = CasingConvention = {}));
25
+ function containsAlpha(s) {
26
+ return /[A-Za-z]/.test(s);
27
+ }
25
28
  function detectCasing(identifier) {
26
- if (identifier.trim() === '') {
29
+ if (identifier.trim() === '' || !containsAlpha(identifier)) {
27
30
  return CasingConvention.Unknown;
28
31
  }
29
32
  const upper = identifier.toUpperCase();
@@ -78,8 +81,16 @@ function getMostUsedCasing(symbols) {
78
81
  return [...map].reduce((p, c) => p[1] > c[1] ? p : c)[0];
79
82
  }
80
83
  function fixCasing(identifier, convention) {
84
+ if (!containsAlpha(identifier)) {
85
+ return undefined;
86
+ }
81
87
  const tokens = identifier.split(/(?=[A-Z])|_/).map(s => s.toLowerCase());
82
- const firstUp = (s) => `${s[0].toUpperCase()}${s.substring(1)}`;
88
+ const firstUp = (s) => {
89
+ if (s.length < 1) {
90
+ return s.toUpperCase();
91
+ }
92
+ return `${s[0].toUpperCase()}${s.substring(1)}`;
93
+ };
83
94
  switch (convention) {
84
95
  case CasingConvention.CamelCase: // camelCase
85
96
  return `${tokens[0]}${tokens.slice(1).map(firstUp).join('')}`;
@@ -139,11 +150,15 @@ exports.NAMING_CONVENTION = {
139
150
  id: m.node.info.id
140
151
  }));
141
152
  const casing = config.caseing === 'auto' ? getMostUsedCasing(symbols) : config.caseing;
142
- const results = symbols.filter(m => m.detectedCasing !== casing)
143
- .map(({ id, ...m }) => ({
144
- ...m,
145
- quickFix: createNamingConventionQuickFixes(data.dataflow.graph, id, fixCasing(m.name, casing), casing)
146
- }));
153
+ const results = symbols
154
+ .filter(m => (m.detectedCasing !== casing) && (!config.ignoreNonAlpha || containsAlpha(m.name)))
155
+ .map(({ id, ...m }) => {
156
+ const fix = fixCasing(m.name, casing);
157
+ return {
158
+ ...m,
159
+ quickFix: fix ? createNamingConventionQuickFixes(data.dataflow.graph, id, fix, casing) : undefined
160
+ };
161
+ });
147
162
  return {
148
163
  results: results,
149
164
  '.meta': {
@@ -163,7 +178,8 @@ exports.NAMING_CONVENTION = {
163
178
  description: 'Checks wether the symbols conform to a certain naming convention',
164
179
  tags: [linter_tags_1.LintingRuleTag.Style, linter_tags_1.LintingRuleTag.QuickFix],
165
180
  defaultConfig: {
166
- caseing: 'auto'
181
+ caseing: 'auto',
182
+ ignoreNonAlpha: true
167
183
  }
168
184
  }
169
185
  };
@@ -27,8 +27,8 @@ exports.SEEDED_RANDOMNESS = {
27
27
  }
28
28
  })
29
29
  .with(search_enrichers_1.Enrichment.LastCall, [
30
- ...config.randomnessProducers.filter(p => p.type === 'function').map(p => ({ callName: p.name })),
31
- ...getDefaultAssignments().flatMap(b => b.names).map(a => ({ callName: a, cascadeIf: () => cascade_action_1.CascadeAction.Continue }))
30
+ { callName: config.randomnessProducers.filter(p => p.type === 'function').map(p => p.name) },
31
+ { callName: getDefaultAssignments().flatMap(b => b.names), cascadeIf: () => cascade_action_1.CascadeAction.Continue }
32
32
  ]),
33
33
  processSearchResult: (elements, config, { dataflow }) => {
34
34
  const assignmentProducers = new Set(config.randomnessProducers.filter(p => p.type == 'assignment').map(p => p.name));
@@ -73,7 +73,7 @@ exports.UNUSED_DEFINITION = {
73
73
  }
74
74
  const ingoingEdges = data.dataflow.graph.ingoingEdges(dfgVertex.id);
75
75
  const interestedIn = (0, vertex_1.isVariableDefinitionVertex)(dfgVertex) ? InterestingEdgesVariable : InterestingEdgesFunction;
76
- const ingoingInteresting = [...ingoingEdges?.values() ?? []].some(e => (0, edge_1.edgeIncludesType)(e.types, interestedIn));
76
+ const ingoingInteresting = ingoingEdges?.values().some(e => (0, edge_1.edgeIncludesType)(e.types, interestedIn));
77
77
  if (ingoingInteresting) {
78
78
  return undefined;
79
79
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.4.6",
3
+ "version": "2.4.8",
4
4
  "description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "repository": {
@@ -15,7 +15,7 @@
15
15
  "setup:dev": "git lfs fetch && npm ci && git config --local core.hooksPath .githooks/ && git push --dry-run",
16
16
  "main": "npm run build:bundle-flowr && node dist/src/cli/flowr.min.js",
17
17
  "flowr": "npm run main --",
18
- "main-dev": "ts-node src/cli/flowr.ts",
18
+ "main-dev": "ts-node-dev src/cli/flowr.ts",
19
19
  "publish-library": "cp .npmignore package.json README.md LICENSE dist/src/ && cd dist/src && npm publish --access public",
20
20
  "release": "npx release-it --ci",
21
21
  "stats": "ts-node src/cli/statistics-app.ts",
@@ -51,8 +51,9 @@
51
51
  "test": "vitest --exclude \"test/system-tests/**\" --config test/vitest.config.mts",
52
52
  "test:system": "vitest --dir test/system-tests --config test/system-tests/vitest.config.mts",
53
53
  "test:coverage": "npm run test -- --coverage",
54
- "performance-test": "func() { cd test/performance/ && bash run-all-suites.sh $1 $2 $3 $4; cd ../../; }; func",
54
+ "test:full": "npm run test-full",
55
55
  "test-full": "npm run test:coverage -- --no-watch -- --make-summary --test-installation",
56
+ "performance-test": "func() { cd test/performance/ && bash run-all-suites.sh $1 $2 $3 $4; cd ../../; }; func",
56
57
  "detect-circular-deps": "npx madge --extensions ts,tsx --circular src/",
57
58
  "checkup": "npm run flowr -- --execute \":version\" && npm run lint && npm run test-full -- --allowOnly=false && npm run test:system -- --no-watch && docker build -t test-flowr -f scripts/Dockerfile . && npm run doc && npm run gen:readme && npm-run-all wiki:*"
58
59
  },
@@ -177,32 +178,33 @@
177
178
  "@commitlint/cli": "^19.7.1",
178
179
  "@commitlint/config-angular": "^19.7.1",
179
180
  "@eagleoutice/eslint-config-flowr": "^1.0.19",
180
- "@eslint/eslintrc": "^3.2.0",
181
- "@eslint/js": "^9.20.0",
181
+ "@eslint/eslintrc": "^3.3.1",
182
+ "@eslint/js": "^9.34.0",
182
183
  "@j-ulrich/release-it-regex-bumper": "^5.3.0",
183
184
  "@types/command-line-args": "^5.2.3",
184
185
  "@types/command-line-usage": "^5.0.4",
185
186
  "@types/n-readlines": "^1.0.6",
186
- "@types/n3": "^1.21.1",
187
+ "@types/n3": "^1.26.0",
187
188
  "@types/object-hash": "^3.0.6",
188
189
  "@types/seedrandom": "^3.0.8",
189
- "@types/semver": "^7.5.8",
190
+ "@types/semver": "^7.7.0",
190
191
  "@types/tmp": "^0.2.6",
191
- "@types/ws": "^8.5.14",
192
- "@typescript-eslint/eslint-plugin": "^8.24.0",
193
- "@vitest/coverage-v8": "^3.0.6",
194
- "esbuild": "^0.25.0",
195
- "eslint": "^9.20.1",
192
+ "@types/ws": "^8.18.1",
193
+ "@typescript-eslint/eslint-plugin": "^8.40.0",
194
+ "@vitest/coverage-v8": "^3.2.4",
195
+ "esbuild": "^0.25.9",
196
+ "eslint": "^9.34.0",
196
197
  "license-checker": "^25.0.1",
197
198
  "npm-run-all": "^4.1.5",
198
199
  "release-it": "^19.0.2",
199
200
  "ts-node": "^10.9.2",
201
+ "ts-node-dev": "^2.0.0",
200
202
  "typedoc": "^0.27.7",
201
203
  "typedoc-plugin-missing-exports": "^3.1.0",
202
204
  "typedoc-theme-hierarchy": "^5.0.4",
203
205
  "typedoc-umlclass": "^0.10.1",
204
206
  "typescript": "^5.7.3",
205
- "vitest": "^3.0.6"
207
+ "vitest": "^3.2.4"
206
208
  },
207
209
  "dependencies": {
208
210
  "@eagleoutice/tree-sitter-r": "^1.1.2",
@@ -210,7 +212,7 @@
210
212
  "clipboardy": "^4.0.0",
211
213
  "command-line-args": "^6.0.1",
212
214
  "command-line-usage": "^7.0.3",
213
- "joi": "^17.13.3",
215
+ "joi": "^18.0.1",
214
216
  "lz-string": "^1.5.0",
215
217
  "n-readlines": "^1.0.1",
216
218
  "n3": "^1.23.1",
@@ -221,7 +223,7 @@
221
223
  "semver": "^7.7.1",
222
224
  "tar": "^7.4.3",
223
225
  "tmp": "^0.2.3",
224
- "ts-essentials": "^10.0.4",
226
+ "ts-essentials": "^10.1.1",
225
227
  "tslog": "^4.9.3",
226
228
  "web-tree-sitter": "^0.24.7",
227
229
  "ws": "^8.18.0",
@@ -1,5 +1,9 @@
1
- import type { CallContextQuery, CallContextQueryResult } from './call-context-query-format';
1
+ import type { CallContextQuery, CallContextQueryResult, CallNameTypes, LinkTo } from './call-context-query-format';
2
2
  import type { BasicQueryData } from '../../base-query-format';
3
+ export declare function promoteCallName(callName: CallNameTypes, exact?: boolean): RegExp | Set<string>;
4
+ export type PromotedLinkTo = Omit<LinkTo, 'callName'> & {
5
+ callName: RegExp | Set<string>;
6
+ };
3
7
  /**
4
8
  * Multi-stage call context query resolve.
5
9
  *
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.promoteCallName = promoteCallName;
3
4
  exports.executeCallContextQueries = executeCallContextQueries;
4
5
  const node_id_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/node-id");
5
6
  const vertex_1 = require("../../../dataflow/graph/vertex");
@@ -42,6 +43,9 @@ function isSubCallQuery(query) {
42
43
  function exactCallNameRegex(name) {
43
44
  return new RegExp(`^(${name})$`);
44
45
  }
46
+ function promoteCallName(callName, exact = false) {
47
+ return Array.isArray(callName) ? new Set(callName) : exact ? exactCallNameRegex(callName) : new RegExp(callName);
48
+ }
45
49
  function promoteQueryCallNames(queries) {
46
50
  let requiresCfg = false;
47
51
  const promotedQueries = queries.map(q => {
@@ -49,30 +53,28 @@ function promoteQueryCallNames(queries) {
49
53
  requiresCfg = true;
50
54
  return {
51
55
  ...q,
52
- callName: q.callNameExact ? exactCallNameRegex(q.callName)
53
- : new RegExp(q.callName),
56
+ callName: promoteCallName(q.callName, q.callNameExact),
54
57
  fileFilter: q.fileFilter && {
55
58
  ...q.fileFilter,
56
- filter: new RegExp(q.fileFilter.filter)
59
+ filter: promoteCallName(q.fileFilter.filter)
57
60
  },
58
61
  linkTo: Array.isArray(q.linkTo) ? q.linkTo.map(l => ({
59
62
  ...l,
60
- callName: new RegExp(l.callName)
63
+ callName: promoteCallName(l.callName)
61
64
  })) : {
62
65
  ...q.linkTo,
63
66
  /* we have to add another promotion layer whenever we add something without this call name */
64
- callName: new RegExp(q.linkTo.callName)
67
+ callName: promoteCallName(q.linkTo.callName)
65
68
  }
66
69
  };
67
70
  }
68
71
  else {
69
72
  return {
70
73
  ...q,
71
- callName: q.callNameExact ? exactCallNameRegex(q.callName)
72
- : new RegExp(q.callName),
74
+ callName: promoteCallName(q.callName, q.callNameExact),
73
75
  fileFilter: q.fileFilter && {
74
76
  ...q.fileFilter,
75
- filter: new RegExp(q.fileFilter.filter)
77
+ filter: promoteCallName(q.fileFilter.filter)
76
78
  }
77
79
  };
78
80
  }
@@ -150,7 +152,7 @@ function doesFilepathMatch(file, filter) {
150
152
  if (file === undefined) {
151
153
  return filter.includeUndefinedFiles ?? true;
152
154
  }
153
- return filter.filter.test(file);
155
+ return filter.filter instanceof RegExp ? filter.filter.test(file) : filter.filter.has(file);
154
156
  }
155
157
  function isParameterDefaultValue(nodeId, ast) {
156
158
  let node = ast.idMap.get(nodeId);
@@ -197,13 +199,13 @@ function executeCallContextQueries({ dataflow: { graph }, ast, config }, queries
197
199
  const targets = retrieveAllCallAliases(nodeId, graph);
198
200
  for (const [l, ids] of targets.entries()) {
199
201
  for (const query of queriesWhichWantAliases) {
200
- if (query.callName.test(l)) {
202
+ if (query.callName instanceof RegExp ? query.callName.test(l) : query.callName.has(l)) {
201
203
  initialIdCollector.add(query.kind ?? '.', query.subkind ?? '.', (0, objects_1.compactRecord)({ id: nodeId, name: info.name, aliasRoots: ids }));
202
204
  }
203
205
  }
204
206
  }
205
207
  }
206
- for (const query of promotedQueries.filter(q => !q.includeAliases && q.callName.test(info.name))) {
208
+ for (const query of promotedQueries.filter(q => !q.includeAliases && (q.callName instanceof RegExp ? q.callName.test(info.name) : q.callName.has(info.name)))) {
207
209
  const file = ast.idMap.get(nodeId)?.info.file;
208
210
  if (!doesFilepathMatch(file, query.fileFilter)) {
209
211
  continue;
@@ -223,7 +225,7 @@ function executeCallContextQueries({ dataflow: { graph }, ast, config }, queries
223
225
  continue;
224
226
  }
225
227
  let linkedIds = undefined;
226
- if (cfg && isSubCallQuery(query)) {
228
+ if (cfg && 'linkTo' in query && query.linkTo !== undefined) {
227
229
  const linked = Array.isArray(query.linkTo) ? query.linkTo : [query.linkTo];
228
230
  for (const link of linked) {
229
231
  /* if we have a linkTo query, we have to find the last call */
@@ -20,7 +20,7 @@ export interface FileFilter<FilterType> {
20
20
  */
21
21
  readonly includeUndefinedFiles?: boolean;
22
22
  }
23
- export interface DefaultCallContextQueryFormat<RegexType extends RegExp | string> extends BaseQueryFormat {
23
+ export interface DefaultCallContextQueryFormat<RegexType extends CallNameTypes> extends BaseQueryFormat {
24
24
  readonly type: 'call-context';
25
25
  /** Regex regarding the function name, please note that strings will be interpreted as regular expressions too! */
26
26
  readonly callName: RegexType;
@@ -50,13 +50,14 @@ export interface DefaultCallContextQueryFormat<RegexType extends RegExp | string
50
50
  */
51
51
  readonly fileFilter?: FileFilter<RegexType>;
52
52
  }
53
+ export type CallNameTypes = RegExp | string | string[];
53
54
  /**
54
55
  * Links the current call to the last call of the given kind.
55
56
  * This way, you can link a call like `points` to the latest graphics plot etc.
56
57
  * For now, this uses the static Control-Flow-Graph produced by flowR as the FD over-approximation is still not stable (see #1005).
57
58
  * In short, this means that we are unable to detect origins over function call boundaries but plan on being more precise in the future.
58
59
  */
59
- export interface LinkToLastCall<CallName extends RegExp | string = RegExp | string> extends BaseQueryFormat {
60
+ export interface LinkToLastCall<CallName extends CallNameTypes = CallNameTypes> extends BaseQueryFormat {
60
61
  readonly type: 'link-to-last-call';
61
62
  /** Regex regarding the function name of the last call. Similar to {@link DefaultCallContextQueryFormat#callName}, strings are interpreted as a `RegExp`. */
62
63
  readonly callName: CallName;
@@ -71,10 +72,10 @@ export interface LinkToLastCall<CallName extends RegExp | string = RegExp | stri
71
72
  */
72
73
  readonly cascadeIf?: (target: DataflowGraphVertexInfo, from: NodeId, graph: DataflowGraph) => CascadeAction;
73
74
  }
74
- export type LinkTo<CallName extends RegExp | string = RegExp | string, AttachLinkInfo = NoInfo> = (LinkToLastCall<CallName>) & {
75
+ export type LinkTo<CallName extends CallNameTypes = CallNameTypes, AttachLinkInfo = NoInfo> = (LinkToLastCall<CallName>) & {
75
76
  attachLinkInfo?: AttachLinkInfo;
76
77
  };
77
- export interface SubCallContextQueryFormat<CallName extends RegExp | string = RegExp | string, AttachLinkInfo = NoInfo> extends DefaultCallContextQueryFormat<CallName> {
78
+ export interface SubCallContextQueryFormat<CallName extends CallNameTypes = CallNameTypes, AttachLinkInfo = NoInfo> extends DefaultCallContextQueryFormat<CallName> {
78
79
  readonly linkTo: LinkTo<CallName, AttachLinkInfo> | LinkTo<CallName, AttachLinkInfo>[];
79
80
  }
80
81
  export interface CallContextQuerySubKindResult {
@@ -105,7 +106,7 @@ export type CallContextQueryKindResult = Record<string, {
105
106
  export interface CallContextQueryResult extends BaseQueryResult {
106
107
  readonly kinds: CallContextQueryKindResult;
107
108
  }
108
- export type CallContextQuery<CallName extends RegExp | string = RegExp | string, AttachLinkInfo = NoInfo> = DefaultCallContextQueryFormat<CallName> | SubCallContextQueryFormat<CallName, AttachLinkInfo>;
109
+ export type CallContextQuery<CallName extends CallNameTypes = CallNameTypes, AttachLinkInfo = NoInfo> = DefaultCallContextQueryFormat<CallName> | SubCallContextQueryFormat<CallName, AttachLinkInfo>;
109
110
  export declare const CallContextQueryDefinition: {
110
111
  readonly executor: typeof executeCallContextQueries;
111
112
  readonly asciiSummarizer: (formatter: OutputFormatter, processed: PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>, queryResults: BaseQueryResult, result: string[]) => boolean;
@@ -12,7 +12,7 @@ const query_print_1 = require("../../query-print");
12
12
  const identify_link_to_last_call_relation_1 = require("./identify-link-to-last-call-relation");
13
13
  const CallContextQueryLinkTo = joi_1.default.object({
14
14
  type: joi_1.default.string().valid('link-to-last-call').required().description('The type of the linkTo sub-query.'),
15
- callName: joi_1.default.string().required().description('Regex regarding the function name of the last call. Similar to `callName`, strings are interpreted as a regular expression.'),
15
+ callName: joi_1.default.alternatives(joi_1.default.string(), joi_1.default.array().items(joi_1.default.string())).required().description('Test regarding the function name of the last call. Similar to `callName`, strings are interpreted as a regular expression, and string arrays are checked for containment.'),
16
16
  ignoreIf: joi_1.default.function().optional().description('Should we ignore this (source) call? Currently, there is no well working serialization for this.'),
17
17
  cascadeIf: joi_1.default.function().optional().description('Should we continue searching after the link was created? Currently, there is no well working serialization for this.'),
18
18
  attachLinkInfo: joi_1.default.object().optional().description('Additional information to attach to the link.')
@@ -5,6 +5,7 @@ import { RType } from '../../../r-bridge/lang-4.x/ast/model/type';
5
5
  import type { RNodeWithParent } from '../../../r-bridge/lang-4.x/ast/model/processing/decorate';
6
6
  import type { LinkTo } from './call-context-query-format';
7
7
  import type { ControlFlowGraph } from '../../../control-flow/control-flow-graph';
8
+ import type { PromotedLinkTo } from './call-context-query-executor';
8
9
  export declare enum CallTargets {
9
10
  /** call targets a function that is not defined locally in the script (e.g., the call targets a library function) */
10
11
  OnlyGlobal = "global",
@@ -24,4 +25,4 @@ export declare function getValueOfArgument<Types extends readonly RType[] = read
24
25
  }, additionalAllowedTypes?: Types): (RNodeWithParent & {
25
26
  type: Types[number];
26
27
  }) | undefined;
27
- export declare function identifyLinkToLastCallRelation(from: NodeId, cfg: ControlFlowGraph, graph: DataflowGraph, { callName, ignoreIf, cascadeIf }: LinkTo<RegExp>): NodeId[];
28
+ export declare function identifyLinkToLastCallRelation(from: NodeId, cfg: ControlFlowGraph, graph: DataflowGraph, { callName, ignoreIf, cascadeIf }: LinkTo<RegExp> | PromotedLinkTo): NodeId[];
@@ -121,7 +121,7 @@ function identifyLinkToLastCallRelation(from, cfg, graph, { callName, ignoreIf,
121
121
  if (vertex === undefined || vertex.tag !== vertex_1.VertexType.FunctionCall) {
122
122
  return;
123
123
  }
124
- if (callName.test(vertex.name)) {
124
+ if (callName instanceof RegExp ? callName.test(vertex.name) : callName.has(vertex.name)) {
125
125
  const act = cascadeIf ? cascadeIf(vertex, from, graph) : cascade_action_1.CascadeAction.Stop;
126
126
  if (act === cascade_action_1.CascadeAction.Skip) {
127
127
  return;
@@ -2,9 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.executeConfigQuery = executeConfigQuery;
4
4
  const log_1 = require("../../../util/log");
5
+ const assert_1 = require("../../../util/assert");
6
+ const objects_1 = require("../../../util/objects");
5
7
  function executeConfigQuery({ config }, queries) {
6
8
  if (queries.length !== 1) {
7
- log_1.log.warn('Config query expects only up to one query, but got', queries.length);
9
+ log_1.log.warn('Config query usually expects only up to one query, but got', queries.length);
10
+ }
11
+ const updates = queries.map(q => q.update).filter(assert_1.isNotUndefined);
12
+ for (const update of updates) {
13
+ (0, objects_1.deepMergeObjectInPlace)(config, update);
8
14
  }
9
15
  return {
10
16
  '.meta': {