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