@eagleoutice/flowr 2.4.7 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +48 -38
  2. package/abstract-interpretation/data-frame/absint-visitor.js +3 -2
  3. package/benchmark/slicer.js +2 -2
  4. package/benchmark/summarizer/first-phase/process.js +1 -1
  5. package/benchmark/summarizer/second-phase/graph.js +2 -2
  6. package/cli/repl/commands/repl-query.js +11 -2
  7. package/cli/repl/core.d.ts +2 -2
  8. package/cli/repl/core.js +26 -7
  9. package/cli/repl/server/connection.js +3 -1
  10. package/cli/repl/server/messages/message-slice.d.ts +3 -0
  11. package/cli/repl/server/messages/message-slice.js +2 -0
  12. package/cli/slicer-app.js +7 -2
  13. package/control-flow/extract-cfg.d.ts +3 -3
  14. package/control-flow/extract-cfg.js +4 -4
  15. package/control-flow/useless-loop.js +30 -21
  16. package/dataflow/environments/built-in.d.ts +1 -1
  17. package/dataflow/environments/default-builtin-config.d.ts +9 -0
  18. package/dataflow/environments/default-builtin-config.js +21 -21
  19. package/dataflow/environments/environment.js +18 -9
  20. package/dataflow/environments/overwrite.js +2 -2
  21. package/dataflow/extractor.js +1 -1
  22. package/dataflow/graph/diff-dataflow-graph.js +4 -4
  23. package/dataflow/graph/graph.d.ts +3 -3
  24. package/dataflow/graph/graph.js +4 -1
  25. package/dataflow/graph/quads.js +4 -4
  26. package/dataflow/info.js +1 -1
  27. package/dataflow/internal/linker.d.ts +2 -0
  28. package/dataflow/internal/linker.js +18 -1
  29. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +3 -1
  30. package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +68 -21
  31. package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +1 -2
  32. package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +4 -4
  33. package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +5 -18
  34. package/dataflow/internal/process/functions/call/built-in/built-in-repeat-loop.js +1 -0
  35. package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +4 -5
  36. package/dataflow/internal/process/functions/call/common.js +4 -3
  37. package/documentation/doc-util/doc-query.js +6 -2
  38. package/documentation/doc-util/doc-types.d.ts +7 -2
  39. package/documentation/doc-util/doc-types.js +20 -4
  40. package/documentation/print-core-wiki.js +5 -1
  41. package/documentation/print-dataflow-graph-wiki.js +21 -12
  42. package/documentation/print-faq-wiki.js +5 -0
  43. package/documentation/print-interface-wiki.js +2 -0
  44. package/documentation/print-linter-wiki.js +2 -3
  45. package/documentation/print-linting-and-testing-wiki.js +4 -0
  46. package/documentation/print-query-wiki.js +22 -7
  47. package/documentation/print-readme.js +6 -0
  48. package/linter/linter-executor.js +25 -17
  49. package/linter/linter-format.d.ts +10 -1
  50. package/linter/linter-format.js +8 -0
  51. package/linter/linter-rules.d.ts +1 -0
  52. package/linter/rules/absolute-path.js +8 -8
  53. package/linter/rules/dataframe-access-validation.js +1 -1
  54. package/linter/rules/file-path-validity.js +8 -11
  55. package/linter/rules/naming-convention.d.ts +5 -1
  56. package/linter/rules/naming-convention.js +24 -8
  57. package/linter/rules/seeded-randomness.js +2 -2
  58. package/linter/rules/unused-definition.js +1 -1
  59. package/package.json +20 -15
  60. package/queries/catalog/call-context-query/call-context-query-executor.d.ts +5 -1
  61. package/queries/catalog/call-context-query/call-context-query-executor.js +14 -12
  62. package/queries/catalog/call-context-query/call-context-query-format.d.ts +6 -5
  63. package/queries/catalog/call-context-query/call-context-query-format.js +1 -1
  64. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +2 -1
  65. package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +1 -1
  66. package/queries/catalog/config-query/config-query-executor.js +7 -1
  67. package/queries/catalog/config-query/config-query-format.d.ts +7 -0
  68. package/queries/catalog/config-query/config-query-format.js +72 -1
  69. package/queries/catalog/dependencies-query/dependencies-query-executor.js +50 -75
  70. package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +50 -26
  71. package/queries/catalog/dependencies-query/dependencies-query-format.js +75 -20
  72. package/queries/catalog/dependencies-query/function-info/function-info.d.ts +2 -2
  73. package/queries/catalog/dependencies-query/function-info/visualize-functions.d.ts +2 -0
  74. package/queries/catalog/dependencies-query/function-info/visualize-functions.js +13 -0
  75. package/queries/catalog/happens-before-query/happens-before-query-executor.js +1 -1
  76. package/queries/catalog/linter-query/linter-query-format.js +4 -0
  77. package/queries/query-print.d.ts +2 -2
  78. package/queries/query-print.js +3 -2
  79. package/queries/query.d.ts +28 -21
  80. package/r-bridge/lang-4.x/tree-sitter/tree-sitter-executor.js +1 -1
  81. package/r-bridge/retriever.d.ts +14 -2
  82. package/r-bridge/retriever.js +10 -4
  83. package/search/flowr-search-builder.d.ts +1 -1
  84. package/search/flowr-search-builder.js +1 -1
  85. package/search/flowr-search-filters.d.ts +20 -10
  86. package/search/flowr-search-filters.js +19 -3
  87. package/search/search-executor/search-enrichers.d.ts +1 -1
  88. package/search/search-executor/search-enrichers.js +3 -2
  89. package/search/search-executor/search-generators.js +1 -1
  90. package/search/search-executor/search-transformer.js +1 -1
  91. package/util/formats/adapter-format.d.ts +6 -0
  92. package/util/formats/adapter-format.js +3 -0
  93. package/util/formats/adapter.d.ts +16 -0
  94. package/util/formats/adapter.js +42 -0
  95. package/util/formats/adapters/r-adapter.d.ts +4 -0
  96. package/util/formats/adapters/r-adapter.js +7 -0
  97. package/util/formats/adapters/rmd-adapter.d.ts +26 -0
  98. package/util/formats/adapters/rmd-adapter.js +91 -0
  99. package/util/objects.d.ts +11 -0
  100. package/util/objects.js +26 -0
  101. package/util/version.js +1 -1
