@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.
- package/README.md +48 -38
- package/abstract-interpretation/data-frame/absint-visitor.js +3 -2
- package/benchmark/slicer.js +2 -2
- package/benchmark/summarizer/first-phase/process.js +1 -1
- package/benchmark/summarizer/second-phase/graph.js +2 -2
- package/cli/repl/commands/repl-query.js +11 -2
- package/cli/repl/core.d.ts +2 -2
- package/cli/repl/core.js +26 -7
- package/cli/repl/server/connection.js +3 -1
- package/cli/repl/server/messages/message-slice.d.ts +3 -0
- package/cli/repl/server/messages/message-slice.js +2 -0
- package/cli/slicer-app.js +7 -2
- package/control-flow/extract-cfg.d.ts +3 -3
- package/control-flow/extract-cfg.js +4 -4
- package/control-flow/useless-loop.js +30 -21
- package/dataflow/environments/built-in.d.ts +1 -1
- package/dataflow/environments/default-builtin-config.d.ts +9 -0
- package/dataflow/environments/default-builtin-config.js +21 -21
- package/dataflow/environments/environment.js +18 -9
- package/dataflow/environments/overwrite.js +2 -2
- package/dataflow/extractor.js +1 -1
- package/dataflow/graph/diff-dataflow-graph.js +4 -4
- package/dataflow/graph/graph.d.ts +3 -3
- package/dataflow/graph/graph.js +4 -1
- package/dataflow/graph/quads.js +4 -4
- package/dataflow/info.js +1 -1
- package/dataflow/internal/linker.d.ts +2 -0
- package/dataflow/internal/linker.js +18 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +3 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +68 -21
- package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +1 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +4 -4
- package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +5 -18
- package/dataflow/internal/process/functions/call/built-in/built-in-repeat-loop.js +1 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +4 -5
- package/dataflow/internal/process/functions/call/common.js +4 -3
- package/documentation/doc-util/doc-query.js +6 -2
- package/documentation/doc-util/doc-types.d.ts +7 -2
- package/documentation/doc-util/doc-types.js +20 -4
- package/documentation/print-core-wiki.js +5 -1
- package/documentation/print-dataflow-graph-wiki.js +21 -12
- package/documentation/print-faq-wiki.js +5 -0
- package/documentation/print-interface-wiki.js +2 -0
- package/documentation/print-linter-wiki.js +2 -3
- package/documentation/print-linting-and-testing-wiki.js +4 -0
- package/documentation/print-query-wiki.js +22 -7
- package/documentation/print-readme.js +6 -0
- package/linter/linter-executor.js +25 -17
- package/linter/linter-format.d.ts +10 -1
- package/linter/linter-format.js +8 -0
- package/linter/linter-rules.d.ts +1 -0
- package/linter/rules/absolute-path.js +8 -8
- package/linter/rules/dataframe-access-validation.js +1 -1
- package/linter/rules/file-path-validity.js +8 -11
- package/linter/rules/naming-convention.d.ts +5 -1
- package/linter/rules/naming-convention.js +24 -8
- package/linter/rules/seeded-randomness.js +2 -2
- package/linter/rules/unused-definition.js +1 -1
- package/package.json +20 -15
- package/queries/catalog/call-context-query/call-context-query-executor.d.ts +5 -1
- package/queries/catalog/call-context-query/call-context-query-executor.js +14 -12
- package/queries/catalog/call-context-query/call-context-query-format.d.ts +6 -5
- package/queries/catalog/call-context-query/call-context-query-format.js +1 -1
- package/queries/catalog/call-context-query/identify-link-to-last-call-relation.d.ts +2 -1
- package/queries/catalog/call-context-query/identify-link-to-last-call-relation.js +1 -1
- package/queries/catalog/config-query/config-query-executor.js +7 -1
- package/queries/catalog/config-query/config-query-format.d.ts +7 -0
- package/queries/catalog/config-query/config-query-format.js +72 -1
- package/queries/catalog/dependencies-query/dependencies-query-executor.js +50 -75
- package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +50 -26
- package/queries/catalog/dependencies-query/dependencies-query-format.js +75 -20
- package/queries/catalog/dependencies-query/function-info/function-info.d.ts +2 -2
- package/queries/catalog/dependencies-query/function-info/visualize-functions.d.ts +2 -0
- package/queries/catalog/dependencies-query/function-info/visualize-functions.js +13 -0
- package/queries/catalog/happens-before-query/happens-before-query-executor.js +1 -1
- package/queries/catalog/linter-query/linter-query-format.js +4 -0
- package/queries/query-print.d.ts +2 -2
- package/queries/query-print.js +3 -2
- package/queries/query.d.ts +28 -21
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-executor.js +1 -1
- package/r-bridge/retriever.d.ts +14 -2
- package/r-bridge/retriever.js +10 -4
- package/search/flowr-search-builder.d.ts +1 -1
- package/search/flowr-search-builder.js +1 -1
- package/search/flowr-search-filters.d.ts +20 -10
- package/search/flowr-search-filters.js +19 -3
- package/search/search-executor/search-enrichers.d.ts +1 -1
- package/search/search-executor/search-enrichers.js +3 -2
- package/search/search-executor/search-generators.js +1 -1
- package/search/search-executor/search-transformer.js +1 -1
- package/util/formats/adapter-format.d.ts +6 -0
- package/util/formats/adapter-format.js +3 -0
- package/util/formats/adapter.d.ts +16 -0
- package/util/formats/adapter.js +42 -0
- package/util/formats/adapters/r-adapter.d.ts +4 -0
- package/util/formats/adapters/r-adapter.js +7 -0
- package/util/formats/adapters/rmd-adapter.d.ts +26 -0
- package/util/formats/adapters/rmd-adapter.js +91 -0
- package/util/objects.d.ts +11 -0
- package/util/objects.js +26 -0
- 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
|
-
|
|
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
|
|
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
|

