@eagleoutice/flowr 2.2.13 → 2.2.15
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 +4 -4
- package/cli/repl/commands/repl-cfg.d.ts +2 -2
- package/cli/repl/commands/repl-cfg.js +4 -4
- package/cli/repl/commands/repl-commands.js +3 -3
- package/cli/repl/commands/repl-execute.js +2 -1
- package/cli/repl/server/connection.js +1 -1
- package/cli/script-core/statistics-helper-core.js +1 -1
- package/config.js +1 -1
- package/control-flow/basic-cfg-guided-visitor.d.ts +3 -3
- package/control-flow/cfg-dead-code.d.ts +4 -0
- package/control-flow/cfg-dead-code.js +81 -0
- package/control-flow/cfg-simplification.d.ts +17 -6
- package/control-flow/cfg-simplification.js +23 -19
- package/control-flow/control-flow-graph.d.ts +2 -1
- package/control-flow/control-flow-graph.js +1 -0
- package/control-flow/dfg-cfg-guided-visitor.d.ts +4 -4
- package/control-flow/dfg-cfg-guided-visitor.js +1 -1
- package/control-flow/extract-cfg.d.ts +2 -2
- package/control-flow/extract-cfg.js +70 -67
- package/control-flow/semantic-cfg-guided-visitor.d.ts +17 -8
- package/control-flow/semantic-cfg-guided-visitor.js +50 -17
- package/control-flow/simple-visitor.d.ts +4 -0
- package/control-flow/simple-visitor.js +14 -0
- package/control-flow/syntax-cfg-guided-visitor.d.ts +2 -2
- package/dataflow/environments/built-in-config.d.ts +1 -0
- package/dataflow/environments/built-in.d.ts +10 -1
- package/dataflow/environments/built-in.js +9 -3
- package/dataflow/environments/default-builtin-config.js +1 -1
- package/dataflow/environments/resolve-by-name.d.ts +0 -36
- package/dataflow/environments/resolve-by-name.js +0 -240
- package/dataflow/eval/resolve/alias-tracking.d.ts +87 -0
- package/dataflow/eval/resolve/alias-tracking.js +349 -0
- package/dataflow/eval/resolve/resolve.d.ts +34 -0
- package/dataflow/eval/resolve/resolve.js +93 -0
- package/dataflow/eval/values/general.d.ts +27 -0
- package/dataflow/eval/values/general.js +73 -0
- package/dataflow/eval/values/intervals/interval-constants.d.ts +4 -0
- package/dataflow/eval/values/intervals/interval-constants.js +27 -0
- package/dataflow/eval/values/logical/logical-constants.d.ts +7 -0
- package/dataflow/eval/values/logical/logical-constants.js +31 -0
- package/dataflow/eval/values/r-value.d.ts +58 -0
- package/dataflow/eval/values/r-value.js +90 -0
- package/dataflow/eval/values/scalar/scalar-consatnts.d.ts +15 -0
- package/dataflow/eval/values/scalar/scalar-consatnts.js +35 -0
- package/dataflow/eval/values/sets/set-constants.d.ts +7 -0
- package/dataflow/eval/values/sets/set-constants.js +34 -0
- package/dataflow/eval/values/string/string-constants.d.ts +8 -0
- package/dataflow/eval/values/string/string-constants.js +40 -0
- package/dataflow/eval/values/vectors/vector-constants.d.ts +14 -0
- package/dataflow/eval/values/vectors/vector-constants.js +35 -0
- package/dataflow/graph/unknown-replacement.d.ts +11 -0
- package/dataflow/graph/unknown-replacement.js +12 -0
- package/dataflow/graph/unknown-side-effect.d.ts +7 -0
- package/dataflow/graph/unknown-side-effect.js +13 -0
- package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +8 -5
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +4 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-eval.js +12 -9
- package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +5 -4
- package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +9 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-source.js +12 -15
- package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +23 -0
- package/dataflow/internal/process/functions/call/known-call-handling.js +2 -1
- package/documentation/doc-util/doc-cfg.d.ts +1 -1
- package/documentation/doc-util/doc-cfg.js +3 -3
- package/documentation/doc-util/doc-query.d.ts +6 -3
- package/documentation/doc-util/doc-query.js +3 -1
- package/documentation/print-cfg-wiki.js +31 -31
- package/documentation/print-dataflow-graph-wiki.js +4 -3
- package/documentation/print-engines-wiki.js +1 -1
- package/documentation/print-linter-wiki.d.ts +1 -0
- package/documentation/print-linter-wiki.js +76 -0
- package/documentation/print-query-wiki.js +80 -0
- package/linter/linter-executor.d.ts +9 -0
- package/linter/linter-executor.js +26 -0
- package/linter/linter-format.d.ts +65 -0
- package/linter/linter-format.js +9 -0
- package/linter/linter-rules.d.ts +42 -0
- package/linter/linter-rules.js +14 -0
- package/linter/rules/1-deprecated-functions.d.ts +34 -0
- package/linter/rules/1-deprecated-functions.js +54 -0
- package/linter/rules/2-file-path-validity.d.ts +48 -0
- package/linter/rules/2-file-path-validity.js +93 -0
- package/package.json +2 -1
- package/queries/catalog/call-context-query/call-context-query-executor.js +1 -1
- package/queries/catalog/call-context-query/call-context-query-format.d.ts +2 -2
- package/queries/catalog/call-context-query/call-context-query-format.js +5 -1
- package/queries/catalog/cluster-query/cluster-query-format.d.ts +2 -0
- package/queries/catalog/cluster-query/cluster-query-format.js +5 -1
- package/queries/catalog/config-query/config-query-format.d.ts +1 -0
- package/queries/catalog/config-query/config-query-format.js +2 -1
- package/queries/catalog/control-flow-query/control-flow-query-executor.d.ts +3 -0
- package/queries/catalog/control-flow-query/control-flow-query-executor.js +20 -0
- package/queries/catalog/control-flow-query/control-flow-query-format.d.ts +81 -0
- package/queries/catalog/control-flow-query/control-flow-query-format.js +34 -0
- package/queries/catalog/dataflow-lens-query/dataflow-lens-query-format.d.ts +1 -0
- package/queries/catalog/dataflow-lens-query/dataflow-lens-query-format.js +2 -1
- package/queries/catalog/dataflow-query/dataflow-query-format.d.ts +2 -0
- package/queries/catalog/dataflow-query/dataflow-query-format.js +9 -1
- package/queries/catalog/dependencies-query/dependencies-query-executor.js +33 -32
- package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +1 -0
- package/queries/catalog/dependencies-query/dependencies-query-format.js +10 -1
- package/queries/catalog/happens-before-query/happens-before-query-format.d.ts +1 -0
- package/queries/catalog/happens-before-query/happens-before-query-format.js +2 -1
- package/queries/catalog/id-map-query/id-map-query-format.d.ts +1 -0
- package/queries/catalog/id-map-query/id-map-query-format.js +2 -1
- package/queries/catalog/lineage-query/lineage-query-format.d.ts +1 -0
- package/queries/catalog/lineage-query/lineage-query-format.js +5 -1
- package/queries/catalog/linter-query/linter-query-executor.d.ts +3 -0
- package/queries/catalog/linter-query/linter-query-executor.js +28 -0
- package/queries/catalog/linter-query/linter-query-format.d.ts +80 -0
- package/queries/catalog/linter-query/linter-query-format.js +44 -0
- package/queries/catalog/location-map-query/location-map-query-format.d.ts +1 -0
- package/queries/catalog/location-map-query/location-map-query-format.js +2 -1
- package/queries/catalog/normalized-ast-query/normalized-ast-query-format.d.ts +1 -0
- package/queries/catalog/normalized-ast-query/normalized-ast-query-format.js +2 -1
- package/queries/catalog/origin-query/origin-query-format.d.ts +2 -0
- package/queries/catalog/origin-query/origin-query-format.js +5 -1
- package/queries/catalog/project-query/project-query-executor.js +1 -1
- package/queries/catalog/project-query/project-query-format.d.ts +1 -0
- package/queries/catalog/project-query/project-query-format.js +2 -1
- package/queries/catalog/resolve-value-query/resolve-value-query-executor.js +3 -3
- package/queries/catalog/resolve-value-query/resolve-value-query-format.d.ts +3 -1
- package/queries/catalog/resolve-value-query/resolve-value-query-format.js +4 -23
- package/queries/catalog/search-query/search-query-format.d.ts +1 -0
- package/queries/catalog/search-query/search-query-format.js +5 -1
- package/queries/catalog/static-slice-query/static-slice-query-format.d.ts +2 -0
- package/queries/catalog/static-slice-query/static-slice-query-format.js +9 -1
- package/queries/query.d.ts +143 -1
- package/queries/query.js +4 -0
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +11 -4
- package/r-bridge/retriever.js +1 -1
- package/search/flowr-search-builder.d.ts +31 -2
- package/search/flowr-search-builder.js +30 -0
- package/search/flowr-search.d.ts +7 -1
- package/search/search-executor/search-enrichers.d.ts +73 -0
- package/search/search-executor/search-enrichers.js +101 -0
- package/search/search-executor/search-generators.d.ts +6 -1
- package/search/search-executor/search-generators.js +21 -1
- package/search/search-executor/search-mappers.d.ts +19 -0
- package/search/search-executor/search-mappers.js +21 -0
- package/search/search-executor/search-transformer.d.ts +12 -0
- package/search/search-executor/search-transformer.js +11 -1
- package/slicing/criterion/parse.d.ts +8 -0
- package/slicing/criterion/parse.js +20 -0
- package/util/version.js +1 -1
|
@@ -23,9 +23,12 @@ const log_1 = require("../../../../../../util/log");
|
|
|
23
23
|
const fs_1 = __importDefault(require("fs"));
|
|
24
24
|
const parser_1 = require("../../../../../../r-bridge/lang-4.x/ast/parser/json/parser");
|
|
25
25
|
const shell_executor_1 = require("../../../../../../r-bridge/shell-executor");
|
|
26
|
-
const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
|
|
27
26
|
const assert_1 = require("../../../../../../util/assert");
|
|
28
27
|
const path_1 = __importDefault(require("path"));
|
|
28
|
+
const general_1 = require("../../../../../eval/values/general");
|
|
29
|
+
const r_value_1 = require("../../../../../eval/values/r-value");
|
|
30
|
+
const unknown_side_effect_1 = require("../../../../../graph/unknown-side-effect");
|
|
31
|
+
const alias_tracking_1 = require("../../../../../eval/resolve/alias-tracking");
|
|
29
32
|
let sourceProvider = (0, retriever_1.requestProviderFromFile)();
|
|
30
33
|
function setSourceProvider(provider) {
|
|
31
34
|
sourceProvider = provider;
|
|
@@ -128,7 +131,7 @@ function processSourceCall(name, args, rootId, data, config) {
|
|
|
128
131
|
const sourceFileArgument = args[0];
|
|
129
132
|
if (!config.forceFollow && (0, config_1.getConfig)().ignoreSourceCalls) {
|
|
130
133
|
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Skipping source call ${JSON.stringify(sourceFileArgument)} (disabled in config file)`);
|
|
131
|
-
information.graph.
|
|
134
|
+
(0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId);
|
|
132
135
|
return information;
|
|
133
136
|
}
|
|
134
137
|
let sourceFile;
|
|
@@ -136,14 +139,8 @@ function processSourceCall(name, args, rootId, data, config) {
|
|
|
136
139
|
sourceFile = [(0, retriever_1.removeRQuotes)(sourceFileArgument.lexeme)];
|
|
137
140
|
}
|
|
138
141
|
else if (sourceFileArgument !== r_function_call_1.EmptyArgument) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return x.str;
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
return undefined;
|
|
145
|
-
}
|
|
146
|
-
}).filter(assert_1.isNotUndefined);
|
|
142
|
+
const resolved = (0, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(sourceFileArgument.info.id, { environment: data.environment, idMap: data.completeAst.idMap }));
|
|
143
|
+
sourceFile = resolved?.elements.map(r => r.type === 'string' && (0, r_value_1.isValue)(r.value) ? r.value.str : undefined).filter(assert_1.isNotUndefined);
|
|
147
144
|
}
|
|
148
145
|
if (sourceFile && sourceFile.length === 1) {
|
|
149
146
|
const path = (0, retriever_1.removeRQuotes)(sourceFile[0]);
|
|
@@ -158,14 +155,14 @@ function processSourceCall(name, args, rootId, data, config) {
|
|
|
158
155
|
const findCount = data.referenceChain.filter(e => e.request === request.request && e.content === request.content).length;
|
|
159
156
|
if (findCount > limit) {
|
|
160
157
|
logger_1.dataflowLogger.warn(`Found cycle (>=${limit + 1}) in dataflow analysis for ${JSON.stringify(request)}: ${JSON.stringify(data.referenceChain)}, skipping further dataflow analysis`);
|
|
161
|
-
information.graph.
|
|
158
|
+
(0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId);
|
|
162
159
|
return information;
|
|
163
160
|
}
|
|
164
161
|
return sourceRequest(rootId, request, data, information, (0, decorate_1.sourcedDeterministicCountingIdGenerator)((findCount > 0 ? findCount + '::' : '') + path, name.location));
|
|
165
162
|
}
|
|
166
163
|
}
|
|
167
164
|
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Non-constant argument ${JSON.stringify(sourceFile)} for source is currently not supported, skipping`);
|
|
168
|
-
information.graph.
|
|
165
|
+
(0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId);
|
|
169
166
|
return information;
|
|
170
167
|
}
|
|
171
168
|
function sourceRequest(rootId, request, data, information, getId) {
|
|
@@ -173,7 +170,7 @@ function sourceRequest(rootId, request, data, information, getId) {
|
|
|
173
170
|
/* check if the file exists and if not, fail */
|
|
174
171
|
if (!fs_1.default.existsSync(request.content)) {
|
|
175
172
|
logger_1.dataflowLogger.warn(`Failed to analyze sourced file ${JSON.stringify(request)}: file does not exist`);
|
|
176
|
-
information.graph.
|
|
173
|
+
(0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId);
|
|
177
174
|
return information;
|
|
178
175
|
}
|
|
179
176
|
}
|
|
@@ -195,7 +192,7 @@ function sourceRequest(rootId, request, data, information, getId) {
|
|
|
195
192
|
catch (e) {
|
|
196
193
|
logger_1.dataflowLogger.error(`Failed to analyze sourced file ${JSON.stringify(request)}, skipping: ${e.message}`);
|
|
197
194
|
logger_1.dataflowLogger.error(e.stack);
|
|
198
|
-
information.graph.
|
|
195
|
+
(0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId);
|
|
199
196
|
return information;
|
|
200
197
|
}
|
|
201
198
|
// take the entry point as well as all the written references, and give them a control dependency to the source call to show that they are conditional
|
|
@@ -229,7 +226,7 @@ function standaloneSourceFile(inputRequest, data, uniqueSourceId, information) {
|
|
|
229
226
|
// check if the sourced file has already been dataflow analyzed, and if so, skip it
|
|
230
227
|
if (data.referenceChain.find(e => e.request === request.request && e.content === request.content)) {
|
|
231
228
|
logger_1.dataflowLogger.info(`Found loop in dataflow analysis for ${JSON.stringify(request)}: ${JSON.stringify(data.referenceChain)}, skipping further dataflow analysis`);
|
|
232
|
-
information.graph.
|
|
229
|
+
(0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, uniqueSourceId);
|
|
233
230
|
return information;
|
|
234
231
|
}
|
|
235
232
|
return sourceRequest(uniqueSourceId, request, {
|
|
@@ -11,6 +11,8 @@ const logger_1 = require("../../../../../logger");
|
|
|
11
11
|
const environment_1 = require("../../../../../environments/environment");
|
|
12
12
|
const edge_1 = require("../../../../../graph/edge");
|
|
13
13
|
const identifier_1 = require("../../../../../environments/identifier");
|
|
14
|
+
const general_1 = require("../../../../../eval/values/general");
|
|
15
|
+
const alias_tracking_1 = require("../../../../../eval/resolve/alias-tracking");
|
|
14
16
|
function processWhileLoop(name, args, rootId, data) {
|
|
15
17
|
if (args.length !== 2 || args[1] === r_function_call_1.EmptyArgument) {
|
|
16
18
|
logger_1.dataflowLogger.warn(`While-Loop ${name.content} does not have 2 arguments, skipping`);
|
|
@@ -21,6 +23,13 @@ function processWhileLoop(name, args, rootId, data) {
|
|
|
21
23
|
logger_1.dataflowLogger.warn(`While-Loop ${name.content} has empty arguments in ${JSON.stringify(args)}, skipping`);
|
|
22
24
|
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information;
|
|
23
25
|
}
|
|
26
|
+
// we should defer this to the abstract interpretation
|
|
27
|
+
const values = (0, alias_tracking_1.resolveIdToValue)(unpackedArgs[0]?.info.id, { environment: data.environment, idMap: data.completeAst.idMap });
|
|
28
|
+
const conditionIsAlwaysFalse = (0, general_1.valueSetGuard)(values)?.elements.every(d => d.type === 'logical' && d.value === false) ?? false;
|
|
29
|
+
//We don't care about the body if it never executes
|
|
30
|
+
if (conditionIsAlwaysFalse) {
|
|
31
|
+
unpackedArgs.pop();
|
|
32
|
+
}
|
|
24
33
|
/* we inject the cf-dependency of the while-loop after the condition */
|
|
25
34
|
const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({
|
|
26
35
|
name,
|
|
@@ -36,10 +45,24 @@ function processWhileLoop(name, args, rootId, data) {
|
|
|
36
45
|
}, origin: 'builtin:while-loop'
|
|
37
46
|
});
|
|
38
47
|
const [condition, body] = processedArguments;
|
|
48
|
+
// If the condition is always false, we don't include the body
|
|
49
|
+
if (condition !== undefined && conditionIsAlwaysFalse) {
|
|
50
|
+
information.graph.addEdge(name.info.id, condition.entryPoint, edge_1.EdgeType.Reads);
|
|
51
|
+
return {
|
|
52
|
+
unknownReferences: [],
|
|
53
|
+
in: [{ nodeId: name.info.id, name: name.lexeme, controlDependencies: data.controlDependencies, type: identifier_1.ReferenceType.Function }],
|
|
54
|
+
out: condition.out,
|
|
55
|
+
entryPoint: name.info.id,
|
|
56
|
+
exitPoints: [],
|
|
57
|
+
graph: information.graph,
|
|
58
|
+
environment: information.environment
|
|
59
|
+
};
|
|
60
|
+
}
|
|
39
61
|
(0, assert_1.guard)(condition !== undefined && body !== undefined, () => `While-Loop ${name.content} has no condition or body, impossible!`);
|
|
40
62
|
const originalDependency = data.controlDependencies;
|
|
41
63
|
if ((0, info_1.alwaysExits)(condition)) {
|
|
42
64
|
logger_1.dataflowLogger.warn(`While-Loop ${rootId} forces exit in condition, skipping rest`);
|
|
65
|
+
information.graph.addEdge(name.info.id, condition.entryPoint, edge_1.EdgeType.Reads);
|
|
43
66
|
return condition;
|
|
44
67
|
}
|
|
45
68
|
const remainingInputs = (0, linker_1.linkInputs)([
|
|
@@ -10,6 +10,7 @@ const edge_1 = require("../../../../graph/edge");
|
|
|
10
10
|
const logger_1 = require("../../../../logger");
|
|
11
11
|
const vertex_1 = require("../../../../graph/vertex");
|
|
12
12
|
const log_1 = require("../../../../../util/log");
|
|
13
|
+
const unknown_side_effect_1 = require("../../../../graph/unknown-side-effect");
|
|
13
14
|
function markNonStandardEvaluationEdges(markAsNSE, callArgs, finalGraph, rootId) {
|
|
14
15
|
for (const nse of markAsNSE) {
|
|
15
16
|
if (nse < callArgs.length) {
|
|
@@ -46,7 +47,7 @@ function processKnownFunctionCall({ name, args, rootId, data, reverseOrder = fal
|
|
|
46
47
|
origin: origin === 'default' ? ['function'] : [origin]
|
|
47
48
|
});
|
|
48
49
|
if (hasUnknownSideEffect) {
|
|
49
|
-
finalGraph.
|
|
50
|
+
(0, unknown_side_effect_1.handleUnknownSideEffect)(finalGraph, data.environment, rootId);
|
|
50
51
|
}
|
|
51
52
|
const inIds = remainingReadInArgs;
|
|
52
53
|
const fnRef = { nodeId: rootId, name: functionCallName, controlDependencies: data.controlDependencies, type: identifier_1.ReferenceType.Function };
|
|
@@ -19,5 +19,5 @@ export interface PrintCfgOptions {
|
|
|
19
19
|
readonly simplify?: boolean;
|
|
20
20
|
readonly useDfg?: boolean;
|
|
21
21
|
}
|
|
22
|
-
export declare function
|
|
22
|
+
export declare function printCfgCode(parser: KnownParser, code: string, { showCode, openCode, prefix, simplifications, simplify, useDfg }?: PrintCfgOptions): Promise<string>;
|
|
23
23
|
export {};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getCfg = getCfg;
|
|
4
4
|
exports.printCfg = printCfg;
|
|
5
|
-
exports.
|
|
5
|
+
exports.printCfgCode = printCfgCode;
|
|
6
6
|
const extract_cfg_1 = require("../../control-flow/extract-cfg");
|
|
7
7
|
const default_pipelines_1 = require("../../core/steps/pipeline/default-pipelines");
|
|
8
8
|
const retriever_1 = require("../../r-bridge/retriever");
|
|
@@ -17,7 +17,7 @@ async function getCfg(parser, code, simplifications = [], useDfg = true) {
|
|
|
17
17
|
}).allRemainingSteps() : await (0, default_pipelines_1.createNormalizePipeline)(parser, {
|
|
18
18
|
request: (0, retriever_1.requestFromInput)(code)
|
|
19
19
|
}).allRemainingSteps();
|
|
20
|
-
const cfg = (0, extract_cfg_1.
|
|
20
|
+
const cfg = (0, extract_cfg_1.extractCfg)(result.normalize, useDfg ? result.dataflow.graph : undefined, [...cfg_simplification_1.DefaultCfgSimplificationOrder, ...simplifications]);
|
|
21
21
|
return {
|
|
22
22
|
info: cfg,
|
|
23
23
|
ast: result.normalize,
|
|
@@ -29,7 +29,7 @@ function printCfg(cfg, ast, prefix = 'flowchart BT\n', simplify = false) {
|
|
|
29
29
|
${(0, doc_code_1.codeBlock)('mermaid', (0, cfg_1.cfgToMermaid)(cfg, ast, prefix, simplify))}
|
|
30
30
|
`;
|
|
31
31
|
}
|
|
32
|
-
async function
|
|
32
|
+
async function printCfgCode(parser, code, { showCode = true, openCode = false, prefix = 'flowchart BT\n', simplifications = [], simplify = false, useDfg = true } = {}) {
|
|
33
33
|
const now = performance.now();
|
|
34
34
|
const res = await getCfg(parser, code, simplifications, useDfg);
|
|
35
35
|
const duration = performance.now() - now;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { RShell } from '../../r-bridge/shell';
|
|
2
|
-
import type { Queries, SupportedQueryTypes } from '../../queries/query';
|
|
2
|
+
import type { Queries, QueryResults, SupportedQueryTypes } from '../../queries/query';
|
|
3
|
+
import { DEFAULT_DATAFLOW_PIPELINE } from '../../core/steps/pipeline/default-pipelines';
|
|
3
4
|
import type { SupportedVirtualQueryTypes } from '../../queries/virtual-query/virtual-queries';
|
|
4
5
|
import type { VirtualCompoundConstraint } from '../../queries/virtual-query/compound-query';
|
|
5
|
-
|
|
6
|
+
import type { PipelineOutput } from '../../core/steps/pipeline/pipeline';
|
|
7
|
+
export interface ShowQueryOptions<Base extends SupportedQueryTypes> {
|
|
6
8
|
readonly showCode?: boolean;
|
|
7
9
|
readonly collapseResult?: boolean;
|
|
8
10
|
readonly collapseQuery?: boolean;
|
|
11
|
+
readonly addOutput?: (result: QueryResults<Base>, pipeline: PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>) => string;
|
|
9
12
|
}
|
|
10
|
-
export declare function showQuery<Base extends SupportedQueryTypes, VirtualArguments extends VirtualCompoundConstraint<Base> = VirtualCompoundConstraint<Base>>(shell: RShell, code: string, queries: Queries<Base, VirtualArguments>, { showCode, collapseResult, collapseQuery }?: ShowQueryOptions): Promise<string>;
|
|
13
|
+
export declare function showQuery<Base extends SupportedQueryTypes, VirtualArguments extends VirtualCompoundConstraint<Base> = VirtualCompoundConstraint<Base>>(shell: RShell, code: string, queries: Queries<Base, VirtualArguments>, { showCode, collapseResult, collapseQuery, addOutput }?: ShowQueryOptions<Base>): Promise<string>;
|
|
11
14
|
export interface QueryDocumentation {
|
|
12
15
|
readonly name: string;
|
|
13
16
|
readonly type: 'virtual' | 'active';
|
|
@@ -17,7 +17,7 @@ const doc_dfg_1 = require("./doc-dfg");
|
|
|
17
17
|
const doc_code_1 = require("./doc-code");
|
|
18
18
|
const time_1 = require("../../util/text/time");
|
|
19
19
|
const query_print_1 = require("../../queries/query-print");
|
|
20
|
-
async function showQuery(shell, code, queries, { showCode, collapseResult, collapseQuery } = {}) {
|
|
20
|
+
async function showQuery(shell, code, queries, { showCode, collapseResult, collapseQuery, addOutput = () => '' } = {}) {
|
|
21
21
|
const now = performance.now();
|
|
22
22
|
const analysis = await new pipeline_executor_1.PipelineExecutor(default_pipelines_1.DEFAULT_DATAFLOW_PIPELINE, {
|
|
23
23
|
parser: shell,
|
|
@@ -60,6 +60,8 @@ ${await (0, doc_dfg_1.printDfGraphForCode)(shell, code, { switchCodeAndGraph: tr
|
|
|
60
60
|
|
|
61
61
|
${collapseResult ? '</details>' : ''}
|
|
62
62
|
|
|
63
|
+
${addOutput(results, analysis)}
|
|
64
|
+
|
|
63
65
|
`;
|
|
64
66
|
}
|
|
65
67
|
exports.RegisteredQueries = {
|
|
@@ -89,10 +89,10 @@ class CollectNumbersSyntaxVisitor extends syntax_cfg_guided_visitor_1.SyntaxAwar
|
|
|
89
89
|
class CollectNumbersDataflowVisitor extends dfg_cfg_guided_visitor_1.DataflowAwareCfgGuidedVisitor {
|
|
90
90
|
numbers = [];
|
|
91
91
|
constructor(controlFlow, dataflow) {
|
|
92
|
-
super({ controlFlow, dataflow, defaultVisitingOrder: 'forward' });
|
|
92
|
+
super({ controlFlow, dfg: dataflow, defaultVisitingOrder: 'forward' });
|
|
93
93
|
}
|
|
94
94
|
visitValue(node) {
|
|
95
|
-
const astNode = this.config.
|
|
95
|
+
const astNode = this.config.dfg.idMap?.get(node.id);
|
|
96
96
|
if ((0, r_number_1.isRNumber)(astNode)) {
|
|
97
97
|
this.numbers.push(astNode.content);
|
|
98
98
|
}
|
|
@@ -104,7 +104,7 @@ class CollectNumbersDataflowVisitor extends dfg_cfg_guided_visitor_1.DataflowAwa
|
|
|
104
104
|
class CollectSourcesSemanticVisitor extends semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor {
|
|
105
105
|
sources = [];
|
|
106
106
|
constructor(controlFlow, normalizedAst, dataflow) {
|
|
107
|
-
super({ controlFlow, normalizedAst, dataflow, defaultVisitingOrder: 'forward' });
|
|
107
|
+
super({ controlFlow, normalizedAst, dfg: dataflow, defaultVisitingOrder: 'forward' });
|
|
108
108
|
}
|
|
109
109
|
onAssignmentCall({ source }) {
|
|
110
110
|
if (source) {
|
|
@@ -167,7 +167,7 @@ ${(0, doc_code_1.codeBlock)('r', 'x <- 2 * 3 + 1')}
|
|
|
167
167
|
|
|
168
168
|
The corresponding CFG is a directed, labeled graph with two types of edges (control and flow dependencies).
|
|
169
169
|
|
|
170
|
-
${await (0, doc_cfg_1.
|
|
170
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'x <- 2 * 3 + 1', { showCode: false, prefix: 'flowchart RL\n' })}
|
|
171
171
|
|
|
172
172
|
${(0, doc_structure_1.block)({
|
|
173
173
|
type: 'IMPORTANT',
|
|
@@ -180,33 +180,33 @@ Expressions, such as \`2 * 3\` get an additional node with an artificial id that
|
|
|
180
180
|
|
|
181
181
|
To gain a better understanding, let's have a look at a simple program with a single branching structure:
|
|
182
182
|
|
|
183
|
-
${await (0, doc_cfg_1.
|
|
183
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'if(u) 3 else 2', { showCode: true, openCode: false, prefix: 'flowchart RL\n' })}
|
|
184
184
|
|
|
185
185
|
Here, you can see the \`if\` node followed by the condition (in this case merely \`u\`) that then splits into two branches for the two possible outcomes.
|
|
186
186
|
The \`if\` structure is terminated by the corresponding \`-exit\` node (see the [structure](#cfg-structure) section for more details).
|
|
187
187
|
|
|
188
188
|
For you to compare, the following shows the CFG of an \`if\` without an \`else\` branch:
|
|
189
189
|
|
|
190
|
-
${await (0, doc_cfg_1.
|
|
190
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'if(u || v) 3', { showCode: true, openCode: false, prefix: 'flowchart RL\n' })}
|
|
191
191
|
|
|
192
192
|
Activating the calculation of basic blocks produces the following:
|
|
193
193
|
|
|
194
|
-
${await (0, doc_cfg_1.
|
|
194
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'if(u || v) 3', { showCode: true, openCode: false, prefix: 'flowchart RL\n', simplifications: ['to-basic-blocks'] })}
|
|
195
195
|
|
|
196
196
|
Which is probably much more readable if compacted (although the reconstucted code can sometimes be slightly mislieading as flowR tries its best to make it syntactically correct and hence add closing braces etc. which are technically not part of the respective block):
|
|
197
197
|
|
|
198
|
-
${await (0, doc_cfg_1.
|
|
198
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'if(u || v) 3', { showCode: true, openCode: false, prefix: 'flowchart RL\n', simplifications: ['to-basic-blocks'], simplify: true })}
|
|
199
199
|
|
|
200
200
|
The control flow graph also harmonizes with function definitions, and calls:
|
|
201
201
|
|
|
202
|
-
${await (0, doc_cfg_1.
|
|
202
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'f <- function() { 3 }\nf()', { showCode: true, openCode: true, prefix: 'flowchart RL\n' })}
|
|
203
203
|
|
|
204
204
|
${(0, doc_structure_1.section)('Structure of the Control Flow Graph', 2, 'cfg-structure')}
|
|
205
205
|
|
|
206
|
-
You can produce your very own control flow graph with ${(0, doc_types_1.shortLink)(extract_cfg_1.
|
|
206
|
+
You can produce your very own control flow graph with ${(0, doc_types_1.shortLink)(extract_cfg_1.extractCfg.name, types.info)}.
|
|
207
207
|
The ${(0, doc_types_1.shortLink)(control_flow_graph_1.ControlFlowGraph.name, types.info)} class describes everything required to model the control flow graph, with its edge types described by
|
|
208
208
|
${(0, doc_types_1.shortLink)('CfgEdge', types.info)} and its vertices by ${(0, doc_types_1.shortLink)('CfgSimpleVertex', types.info)}.
|
|
209
|
-
However, you should be aware of the ${(0, doc_types_1.shortLink)('ControlFlowInformation', types.info)} interface which adds some additional information the
|
|
209
|
+
However, you should be aware of the ${(0, doc_types_1.shortLink)('ControlFlowInformation', types.info)} interface which adds some additional information the CFG
|
|
210
210
|
(and is used during the construction of the CFG as well):
|
|
211
211
|
|
|
212
212
|
${(0, doc_types_1.printHierarchy)({ info: types.info, root: 'ControlFlowInformation', program: types.program, openTop: true })}
|
|
@@ -214,7 +214,7 @@ ${(0, doc_types_1.printHierarchy)({ info: types.info, root: 'ControlFlowInformat
|
|
|
214
214
|
To check whether the CFG has the expected shape, you can use the test function ${(0, doc_types_1.shortLink)('assertCfg', testTypes.info)} which supports testing for
|
|
215
215
|
sub-graphs as well (it provides diffing capabilities similar to ${(0, doc_types_1.shortLink)('assertDataflow', testTypes.info)}).
|
|
216
216
|
As the CFG may become unhandy for larger programs, there are simplifications available with ${(0, doc_types_1.shortLink)(cfg_simplification_1.simplifyControlFlowInformation.name, types.info)}
|
|
217
|
-
(these can be passed on to the ${(0, doc_types_1.shortLink)(extract_cfg_1.
|
|
217
|
+
(these can be passed on to the ${(0, doc_types_1.shortLink)(extract_cfg_1.extractCfg.name, types.info)} function as well).
|
|
218
218
|
|
|
219
219
|
${(0, doc_structure_1.section)('CFG Vertices', 3, 'cfg-structure-vertices')}
|
|
220
220
|
|
|
@@ -258,7 +258,7 @@ ${(0, doc_structure_1.section)('Flow Dependencies', 4, 'cfg-flow-dependency')}
|
|
|
258
258
|
The most common edge is the flow dependency (FD) which simply signals that the source vertex happens _after_ the target vertex in the control flow.
|
|
259
259
|
So \`x; y\` would produce a flow dependency from \`y\` to \`x\` (additionally to the program-enveloping root expression list):
|
|
260
260
|
|
|
261
|
-
${await (0, doc_cfg_1.
|
|
261
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'x; y', { showCode: false, prefix: 'flowchart RL\n' })}
|
|
262
262
|
|
|
263
263
|
${(0, doc_structure_1.section)('Control Dependencies', 4, 'cfg-control-dependency')}
|
|
264
264
|
|
|
@@ -271,12 +271,12 @@ The extra \`caused\` link signals the vertex that caused the control flow influe
|
|
|
271
271
|
|
|
272
272
|
|
|
273
273
|
${await (async () => {
|
|
274
|
-
const exa = await (0, doc_cfg_1.
|
|
274
|
+
const exa = await (0, doc_cfg_1.printCfgCode)(shell, 'if(u) 3 else 2', { showCode: true, prefix: 'flowchart RL\n' });
|
|
275
275
|
return (0, doc_structure_1.details)('Example: if-else', exa);
|
|
276
276
|
})()}
|
|
277
277
|
|
|
278
278
|
${await (async () => {
|
|
279
|
-
const exa = await (0, doc_cfg_1.
|
|
279
|
+
const exa = await (0, doc_cfg_1.printCfgCode)(shell, 'while(u) b', { showCode: true, prefix: 'flowchart RL\n' });
|
|
280
280
|
return (0, doc_structure_1.details)('Example: while-loop', exa);
|
|
281
281
|
})()}
|
|
282
282
|
<br/>
|
|
@@ -286,12 +286,12 @@ Additionally, the control flow graph does not have to be connected. If you use a
|
|
|
286
286
|
the corresponding exit markers are not reachable from the entry:
|
|
287
287
|
|
|
288
288
|
${await (async () => {
|
|
289
|
-
const exa = await (0, doc_cfg_1.
|
|
289
|
+
const exa = await (0, doc_cfg_1.printCfgCode)(shell, 'repeat { b }; after', { showCode: true, prefix: 'flowchart RL\n' });
|
|
290
290
|
return (0, doc_structure_1.details)('Example: repeat-loop (infinite)', exa);
|
|
291
291
|
})()}
|
|
292
292
|
|
|
293
293
|
${await (async () => {
|
|
294
|
-
const exa = await (0, doc_cfg_1.
|
|
294
|
+
const exa = await (0, doc_cfg_1.printCfgCode)(shell, 'repeat { b; if(u) break; }; after', { showCode: true, prefix: 'flowchart RL\n' });
|
|
295
295
|
return (0, doc_structure_1.details)('Example: repeat-loop (with break)', exa);
|
|
296
296
|
})()}
|
|
297
297
|
<br/>
|
|
@@ -299,22 +299,22 @@ ${await (async () => {
|
|
|
299
299
|
In the context of a for-loop, the control dependency refer to whether the respective vector still has values to iterate over.
|
|
300
300
|
|
|
301
301
|
${await (async () => {
|
|
302
|
-
const exa = await (0, doc_cfg_1.
|
|
302
|
+
const exa = await (0, doc_cfg_1.printCfgCode)(shell, 'for(i in 1:10) b', { showCode: true, prefix: 'flowchart RL\n' });
|
|
303
303
|
return (0, doc_structure_1.details)('Example: for-loop', exa);
|
|
304
304
|
})()}
|
|
305
305
|
|
|
306
306
|
${(0, doc_structure_1.section)('Extra: Call Links', 4, 'cfg-call-links')}
|
|
307
307
|
|
|
308
|
-
If you generate the CFG with the ${(0, doc_types_1.shortLink)(extract_cfg_1.
|
|
308
|
+
If you generate the CFG with the ${(0, doc_types_1.shortLink)(extract_cfg_1.extractCfg.name, types.info)} function you can (and, if you want to gain inter-procedural information, should)
|
|
309
309
|
pass a matching [dataflow graph](${doc_files_1.FlowrWikiBaseRef}/Dataflow%20Graph) to it to incorporate the dataflow perspective into the CFG.
|
|
310
310
|
|
|
311
311
|
The difference becomes obvious when we look at the code \`f <- function() b; f()\` first without the dataflow graph:
|
|
312
312
|
|
|
313
|
-
${await (0, doc_cfg_1.
|
|
313
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'f <- function() b; f()', { showCode: true, prefix: 'flowchart RL\n', useDfg: false })}
|
|
314
314
|
|
|
315
315
|
And now, including dataflow information:
|
|
316
316
|
|
|
317
|
-
${await (0, doc_cfg_1.
|
|
317
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'f <- function() b; f()', { showCode: true, prefix: 'flowchart RL\n', useDfg: true })}
|
|
318
318
|
|
|
319
319
|
There are two important additions:
|
|
320
320
|
|
|
@@ -325,7 +325,7 @@ There are two important additions:
|
|
|
325
325
|
For built-in functions that are provided by flowR's built-in configuration (see the [interface wiki page](${doc_files_1.FlowrWikiBaseRef}/Interface)) the CFG does not contain
|
|
326
326
|
the additional information directly:
|
|
327
327
|
|
|
328
|
-
${await (0, doc_cfg_1.
|
|
328
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'print(3)', { showCode: true, prefix: 'flowchart RL\n' })}
|
|
329
329
|
|
|
330
330
|
This is due to the fact that the [dataflow graph](${doc_files_1.FlowrWikiBaseRef}/Dataflow%20Graph) does contain the required call information (and there are no new control vertices to add as the built-in call has no target in the source code):
|
|
331
331
|
|
|
@@ -340,16 +340,16 @@ Yet, we can request basic blocks or transform an existing CFG into basic blocks
|
|
|
340
340
|
|
|
341
341
|
Any program without any (un-)conditional jumps now contains a single basic block:
|
|
342
342
|
|
|
343
|
-
${await (0, doc_cfg_1.
|
|
343
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'x <- 2 * 3 + 1', { showCode: true, openCode: true, prefix: 'flowchart RL\n', simplifications: ['to-basic-blocks'], simplify: true })}
|
|
344
344
|
|
|
345
345
|
While the CFG without basic blocks is much bigger:
|
|
346
346
|
|
|
347
|
-
${await (0, doc_cfg_1.
|
|
347
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'x <- 2 * 3 + 1', { showCode: false, prefix: 'flowchart RL\n' })}
|
|
348
348
|
|
|
349
349
|
In a way, using the basic blocks perspective does not remove any of these vertices (we just usually visualize them compacted as their execution order should be "obvious").
|
|
350
350
|
The vertices are still there, as elems of the ${(0, doc_types_1.shortLink)('CfgBasicBlockVertex', types.info)}:
|
|
351
351
|
|
|
352
|
-
${await (0, doc_cfg_1.
|
|
352
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, 'x <- 2 * 3 + 1', { showCode: false, prefix: 'flowchart RL\n', simplifications: ['to-basic-blocks'], simplify: false })}
|
|
353
353
|
|
|
354
354
|
The benefit (for comprehensibility and algorithms) becomes more apparent when we look at a more complicated program:
|
|
355
355
|
|
|
@@ -357,12 +357,12 @@ ${(0, doc_code_1.codeBlock)('r', CfgLongExample)}
|
|
|
357
357
|
|
|
358
358
|
With basic blocks, this code looks like this:
|
|
359
359
|
|
|
360
|
-
${await (0, doc_cfg_1.
|
|
360
|
+
${await (0, doc_cfg_1.printCfgCode)(shell, CfgLongExample, { showCode: false, prefix: 'flowchart RL\n', simplifications: ['to-basic-blocks'], simplify: true })}
|
|
361
361
|
|
|
362
362
|
Now, without basic blocks, this is a different story...
|
|
363
363
|
|
|
364
364
|
${await (async () => {
|
|
365
|
-
const exa = await (0, doc_cfg_1.
|
|
365
|
+
const exa = await (0, doc_cfg_1.printCfgCode)(shell, CfgLongExample, { showCode: false, prefix: 'flowchart RL\n' });
|
|
366
366
|
return (0, doc_structure_1.details)('The full CFG', exa);
|
|
367
367
|
})()}
|
|
368
368
|
|
|
@@ -475,7 +475,7 @@ Again, executing it with the CFG and Dataflow of the expression \`x - 1 + 2L * 3
|
|
|
475
475
|
|
|
476
476
|
${await (async () => {
|
|
477
477
|
const res = await (0, doc_cfg_1.getCfg)(shell, 'x - 1 + 2L * 3');
|
|
478
|
-
const visitor = new CollectNumbersDataflowVisitor(res.info, res.dataflow);
|
|
478
|
+
const visitor = new CollectNumbersDataflowVisitor(res.info, res.dataflow.graph);
|
|
479
479
|
visitor.start();
|
|
480
480
|
const collected = visitor.getNumbers();
|
|
481
481
|
return collected.map(n => '\n- `' + JSON.stringify(n) + '`').join('');
|
|
@@ -500,7 +500,7 @@ Executing it with the CFG and Dataflow of the expression \`x <- 2; 3 -> x; assig
|
|
|
500
500
|
|
|
501
501
|
${await (async () => {
|
|
502
502
|
const res = await (0, doc_cfg_1.getCfg)(shell, 'x <- 2; 3 -> x; assign("x", 42 + 21)');
|
|
503
|
-
const visitor = new CollectSourcesSemanticVisitor(res.info, res.ast, res.dataflow);
|
|
503
|
+
const visitor = new CollectSourcesSemanticVisitor(res.info, res.ast, res.dataflow.graph);
|
|
504
504
|
visitor.start();
|
|
505
505
|
const collected = visitor.getSources();
|
|
506
506
|
return collected.map(n => '\n- `' + n + '`').join('');
|
|
@@ -529,7 +529,7 @@ ${await (async function () {
|
|
|
529
529
|
(0, assert_1.guard)(plusVertex.type === control_flow_graph_1.CfgVertexType.Expression);
|
|
530
530
|
const numOfExits = plusVertex.end?.length ?? 0;
|
|
531
531
|
(0, assert_1.guard)(plusVertex.end && numOfExits === 1);
|
|
532
|
-
return `${await (0, doc_cfg_1.
|
|
532
|
+
return `${await (0, doc_cfg_1.printCfgCode)(shell, 'x + 1', { showCode: true, prefix: 'flowchart RL\n' })}
|
|
533
533
|
|
|
534
534
|
Looking at the binary operation vertex for \`+\` (with id \`${plusVertexId}\`) we see that it is linked to a single exit ("end marker") point: \`${plusVertex.end[0]}\`.
|
|
535
535
|
Checking this vertex essentially reveals all exit points of the expression ‐ in this case, this simply refers to the operands of the addition.
|
|
@@ -544,7 +544,7 @@ ${(0, doc_structure_1.details)('Example: Exit Points for an if', await (async fu
|
|
|
544
544
|
(0, assert_1.guard)(ifVertex.type === control_flow_graph_1.CfgVertexType.Statement);
|
|
545
545
|
const numOfExits = ifVertex.end?.length ?? 0;
|
|
546
546
|
(0, assert_1.guard)(ifVertex.end && numOfExits === 1);
|
|
547
|
-
return `${await (0, doc_cfg_1.
|
|
547
|
+
return `${await (0, doc_cfg_1.printCfgCode)(shell, expr, { showCode: true, prefix: 'flowchart RL\n' })}
|
|
548
548
|
|
|
549
549
|
Looking at the if vertex for (with id \`${ifVertexId}\`) we see that it is again linked to a single exit point: \`${ifVertex.end[0]}\`.
|
|
550
550
|
Yet, now this exit vertex is linked to the two branches of the if statement (the \`then\` and \`else\` branch).
|
|
@@ -34,6 +34,7 @@ const linker_1 = require("../dataflow/internal/linker");
|
|
|
34
34
|
const doc_normalized_ast_1 = require("./doc-util/doc-normalized-ast");
|
|
35
35
|
const dfg_get_origin_1 = require("../dataflow/origin/dfg-get-origin");
|
|
36
36
|
const identify_link_to_last_call_relation_1 = require("../queries/catalog/call-context-query/identify-link-to-last-call-relation");
|
|
37
|
+
const alias_tracking_1 = require("../dataflow/eval/resolve/alias-tracking");
|
|
37
38
|
const doc_issue_1 = require("./doc-util/doc-issue");
|
|
38
39
|
const unnamed_call_handling_1 = require("../dataflow/internal/process/functions/call/unnamed-call-handling");
|
|
39
40
|
const environment_builder_1 = require("../../test/functionality/_helper/dataflow/environment-builder");
|
|
@@ -977,7 +978,7 @@ Depending on what you are interested in, there exists a plethora of functions an
|
|
|
977
978
|
* The **[Query API](${doc_files_1.FlowrWikiBaseRef}/Query%20API)** provides many functions to query the dataflow graph for specific information (dependencies, calls, slices, clusters, ...)
|
|
978
979
|
* The **[Search API](${doc_files_1.FlowrWikiBaseRef}/Search%20API)** allows you to search for specific vertices or edges in the dataflow graph or the original program
|
|
979
980
|
* ${(0, doc_types_1.shortLink)(node_id_1.recoverName.name, vertexType.info)} and ${(0, doc_types_1.shortLink)(node_id_1.recoverContent.name, vertexType.info)} to get the name or content of a vertex in the dataflow graph
|
|
980
|
-
* ${(0, doc_types_1.shortLink)(
|
|
981
|
+
* ${(0, doc_types_1.shortLink)(alias_tracking_1.resolveIdToValue.name, vertexType.info)} to resolve the value of a variable or id (if possible, see [below](#dfg-resolving-values))
|
|
981
982
|
* ${(0, doc_types_1.shortLink)(edge_1.edgeIncludesType.name, vertexType.info)} to check if an edge includes a specific type and ${(0, doc_types_1.shortLink)(edge_1.splitEdgeTypes.name, vertexType.info)} to split the bitmask of edges into its types (see [below](#dfg-resolving-values))
|
|
982
983
|
* ${(0, doc_types_1.shortLink)(identify_link_to_last_call_relation_1.getValueOfArgument.name, vertexType.info)} to get the (syntactical) value of an argument in a function call
|
|
983
984
|
* ${(0, doc_types_1.shortLink)(dfg_get_origin_1.getOriginInDfg.name, vertexType.info)} to get information about where a read, call, ... comes from (see [below](#dfg-resolving-values))
|
|
@@ -989,8 +990,8 @@ ${(0, doc_structure_1.section)('Resolving Values', 3, 'dfg-resolving-values')}
|
|
|
989
990
|
FlowR supports a [configurable](${doc_files_1.FlowrWikiBaseRef}/Interface#configuring-flowr) level of value tracking—all with the goal of knowing the static value domain of a variable.
|
|
990
991
|
These capabilities are exposed by the [resolve value Query](${doc_files_1.FlowrWikiBaseRef}/Query-API#resolve-value-query) and backed by two important functions:
|
|
991
992
|
|
|
992
|
-
${(0, doc_types_1.shortLink)(
|
|
993
|
-
value resolution
|
|
993
|
+
${(0, doc_types_1.shortLink)(alias_tracking_1.resolveIdToValue.name, vertexType.info)} provides an environment-sensitive (see ${(0, doc_types_1.shortLink)('REnvironmentInformation', vertexType.info)})
|
|
994
|
+
value resolution depending on if the environment is provided.
|
|
994
995
|
|
|
995
996
|
${(0, doc_structure_1.section)('Assessing Edges', 3, 'dfg-assess-edge')}
|
|
996
997
|
|
|
@@ -49,7 +49,7 @@ are exposed with some command line options (e.g., when using the docker image of
|
|
|
49
49
|
|
|
50
50
|
${(0, doc_structure_1.block)({
|
|
51
51
|
type: 'WARNING',
|
|
52
|
-
content: 'As the tree-sitter engine is only for parsing, it cannot execute R code.'
|
|
52
|
+
content: 'As the tree-sitter engine is only for parsing, it cannot execute R code. This engine is now the default.'
|
|
53
53
|
})}
|
|
54
54
|
|
|
55
55
|
In general, there is no need for you to pass custom paths using either
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const log_1 = require("../../test/functionality/_helper/log");
|
|
7
|
+
const doc_auto_gen_1 = require("./doc-util/doc-auto-gen");
|
|
8
|
+
const doc_files_1 = require("./doc-util/doc-files");
|
|
9
|
+
const linter_rules_1 = require("../linter/linter-rules");
|
|
10
|
+
const doc_code_1 = require("./doc-util/doc-code");
|
|
11
|
+
const shell_1 = require("../r-bridge/shell");
|
|
12
|
+
const doc_query_1 = require("./doc-util/doc-query");
|
|
13
|
+
const doc_types_1 = require("./doc-util/doc-types");
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
async function getText(shell) {
|
|
16
|
+
const rVersion = (await shell.usedRVersion())?.format() ?? 'unknown';
|
|
17
|
+
return `${(0, doc_auto_gen_1.autoGenHeader)({ filename: module.filename, purpose: 'linter', rVersion })}
|
|
18
|
+
|
|
19
|
+
This page describes the flowR linter, which is a tool that utilizes flowR's dataflow analysis to find common issues in R scripts. The linter can currently be used through the linter [query](${doc_files_1.FlowrWikiBaseRef}/Query%20API).
|
|
20
|
+
|
|
21
|
+
## Linting Rules
|
|
22
|
+
|
|
23
|
+
The following linting rules are available:
|
|
24
|
+
|
|
25
|
+
${await rule(shell, 'deprecated-functions', 'DeprecatedFunctionsConfig', 'Deprecated Functions', 'This rule detects the usage of deprecated functions in code based on a predefined list of known deprecated functions.', `
|
|
26
|
+
first <- data.frame(x = c(1, 2, 3), y = c(1, 2, 3))
|
|
27
|
+
second <- data.frame(x = c(1, 3, 2), y = c(1, 3, 2))
|
|
28
|
+
dplyr::all_equal(first, second)
|
|
29
|
+
`)}
|
|
30
|
+
|
|
31
|
+
${await rule(shell, 'file-path-validity', 'FilePathValidityConfig', 'File Path Validity', 'This rule finds places in the code where files are being read from. In such places, it checks whether the file path is valid and whether the file exists on disk.', `
|
|
32
|
+
my_data <- read.csv("C:/Users/me/Documents/My R Scripts/Reproducible.csv")
|
|
33
|
+
`)}
|
|
34
|
+
|
|
35
|
+
`.trim();
|
|
36
|
+
}
|
|
37
|
+
async function rule(shell, name, configType, friendlyName, description, example) {
|
|
38
|
+
const types = (0, doc_types_1.getTypesFromFolderAsMermaid)({
|
|
39
|
+
rootFolder: path_1.default.resolve('./src/linter/'),
|
|
40
|
+
typeName: configType,
|
|
41
|
+
inlineTypes: doc_types_1.mermaidHide
|
|
42
|
+
});
|
|
43
|
+
return `
|
|
44
|
+
### ${friendlyName} (\`${name}\`)
|
|
45
|
+
|
|
46
|
+
${description}
|
|
47
|
+
|
|
48
|
+
<details>
|
|
49
|
+
|
|
50
|
+
#### Configuration
|
|
51
|
+
|
|
52
|
+
Linting rules can be configured by passing a configuration object to the linter query as shown in the example below. The \`${name}\` rule accepts the following configuration options:
|
|
53
|
+
|
|
54
|
+
${Object.getOwnPropertyNames(linter_rules_1.LintingRules[name].defaultConfig).sort().map(key => `- ${(0, doc_types_1.shortLink)(`${configType}:::${key}`, types.info)}\\\n${(0, doc_types_1.getDocumentationForType)(`${configType}::${key}`, types.info)}`).join('\n')}
|
|
55
|
+
|
|
56
|
+
#### Example
|
|
57
|
+
|
|
58
|
+
${(0, doc_code_1.codeBlock)('r', example)}
|
|
59
|
+
|
|
60
|
+
The linting query can be used to run this rule on the above example:
|
|
61
|
+
|
|
62
|
+
${await (0, doc_query_1.showQuery)(shell, example, [{ type: 'linter', rules: [{ name, config: {} }] }], { collapseQuery: true })}
|
|
63
|
+
|
|
64
|
+
</details>
|
|
65
|
+
`.trim();
|
|
66
|
+
}
|
|
67
|
+
if (require.main === module) {
|
|
68
|
+
(0, log_1.setMinLevelOfAllLogs)(6 /* LogLevel.Fatal */);
|
|
69
|
+
const shell = new shell_1.RShell();
|
|
70
|
+
void getText(shell).then(str => {
|
|
71
|
+
console.log(str);
|
|
72
|
+
}).finally(() => {
|
|
73
|
+
shell.close();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=print-linter-wiki.js.map
|