@@ -38,6 +38,7 @@ const control_flow_query_executor_1 = require("../queries/catalog/control-flow-q
38
38
  const doc_cfg_1 = require("./doc-util/doc-cfg");
39
39
  const df_shape_query_executor_1 = require("../queries/catalog/df-shape-query/df-shape-query-executor");
40
40
  const _00_slice_1 = require("../core/steps/all/static-slicing/00-slice");
41
+ const doc_repl_1 = require("./doc-util/doc-repl");
41
42
  (0, doc_query_1.registerQueryDocumentation)('call-context', {
42
43
  name: 'Call-Context Query',
43
44
  type: 'active',
@@ -329,10 +330,26 @@ ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
329
330
  shortDescription: 'Returns the current configuration of flowR.',
330
331
  functionName: config_query_executor_1.executeConfigQuery.name,
331
332
  functionFile: '../queries/catalog/config-query/config-query-format.ts',
332
- // eslint-disable-next-line @typescript-eslint/require-await -- no need for async here
333
- buildExplanation: async () => {
333
+ buildExplanation: async (shell) => {
334
334
  return `
335
- This query provides access to the current configuration of the flowR instance. See the [Interface](${doc_files_1.FlowrWikiBaseRef}/Interface) wiki page for more information on what the configuration represents.`;
335
+ This query provides access to the current configuration of the flowR instance. See the [Interface](${doc_files_1.FlowrWikiBaseRef}/Interface) wiki page for more information on what the configuration represents.
336
+ Additionally, you can use this query to update the configuration of flowR on-the-fly (please do not rely on this mechanism it is mostly of interest for demonstrations).
337
+ ${await (0, doc_query_1.showQuery)(shell, '', [{
338
+ type: 'config',
339
+ update: {
340
+ ignoreSourceCalls: true
341
+ }
342
+ }], { showCode: false, collapseQuery: true, collapseResult: true })}
343
+
344
+ Please note that, in the repl, a special syntax starting with \`+\` (which should be autocompleted) can be used to update the configuration on the fly:
345
+
346
+ ${await (0, doc_repl_1.documentReplSession)(shell, [
347
+ {
348
+ command: ':query @config +solver.slicer.threshold=10000',
349
+ description: 'Set the slicing threshold to 10,000.'
350
+ }
351
+ ])}
352
+ `;
336
353
  }
337
354
  });
338
355
  (0, doc_query_1.registerQueryDocumentation)('df-shape', {
@@ -495,10 +512,8 @@ In the meantime we offer several properties to overwrite the default behavior (e
495
512
  ${await (0, doc_query_1.showQuery)(shell, longerCode, [{
496
513
  type: 'dependencies',
497
514
  ignoreDefaultFunctions: true,
515
+ enabledCategories: ['library'],
498
516
  libraryFunctions: [{ package: 'base', name: 'print', argIdx: 0, argName: 'library', resolveValue: true }],
499
- sourceFunctions: [],
500
- readFunctions: [],
501
- writeFunctions: []
502
517
  }], { showCode: false, collapseQuery: false, collapseResult: true })}
503
518
 
504
519
  Here, \`resolveValue\` tells the dependency query to resolve the value of this argument in case it is not a constant.
@@ -651,7 +666,7 @@ ${(0, doc_query_1.tocForQueryType)('virtual')}
651
666
 
652
667
  <summary>Detailed Query Format (Automatically Generated)</summary>
653
668
 
654
- Although it is probably better to consult the detailed explanations below, if you want to have a look at the scehma, here is its description:
669
+ Although it is probably better to consult the detailed explanations below, if you want to have a look at the schema, here is its description:
655
670
 
656
671
  ${(0, schema_1.describeSchema)((0, query_1.QueriesSchema)(), ansi_1.markdownFormatter)}
657
672
 
@@ -142,6 +142,12 @@ You can enter ${(0, doc_cli_option_1.getReplCommand)('help')} to gain more infor
142
142
 
143
143
  ![Example of a simple REPL session](wiki/gif/repl-demo-opt.gif)
144
144
 
145
+ If you want to use the same commands:
146
+
147
+ 1. First this runs \`docker run -it --rm eagleoutice/flowr\` in a terminal to start the REPL.
148
+ 2. In the REPL, it runs \`:slicer -c '11@prod' demo.R --diff\` to slice the example file \`demo.R\` for the print statement in line 11.
149
+ Please note that the \`11\` refers to the 11th line number to slice for!
150
+
145
151
  </details>
146
152
 
147
153
  ## 📜 More Information
@@ -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.7",
3
+ "version": "2.5.0",
4
4
  "description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "repository": {
@@ -15,7 +15,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,34 @@
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",
186
+ "@types/commonmark": "^0.27.10",
185
187
  "@types/n-readlines": "^1.0.6",
186
- "@types/n3": "^1.21.1",
188
+ "@types/n3": "^1.26.0",
187
189
  "@types/object-hash": "^3.0.6",
188
190
  "@types/seedrandom": "^3.0.8",
189
- "@types/semver": "^7.5.8",
191
+ "@types/semver": "^7.7.0",
190
192
  "@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",
193
+ "@types/ws": "^8.18.1",
194
+ "@typescript-eslint/eslint-plugin": "^8.40.0",
195
+ "@vitest/coverage-v8": "^3.2.4",
196
+ "esbuild": "^0.25.9",
197
+ "eslint": "^9.34.0",
196
198
  "license-checker": "^25.0.1",
197
199
  "npm-run-all": "^4.1.5",
198
200
  "release-it": "^19.0.2",
199
201
  "ts-node": "^10.9.2",
202
+ "ts-node-dev": "^2.0.0",
200
203
  "typedoc": "^0.27.7",
201
204
  "typedoc-plugin-missing-exports": "^3.1.0",
202
205
  "typedoc-theme-hierarchy": "^5.0.4",
203
206
  "typedoc-umlclass": "^0.10.1",
204
207
  "typescript": "^5.7.3",
205
- "vitest": "^3.0.6"
208
+ "vitest": "^3.2.4"
206
209
  },
207
210
  "dependencies": {
208
211
  "@eagleoutice/tree-sitter-r": "^1.1.2",
@@ -210,7 +213,9 @@
210
213
  "clipboardy": "^4.0.0",
211
214
  "command-line-args": "^6.0.1",
212
215
  "command-line-usage": "^7.0.3",
213
- "joi": "^17.13.3",
216
+ "commonmark": "^0.31.2",
217
+ "gray-matter": "^4.0.3",
218
+ "joi": "^18.0.1",
214
219
  "lz-string": "^1.5.0",
215
220
  "n-readlines": "^1.0.1",
216
221
  "n3": "^1.23.1",
@@ -221,7 +226,7 @@
221
226
  "semver": "^7.7.1",
222
227
  "tar": "^7.4.3",
223
228
  "tmp": "^0.2.3",
224
- "ts-essentials": "^10.0.4",
229
+ "ts-essentials": "^10.1.1",
225
230
  "tslog": "^4.9.3",
226
231
  "web-tree-sitter": "^0.24.7",
227
232
  "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 */