|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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.
|
package/linter/linter-format.js
CHANGED
|
@@ -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
|
/**
|
package/linter/linter-rules.d.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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 =
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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) =>
|
|
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
|
|
143
|
-
.
|
|
144
|
-
...m
|
|
145
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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 =
|
|
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.
|
|
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
|
-
"
|
|
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.
|
|
181
|
-
"@eslint/js": "^9.
|
|
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.
|
|
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.
|
|
191
|
+
"@types/semver": "^7.7.0",
|
|
190
192
|
"@types/tmp": "^0.2.6",
|
|
191
|
-
"@types/ws": "^8.
|
|
192
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
193
|
-
"@vitest/coverage-v8": "^3.
|
|
194
|
-
"esbuild": "^0.25.
|
|
195
|
-
"eslint": "^9.
|
|
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.
|
|
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
|
-
"
|
|
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.
|
|
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.
|
|
53
|
-
: new RegExp(q.callName),
|
|
56
|
+
callName: promoteCallName(q.callName, q.callNameExact),
|
|
54
57
|
fileFilter: q.fileFilter && {
|
|
55
58
|
...q.fileFilter,
|
|
56
|
-
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:
|
|
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:
|
|
67
|
+
callName: promoteCallName(q.linkTo.callName)
|
|
65
68
|
}
|
|
66
69
|
};
|
|
67
70
|
}
|
|
68
71
|
else {
|
|
69
72
|
return {
|
|
70
73
|
...q,
|
|
71
|
-
callName: q.
|
|
72
|
-
: new RegExp(q.callName),
|
|
74
|
+
callName: promoteCallName(q.callName, q.callNameExact),
|
|
73
75
|
fileFilter: q.fileFilter && {
|
|
74
76
|
...q.fileFilter,
|
|
75
|
-
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 &&
|
|
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 */
|