@eagleoutice/flowr 2.2.15 → 2.3.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 +226 -6
- package/abstract-interpretation/data-frame/absint-info.d.ts +109 -0
- package/abstract-interpretation/data-frame/absint-info.js +31 -0
- package/abstract-interpretation/data-frame/absint-visitor.d.ts +59 -0
- package/abstract-interpretation/data-frame/absint-visitor.js +173 -0
- package/abstract-interpretation/data-frame/domain.d.ts +107 -0
- package/abstract-interpretation/data-frame/domain.js +315 -0
- package/abstract-interpretation/data-frame/mappers/access-mapper.d.ts +17 -0
- package/abstract-interpretation/data-frame/mappers/access-mapper.js +166 -0
- package/abstract-interpretation/data-frame/mappers/arguments.d.ts +117 -0
- package/abstract-interpretation/data-frame/mappers/arguments.js +188 -0
- package/abstract-interpretation/data-frame/mappers/assignment-mapper.d.ts +20 -0
- package/abstract-interpretation/data-frame/mappers/assignment-mapper.js +34 -0
- package/abstract-interpretation/data-frame/mappers/function-mapper.d.ts +261 -0
- package/abstract-interpretation/data-frame/mappers/function-mapper.js +1219 -0
- package/abstract-interpretation/data-frame/mappers/replacement-mapper.d.ts +12 -0
- package/abstract-interpretation/data-frame/mappers/replacement-mapper.js +206 -0
- package/abstract-interpretation/data-frame/resolve-args.d.ts +42 -0
- package/abstract-interpretation/data-frame/resolve-args.js +118 -0
- package/abstract-interpretation/data-frame/semantics.d.ts +213 -0
- package/abstract-interpretation/data-frame/semantics.js +366 -0
- package/abstract-interpretation/data-frame/shape-inference.d.ts +38 -0
- package/abstract-interpretation/data-frame/shape-inference.js +117 -0
- package/benchmark/slicer.d.ts +18 -2
- package/benchmark/slicer.js +143 -5
- package/benchmark/stats/print.js +123 -45
- package/benchmark/stats/size-of.d.ts +7 -0
- package/benchmark/stats/size-of.js +1 -0
- package/benchmark/stats/stats.d.ts +30 -1
- package/benchmark/stats/stats.js +4 -2
- package/benchmark/summarizer/data.d.ts +33 -2
- package/benchmark/summarizer/first-phase/input.js +5 -1
- package/benchmark/summarizer/first-phase/process.d.ts +2 -1
- package/benchmark/summarizer/first-phase/process.js +49 -3
- package/benchmark/summarizer/second-phase/process.js +101 -3
- package/cli/benchmark-app.d.ts +2 -0
- package/cli/benchmark-app.js +5 -1
- package/cli/benchmark-helper-app.d.ts +2 -0
- package/cli/benchmark-helper-app.js +13 -8
- package/cli/common/options.js +4 -0
- package/cli/export-quads-app.js +2 -1
- package/cli/flowr.js +58 -57
- package/cli/repl/commands/repl-cfg.js +13 -13
- package/cli/repl/commands/repl-commands.js +2 -2
- package/cli/repl/commands/repl-dataflow.js +10 -10
- package/cli/repl/commands/repl-execute.d.ts +2 -3
- package/cli/repl/commands/repl-execute.js +4 -4
- package/cli/repl/commands/repl-lineage.js +4 -4
- package/cli/repl/commands/repl-main.d.ts +12 -1
- package/cli/repl/commands/repl-normalize.js +6 -6
- package/cli/repl/commands/repl-parse.js +2 -2
- package/cli/repl/commands/repl-query.js +9 -9
- package/cli/repl/commands/repl-version.js +1 -1
- package/cli/repl/core.d.ts +5 -2
- package/cli/repl/core.js +10 -8
- package/cli/repl/server/connection.d.ts +3 -1
- package/cli/repl/server/connection.js +7 -5
- package/cli/repl/server/server.d.ts +3 -2
- package/cli/repl/server/server.js +4 -2
- package/cli/script-core/statistics-core.d.ts +2 -1
- package/cli/script-core/statistics-core.js +2 -2
- package/cli/script-core/statistics-helper-core.d.ts +2 -1
- package/cli/script-core/statistics-helper-core.js +5 -4
- package/cli/slicer-app.js +4 -2
- package/cli/statistics-app.js +2 -1
- package/cli/statistics-helper-app.js +2 -1
- package/config.d.ts +43 -10
- package/config.js +47 -43
- package/control-flow/cfg-dead-code.js +45 -2
- package/control-flow/cfg-simplification.d.ts +2 -0
- package/control-flow/control-flow-graph.d.ts +2 -0
- package/control-flow/control-flow-graph.js +8 -0
- package/control-flow/dfg-cfg-guided-visitor.d.ts +5 -3
- package/control-flow/dfg-cfg-guided-visitor.js +15 -4
- package/control-flow/extract-cfg.d.ts +4 -2
- package/control-flow/extract-cfg.js +4 -3
- package/control-flow/semantic-cfg-guided-visitor.d.ts +20 -2
- package/control-flow/semantic-cfg-guided-visitor.js +24 -4
- package/core/pipeline-executor.d.ts +4 -1
- package/core/pipeline-executor.js +6 -5
- package/core/steps/all/core/10-normalize.d.ts +2 -0
- package/core/steps/all/core/10-normalize.js +1 -1
- package/core/steps/all/core/11-normalize-tree-sitter.d.ts +2 -1
- package/core/steps/all/core/11-normalize-tree-sitter.js +2 -2
- package/core/steps/all/core/20-dataflow.d.ts +2 -1
- package/core/steps/all/core/20-dataflow.js +2 -2
- package/core/steps/all/static-slicing/00-slice.d.ts +2 -1
- package/core/steps/all/static-slicing/00-slice.js +2 -2
- package/core/steps/pipeline/default-pipelines.d.ts +32 -31
- package/core/steps/pipeline/default-pipelines.js +8 -8
- package/core/steps/pipeline-step.d.ts +2 -1
- package/dataflow/environments/built-in-config.d.ts +3 -3
- package/dataflow/environments/built-in.d.ts +11 -3
- package/dataflow/environments/built-in.js +5 -3
- package/dataflow/environments/default-builtin-config.js +4 -2
- package/dataflow/environments/define.d.ts +2 -1
- package/dataflow/environments/define.js +4 -5
- package/dataflow/environments/remove.d.ts +6 -0
- package/dataflow/environments/remove.js +29 -0
- package/dataflow/eval/resolve/alias-tracking.d.ts +7 -2
- package/dataflow/eval/resolve/alias-tracking.js +11 -8
- package/dataflow/eval/resolve/resolve-argument.d.ts +8 -0
- package/dataflow/eval/resolve/resolve-argument.js +118 -0
- package/dataflow/eval/resolve/resolve.d.ts +65 -18
- package/dataflow/eval/resolve/resolve.js +144 -48
- package/dataflow/eval/values/string/string-constants.d.ts +1 -1
- package/dataflow/eval/values/string/string-constants.js +7 -2
- package/dataflow/extractor.d.ts +2 -1
- package/dataflow/extractor.js +2 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-access.js +5 -6
- package/dataflow/internal/process/functions/call/built-in/built-in-apply.js +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.d.ts +4 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-assignment.js +11 -11
- package/dataflow/internal/process/functions/call/built-in/built-in-eval.js +10 -11
- package/dataflow/internal/process/functions/call/built-in/built-in-expression-list.js +7 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-for-loop.js +2 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-if-then-else.js +1 -1
- package/dataflow/internal/process/functions/call/built-in/built-in-list.js +2 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-replacement.js +2 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-source.d.ts +6 -3
- package/dataflow/internal/process/functions/call/built-in/built-in-source.js +19 -15
- package/dataflow/internal/process/functions/call/built-in/built-in-vector.js +2 -2
- package/dataflow/internal/process/functions/call/built-in/built-in-while-loop.js +1 -1
- package/dataflow/internal/process/functions/call/common.js +1 -1
- package/dataflow/internal/process/functions/process-parameter.js +1 -1
- package/dataflow/origin/dfg-get-symbol-refs.d.ts +21 -0
- package/dataflow/origin/dfg-get-symbol-refs.js +50 -0
- package/dataflow/processor.d.ts +5 -0
- package/documentation/doc-util/doc-cfg.js +4 -3
- package/documentation/doc-util/doc-code.d.ts +1 -1
- package/documentation/doc-util/doc-dfg.js +3 -2
- package/documentation/doc-util/doc-functions.d.ts +24 -0
- package/documentation/doc-util/doc-functions.js +65 -0
- package/documentation/doc-util/doc-normalized-ast.js +3 -2
- package/documentation/doc-util/doc-print.d.ts +5 -0
- package/documentation/doc-util/doc-print.js +36 -0
- package/documentation/doc-util/doc-query.js +13 -2
- package/documentation/doc-util/doc-repl.js +2 -1
- package/documentation/doc-util/doc-search.js +3 -2
- package/documentation/doc-util/doc-types.d.ts +28 -6
- package/documentation/doc-util/doc-types.js +89 -45
- package/documentation/print-cfg-wiki.js +6 -7
- package/documentation/print-core-wiki.js +5 -5
- package/documentation/print-dataflow-graph-wiki.js +10 -10
- package/documentation/print-engines-wiki.js +1 -2
- package/documentation/print-faq-wiki.js +8 -2
- package/documentation/print-interface-wiki.js +12 -2
- package/documentation/print-linter-issue.d.ts +1 -0
- package/documentation/print-linter-issue.js +71 -0
- package/documentation/print-linter-wiki.js +223 -34
- package/documentation/print-linting-and-testing-wiki.js +2 -4
- package/documentation/print-normalized-ast-wiki.js +3 -3
- package/documentation/print-query-wiki.js +18 -2
- package/documentation/print-readme.js +24 -1
- package/documentation/print-search-wiki.js +1 -2
- package/linter/linter-executor.d.ts +3 -1
- package/linter/linter-executor.js +3 -2
- package/linter/linter-format.d.ts +67 -7
- package/linter/linter-format.js +12 -1
- package/linter/linter-rules.d.ts +178 -16
- package/linter/linter-rules.js +14 -4
- package/linter/linter-tags.d.ts +80 -0
- package/linter/linter-tags.js +85 -0
- package/linter/rules/absolute-path.d.ts +71 -0
- package/linter/rules/absolute-path.js +177 -0
- package/linter/rules/dataframe-access-validation.d.ts +53 -0
- package/linter/rules/dataframe-access-validation.js +116 -0
- package/linter/rules/deprecated-functions.d.ts +43 -0
- package/linter/rules/deprecated-functions.js +58 -0
- package/linter/rules/{2-file-path-validity.d.ts → file-path-validity.d.ts} +16 -6
- package/linter/rules/{2-file-path-validity.js → file-path-validity.js} +21 -13
- package/linter/rules/naming-convention.d.ts +71 -0
- package/linter/rules/naming-convention.js +168 -0
- package/linter/rules/seeded-randomness.d.ts +65 -0
- package/linter/rules/seeded-randomness.js +122 -0
- package/linter/rules/unused-definition.d.ts +41 -0
- package/linter/rules/unused-definition.js +105 -0
- package/package.json +5 -2
- package/queries/base-query-format.d.ts +2 -0
- package/queries/catalog/call-context-query/call-context-query-executor.d.ts +1 -1
- package/queries/catalog/call-context-query/call-context-query-executor.js +2 -2
- package/queries/catalog/cluster-query/cluster-query-format.d.ts +1 -1
- package/queries/catalog/config-query/config-query-executor.d.ts +1 -1
- package/queries/catalog/config-query/config-query-executor.js +2 -3
- package/queries/catalog/control-flow-query/control-flow-query-executor.d.ts +1 -1
- package/queries/catalog/control-flow-query/control-flow-query-executor.js +2 -2
- package/queries/catalog/control-flow-query/control-flow-query-format.d.ts +1 -1
- package/queries/catalog/dataflow-lens-query/dataflow-lens-query-format.d.ts +1 -1
- package/queries/catalog/dataflow-query/dataflow-query-format.d.ts +1 -1
- package/queries/catalog/dependencies-query/dependencies-query-executor.js +4 -116
- package/queries/catalog/dependencies-query/dependencies-query-format.d.ts +1 -1
- package/queries/catalog/df-shape-query/df-shape-query-executor.d.ts +3 -0
- package/queries/catalog/df-shape-query/df-shape-query-executor.js +46 -0
- package/queries/catalog/df-shape-query/df-shape-query-format.d.ts +72 -0
- package/queries/catalog/df-shape-query/df-shape-query-format.js +31 -0
- package/queries/catalog/happens-before-query/happens-before-query-format.d.ts +1 -1
- package/queries/catalog/id-map-query/id-map-query-format.d.ts +1 -1
- package/queries/catalog/lineage-query/lineage-query-format.d.ts +1 -1
- package/queries/catalog/linter-query/linter-query-executor.d.ts +1 -1
- package/queries/catalog/linter-query/linter-query-executor.js +2 -2
- package/queries/catalog/linter-query/linter-query-format.d.ts +1 -1
- package/queries/catalog/linter-query/linter-query-format.js +16 -12
- package/queries/catalog/normalized-ast-query/normalized-ast-query-format.d.ts +1 -1
- package/queries/catalog/origin-query/origin-query-format.d.ts +1 -1
- package/queries/catalog/project-query/project-query-format.d.ts +1 -1
- package/queries/catalog/resolve-value-query/resolve-value-query-executor.d.ts +1 -1
- package/queries/catalog/resolve-value-query/resolve-value-query-executor.js +2 -2
- package/queries/catalog/resolve-value-query/resolve-value-query-format.d.ts +1 -1
- package/queries/catalog/search-query/search-query-executor.d.ts +1 -1
- package/queries/catalog/search-query/search-query-executor.js +2 -2
- package/queries/catalog/search-query/search-query-format.d.ts +1 -1
- package/queries/catalog/static-slice-query/static-slice-query-executor.d.ts +1 -1
- package/queries/catalog/static-slice-query/static-slice-query-executor.js +2 -2
- package/queries/catalog/static-slice-query/static-slice-query-format.d.ts +1 -1
- package/queries/query.d.ts +76 -16
- package/queries/query.js +2 -0
- package/r-bridge/lang-4.x/ast/parser/json/parser.d.ts +2 -1
- package/r-bridge/lang-4.x/ast/parser/json/parser.js +4 -2
- package/r-bridge/lang-4.x/convert-values.js +2 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-executor.d.ts +3 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-executor.js +4 -4
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.d.ts +1 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-normalize.js +7 -5
- package/r-bridge/shell.d.ts +3 -2
- package/r-bridge/shell.js +4 -5
- package/search/flowr-search-builder.d.ts +6 -2
- package/search/flowr-search-builder.js +7 -0
- package/search/flowr-search-filters.d.ts +32 -8
- package/search/flowr-search-filters.js +42 -15
- package/search/flowr-search.d.ts +4 -0
- package/search/search-executor/search-enrichers.d.ts +7 -3
- package/search/search-executor/search-enrichers.js +29 -20
- package/search/search-executor/search-generators.js +1 -1
- package/search/search-executor/search-transformer.d.ts +2 -0
- package/search/search-executor/search-transformer.js +10 -1
- package/slicing/static/static-slicer.d.ts +1 -1
- package/slicing/static/static-slicer.js +2 -3
- package/statistics/statistics.d.ts +3 -1
- package/statistics/statistics.js +5 -4
- package/util/containers.d.ts +12 -9
- package/util/containers.js +12 -9
- package/util/files.d.ts +8 -2
- package/util/files.js +22 -4
- package/util/objects.d.ts +5 -4
- package/util/r-value.d.ts +23 -0
- package/util/r-value.js +113 -0
- package/util/range.d.ts +5 -1
- package/util/range.js +11 -3
- package/util/text/strings.d.ts +6 -0
- package/util/text/strings.js +35 -0
- package/util/version.js +1 -1
- package/linter/rules/1-deprecated-functions.d.ts +0 -34
- package/linter/rules/1-deprecated-functions.js +0 -54
- package/util/cfg/cfg.d.ts +0 -0
- package/util/cfg/cfg.js +0 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { DataflowGraph } from '../../dataflow/graph/graph';
|
|
2
|
+
import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
3
|
+
import type { MergeableRecord } from '../../util/objects';
|
|
4
|
+
import type { SourceRange } from '../../util/range';
|
|
5
|
+
import type { LintQuickFixReplacement, LintingResult } from '../linter-format';
|
|
6
|
+
import { LintingCertainty } from '../linter-format';
|
|
7
|
+
import { LintingRuleTag } from '../linter-tags';
|
|
8
|
+
export declare enum CasingConvention {
|
|
9
|
+
CamelCase = "camelCase",
|
|
10
|
+
PascalCase = "PascalCase",
|
|
11
|
+
SnakeCase = "snake_case",
|
|
12
|
+
ConstantCase = "CONSTANT_CASE",
|
|
13
|
+
CamelSnakeCase = "camel_Snake_Case",
|
|
14
|
+
PascalSnakeCase = "Pascal_Snake_Case",
|
|
15
|
+
Unknown = "unknown"
|
|
16
|
+
}
|
|
17
|
+
export interface NamingConventionResult extends LintingResult {
|
|
18
|
+
name: string;
|
|
19
|
+
detectedCasing: CasingConvention;
|
|
20
|
+
range: SourceRange;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* It is planned to have a config like ESLint
|
|
24
|
+
*/
|
|
25
|
+
export interface NamingConventionConfig extends MergeableRecord {
|
|
26
|
+
caseing: CasingConvention | 'auto';
|
|
27
|
+
}
|
|
28
|
+
export interface NamingConventionMetadata extends MergeableRecord {
|
|
29
|
+
/** number of symbols matching the casing convetion */
|
|
30
|
+
numMatches: number;
|
|
31
|
+
/** number of symbols breaking the casing convetion */
|
|
32
|
+
numBreak: number;
|
|
33
|
+
}
|
|
34
|
+
export declare function detectCasing(identifier: string): CasingConvention;
|
|
35
|
+
export declare function getMostUsedCasing(symbols: {
|
|
36
|
+
detectedCasing: CasingConvention;
|
|
37
|
+
}[]): CasingConvention;
|
|
38
|
+
export declare function fixCasing(identifier: string, convention: CasingConvention): string;
|
|
39
|
+
export declare function createNamingConventionQuickFixes(graph: DataflowGraph, nodeId: NodeId, replacement: string, conv: CasingConvention): LintQuickFixReplacement[] | undefined;
|
|
40
|
+
export declare const NAMING_CONVENTION: {
|
|
41
|
+
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>[]>>;
|
|
42
|
+
readonly processSearchResult: (elements: 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>[]>, config: NamingConventionConfig, data: {
|
|
43
|
+
normalize: import("../../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst;
|
|
44
|
+
dataflow: import("../../dataflow/info").DataflowInformation;
|
|
45
|
+
config: import("../../config").FlowrConfigOptions;
|
|
46
|
+
}) => {
|
|
47
|
+
results: {
|
|
48
|
+
quickFix: LintQuickFixReplacement[] | undefined;
|
|
49
|
+
certainty: LintingCertainty;
|
|
50
|
+
detectedCasing: CasingConvention;
|
|
51
|
+
name: string;
|
|
52
|
+
range: SourceRange;
|
|
53
|
+
}[];
|
|
54
|
+
'.meta': {
|
|
55
|
+
numMatches: number;
|
|
56
|
+
numBreak: number;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
readonly prettyPrint: {
|
|
60
|
+
readonly query: (result: NamingConventionResult) => string;
|
|
61
|
+
readonly full: (result: NamingConventionResult) => string;
|
|
62
|
+
};
|
|
63
|
+
readonly info: {
|
|
64
|
+
readonly name: "Naming Convention";
|
|
65
|
+
readonly description: "Checks wether the symbols conform to a certain naming convention";
|
|
66
|
+
readonly tags: readonly [LintingRuleTag.Style, LintingRuleTag.QuickFix];
|
|
67
|
+
readonly defaultConfig: {
|
|
68
|
+
readonly caseing: "auto";
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NAMING_CONVENTION = exports.CasingConvention = void 0;
|
|
4
|
+
exports.detectCasing = detectCasing;
|
|
5
|
+
exports.getMostUsedCasing = getMostUsedCasing;
|
|
6
|
+
exports.fixCasing = fixCasing;
|
|
7
|
+
exports.createNamingConventionQuickFixes = createNamingConventionQuickFixes;
|
|
8
|
+
const vertex_1 = require("../../dataflow/graph/vertex");
|
|
9
|
+
const dfg_get_symbol_refs_1 = require("../../dataflow/origin/dfg-get-symbol-refs");
|
|
10
|
+
const flowr_search_builder_1 = require("../../search/flowr-search-builder");
|
|
11
|
+
const assert_1 = require("../../util/assert");
|
|
12
|
+
const dfg_1 = require("../../util/mermaid/dfg");
|
|
13
|
+
const linter_format_1 = require("../linter-format");
|
|
14
|
+
const linter_tags_1 = require("../linter-tags");
|
|
15
|
+
var CasingConvention;
|
|
16
|
+
(function (CasingConvention) {
|
|
17
|
+
CasingConvention["CamelCase"] = "camelCase";
|
|
18
|
+
CasingConvention["PascalCase"] = "PascalCase";
|
|
19
|
+
CasingConvention["SnakeCase"] = "snake_case";
|
|
20
|
+
CasingConvention["ConstantCase"] = "CONSTANT_CASE";
|
|
21
|
+
CasingConvention["CamelSnakeCase"] = "camel_Snake_Case";
|
|
22
|
+
CasingConvention["PascalSnakeCase"] = "Pascal_Snake_Case";
|
|
23
|
+
CasingConvention["Unknown"] = "unknown";
|
|
24
|
+
})(CasingConvention || (exports.CasingConvention = CasingConvention = {}));
|
|
25
|
+
function detectCasing(identifier) {
|
|
26
|
+
if (identifier.trim() === '') {
|
|
27
|
+
return CasingConvention.Unknown;
|
|
28
|
+
}
|
|
29
|
+
const upper = identifier.toUpperCase();
|
|
30
|
+
const lower = identifier.toLowerCase();
|
|
31
|
+
const isAllUpper = identifier === upper;
|
|
32
|
+
const isAllLower = identifier === lower;
|
|
33
|
+
if (identifier.includes('_')) {
|
|
34
|
+
if (isAllUpper) { // CONSTANT_CASE
|
|
35
|
+
return CasingConvention.ConstantCase;
|
|
36
|
+
}
|
|
37
|
+
else if (isAllLower) { // snake_case
|
|
38
|
+
return CasingConvention.SnakeCase;
|
|
39
|
+
}
|
|
40
|
+
// Returns true if the letter after an _ is uppercase
|
|
41
|
+
function expectUpperAfterScore(identifier) {
|
|
42
|
+
for (let i = 0; i < identifier.length - 1; i++) {
|
|
43
|
+
if (identifier[i] === '_') {
|
|
44
|
+
if (identifier[i + 1] !== upper[i + 1]) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (identifier[0] === lower[0] && expectUpperAfterScore(identifier)) { // camel_Snake_Case
|
|
52
|
+
return CasingConvention.CamelSnakeCase;
|
|
53
|
+
}
|
|
54
|
+
else if (identifier[0] === upper[0] && expectUpperAfterScore(identifier)) { // Pascal_Snake_Case
|
|
55
|
+
return CasingConvention.PascalSnakeCase;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
if (identifier[0] === lower[0]) { // camelCase
|
|
60
|
+
return CasingConvention.CamelCase;
|
|
61
|
+
}
|
|
62
|
+
else if (identifier[0] === upper[0]) { // PascalCase
|
|
63
|
+
return CasingConvention.PascalCase;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return CasingConvention.Unknown;
|
|
67
|
+
}
|
|
68
|
+
function getMostUsedCasing(symbols) {
|
|
69
|
+
if (symbols.length === 0) {
|
|
70
|
+
return CasingConvention.Unknown;
|
|
71
|
+
}
|
|
72
|
+
const map = new Map();
|
|
73
|
+
for (const symbol of symbols) {
|
|
74
|
+
const o = map.get(symbol.detectedCasing) ?? 0;
|
|
75
|
+
map.set(symbol.detectedCasing, o + 1);
|
|
76
|
+
}
|
|
77
|
+
// Return element with most occurances
|
|
78
|
+
return [...map].reduce((p, c) => p[1] > c[1] ? p : c)[0];
|
|
79
|
+
}
|
|
80
|
+
function fixCasing(identifier, convention) {
|
|
81
|
+
const tokens = identifier.split(/(?=[A-Z])|_/).map(s => s.toLowerCase());
|
|
82
|
+
const firstUp = (s) => `${s[0].toUpperCase()}${s.substring(1)}`;
|
|
83
|
+
switch (convention) {
|
|
84
|
+
case CasingConvention.CamelCase: // camelCase
|
|
85
|
+
return `${tokens[0]}${tokens.slice(1).map(firstUp).join('')}`;
|
|
86
|
+
case CasingConvention.PascalCase: // PascalCase
|
|
87
|
+
return tokens.map(firstUp).join('');
|
|
88
|
+
case CasingConvention.SnakeCase: // snake_case
|
|
89
|
+
return tokens.join('_');
|
|
90
|
+
case CasingConvention.ConstantCase: // CONSTANT_CASE
|
|
91
|
+
return tokens.map(s => s.toUpperCase()).join('_');
|
|
92
|
+
case CasingConvention.CamelSnakeCase: // camel_Snake_Case
|
|
93
|
+
return `${tokens[0]}_${tokens.slice(1).map(firstUp).join('_')}`;
|
|
94
|
+
case CasingConvention.PascalSnakeCase: // Pascal_Snake_Case
|
|
95
|
+
return tokens.map(firstUp).join('_');
|
|
96
|
+
case CasingConvention.Unknown:
|
|
97
|
+
return identifier;
|
|
98
|
+
default:
|
|
99
|
+
(0, assert_1.assertUnreachable)(convention);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function createNamingConventionQuickFixes(graph, nodeId, replacement, conv) {
|
|
103
|
+
const refs = (0, dfg_get_symbol_refs_1.getAllRefsToSymbol)(graph, nodeId);
|
|
104
|
+
const idMap = graph.idMap;
|
|
105
|
+
if (refs === undefined || idMap === undefined) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const result = [];
|
|
109
|
+
for (const ref of refs) {
|
|
110
|
+
const node = idMap.get(ref);
|
|
111
|
+
if (node === undefined) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const range = node.info.fullRange;
|
|
115
|
+
if (range) {
|
|
116
|
+
// In case of a function call we only need to include the name, not the '()'
|
|
117
|
+
range[3] = range[1] + node.lexeme.length - 1;
|
|
118
|
+
result.push({
|
|
119
|
+
type: 'replace',
|
|
120
|
+
replacement: replacement,
|
|
121
|
+
description: `Rename to match naming convention ${conv}`,
|
|
122
|
+
range: range
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return result.length === 0 ?
|
|
127
|
+
undefined : // We sort so that when applied in order the fixes will start from the end of the line to avoid conflicts
|
|
128
|
+
result.sort((a, b) => a.range[0] == b.range[0] ? b.range[1] - a.range[1] : b.range[0] - a.range[0]);
|
|
129
|
+
}
|
|
130
|
+
exports.NAMING_CONVENTION = {
|
|
131
|
+
createSearch: (_config) => flowr_search_builder_1.Q.all().filter(vertex_1.VertexType.VariableDefinition),
|
|
132
|
+
processSearchResult: (elements, config, data) => {
|
|
133
|
+
const symbols = elements.getElements()
|
|
134
|
+
.map(m => ({
|
|
135
|
+
certainty: linter_format_1.LintingCertainty.Definitely,
|
|
136
|
+
detectedCasing: detectCasing(m.node.lexeme),
|
|
137
|
+
name: m.node.lexeme,
|
|
138
|
+
range: m.node.info.fullRange,
|
|
139
|
+
id: m.node.info.id
|
|
140
|
+
}));
|
|
141
|
+
const casing = config.caseing === 'auto' ? getMostUsedCasing(symbols) : config.caseing;
|
|
142
|
+
const results = symbols.filter(m => m.detectedCasing !== casing)
|
|
143
|
+
.map(({ id, ...m }) => ({
|
|
144
|
+
...m,
|
|
145
|
+
quickFix: createNamingConventionQuickFixes(data.dataflow.graph, id, fixCasing(m.name, casing), casing)
|
|
146
|
+
}));
|
|
147
|
+
return {
|
|
148
|
+
results: results,
|
|
149
|
+
'.meta': {
|
|
150
|
+
numMatches: symbols.length - results.length,
|
|
151
|
+
numBreak: results.length
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
prettyPrint: {
|
|
156
|
+
[linter_format_1.LintingPrettyPrintContext.Query]: result => `Identifier '${result.name}' at ${(0, dfg_1.formatRange)(result.range)} (${result.detectedCasing})`,
|
|
157
|
+
[linter_format_1.LintingPrettyPrintContext.Full]: result => `Identifier '${result.name}' at ${(0, dfg_1.formatRange)(result.range)} follows wrong convention: ${result.detectedCasing}`
|
|
158
|
+
},
|
|
159
|
+
info: {
|
|
160
|
+
name: 'Naming Convention',
|
|
161
|
+
description: 'Checks wether the symbols conform to a certain naming convention',
|
|
162
|
+
tags: [linter_tags_1.LintingRuleTag.Style, linter_tags_1.LintingRuleTag.QuickFix],
|
|
163
|
+
defaultConfig: {
|
|
164
|
+
caseing: 'auto'
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
//# sourceMappingURL=naming-convention.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { LintingResult } from '../linter-format';
|
|
2
|
+
import { LintingCertainty } from '../linter-format';
|
|
3
|
+
import type { SourceRange } from '../../util/range';
|
|
4
|
+
import type { MergeableRecord } from '../../util/objects';
|
|
5
|
+
import type { Identifier } from '../../dataflow/environments/identifier';
|
|
6
|
+
import { LintingRuleTag } from '../linter-tags';
|
|
7
|
+
export interface SeededRandomnessResult extends LintingResult {
|
|
8
|
+
function: string;
|
|
9
|
+
range: SourceRange;
|
|
10
|
+
}
|
|
11
|
+
export interface SeededRandomnessConfig extends MergeableRecord {
|
|
12
|
+
/**
|
|
13
|
+
* A set of functions and variables whose invocation or assignment causes a random seeded to be set.
|
|
14
|
+
* Each entry has a `type`, which is either `function` or `assignment`, and a `name`, which is the name of the function or variable.
|
|
15
|
+
* The default value for this is the function `set.seed` and the variable `.Random.seed`.
|
|
16
|
+
*/
|
|
17
|
+
randomnessProducers: {
|
|
18
|
+
type: 'function' | 'assignment';
|
|
19
|
+
name: string;
|
|
20
|
+
}[];
|
|
21
|
+
/**
|
|
22
|
+
* A set of randomness consumer function names that require a seed to be set prior to invocation.
|
|
23
|
+
*/
|
|
24
|
+
randomnessConsumers: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface SeededRandomnessMeta extends MergeableRecord {
|
|
27
|
+
consumerCalls: number;
|
|
28
|
+
callsWithFunctionProducers: number;
|
|
29
|
+
callsWithAssignmentProducers: number;
|
|
30
|
+
callsWithNonConstantProducers: number;
|
|
31
|
+
}
|
|
32
|
+
export declare const SEEDED_RANDOMNESS: {
|
|
33
|
+
readonly createSearch: (config: SeededRandomnessConfig) => import("../../search/flowr-search-builder").FlowrSearchBuilder<"all", ["with", "filter", "with"], 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/search-executor/search-enrichers").EnrichedFlowrSearchElement<import("../../r-bridge/lang-4.x/ast/model/processing/decorate").ParentInformation>[]>>;
|
|
34
|
+
readonly processSearchResult: (elements: 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>[]>, config: SeededRandomnessConfig, { dataflow }: {
|
|
35
|
+
normalize: import("../../r-bridge/lang-4.x/ast/model/processing/decorate").NormalizedAst;
|
|
36
|
+
dataflow: import("../../dataflow/info").DataflowInformation;
|
|
37
|
+
config: import("../../config").FlowrConfigOptions;
|
|
38
|
+
}) => {
|
|
39
|
+
results: {
|
|
40
|
+
certainty: LintingCertainty.Definitely;
|
|
41
|
+
function: Identifier;
|
|
42
|
+
range: SourceRange;
|
|
43
|
+
}[];
|
|
44
|
+
'.meta': SeededRandomnessMeta;
|
|
45
|
+
};
|
|
46
|
+
readonly info: {
|
|
47
|
+
readonly defaultConfig: {
|
|
48
|
+
readonly randomnessProducers: readonly [{
|
|
49
|
+
readonly type: "function";
|
|
50
|
+
readonly name: "set.seed";
|
|
51
|
+
}, {
|
|
52
|
+
readonly type: "assignment";
|
|
53
|
+
readonly name: ".Random.seed";
|
|
54
|
+
}];
|
|
55
|
+
readonly randomnessConsumers: readonly ["jitter", "sample", "sample.int", "arima.sim", "kmeans", "princomp", "rcauchy", "rchisq", "rexp", "rgamma", "rgeom", "rlnorm", "rlogis", "rmultinom", "rnbinom", "rnorm", "rpois", "runif", "pointLabel", "some", "rbernoulli", "rdunif", "generateSeedVectors"];
|
|
56
|
+
};
|
|
57
|
+
readonly tags: readonly [LintingRuleTag.Robustness, LintingRuleTag.Reproducibility];
|
|
58
|
+
readonly name: "Seeded Randomness";
|
|
59
|
+
readonly 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`.";
|
|
60
|
+
};
|
|
61
|
+
readonly prettyPrint: {
|
|
62
|
+
readonly query: (result: SeededRandomnessResult, _meta: SeededRandomnessMeta) => string;
|
|
63
|
+
readonly full: (result: SeededRandomnessResult, _meta: SeededRandomnessMeta) => string;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SEEDED_RANDOMNESS = void 0;
|
|
4
|
+
const linter_format_1 = require("../linter-format");
|
|
5
|
+
const flowr_search_builder_1 = require("../../search/flowr-search-builder");
|
|
6
|
+
const dfg_1 = require("../../util/mermaid/dfg");
|
|
7
|
+
const search_enrichers_1 = require("../../search/search-executor/search-enrichers");
|
|
8
|
+
const flowr_search_filters_1 = require("../../search/flowr-search-filters");
|
|
9
|
+
const default_builtin_config_1 = require("../../dataflow/environments/default-builtin-config");
|
|
10
|
+
const graph_1 = require("../../dataflow/graph/graph");
|
|
11
|
+
const cascade_action_1 = require("../../queries/catalog/call-context-query/cascade-action");
|
|
12
|
+
const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id");
|
|
13
|
+
const linter_tags_1 = require("../linter-tags");
|
|
14
|
+
const alias_tracking_1 = require("../../dataflow/eval/resolve/alias-tracking");
|
|
15
|
+
const general_1 = require("../../dataflow/eval/values/general");
|
|
16
|
+
const config_1 = require("../../config");
|
|
17
|
+
const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
|
|
18
|
+
const r_value_1 = require("../../dataflow/eval/values/r-value");
|
|
19
|
+
exports.SEEDED_RANDOMNESS = {
|
|
20
|
+
createSearch: (config) => flowr_search_builder_1.Q.all()
|
|
21
|
+
.with(search_enrichers_1.Enrichment.CallTargets, { onlyBuiltin: true })
|
|
22
|
+
.filter({
|
|
23
|
+
name: flowr_search_filters_1.FlowrFilter.MatchesEnrichment,
|
|
24
|
+
args: {
|
|
25
|
+
enrichment: search_enrichers_1.Enrichment.CallTargets,
|
|
26
|
+
test: (0, flowr_search_filters_1.testFunctionsIgnoringPackage)(config.randomnessConsumers)
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
.with(search_enrichers_1.Enrichment.LastCall, [
|
|
30
|
+
...config.randomnessProducers.filter(p => p.type === 'function').map(p => ({ callName: p.name })),
|
|
31
|
+
...getDefaultAssignments().flatMap(b => b.names).map(a => ({ callName: a, cascadeIf: () => cascade_action_1.CascadeAction.Continue }))
|
|
32
|
+
]),
|
|
33
|
+
processSearchResult: (elements, config, { dataflow }) => {
|
|
34
|
+
const assignmentProducers = new Set(config.randomnessProducers.filter(p => p.type == 'assignment').map(p => p.name));
|
|
35
|
+
const assignmentArgIndexes = new Map(getDefaultAssignments().flatMap(a => a.names.map(n => ([n, a.config?.swapSourceAndTarget ? 1 : 0]))));
|
|
36
|
+
const metadata = {
|
|
37
|
+
consumerCalls: 0,
|
|
38
|
+
callsWithFunctionProducers: 0,
|
|
39
|
+
callsWithAssignmentProducers: 0,
|
|
40
|
+
callsWithNonConstantProducers: 0
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
results: elements.getElements()
|
|
44
|
+
// map and filter consumers
|
|
45
|
+
.flatMap(element => (0, search_enrichers_1.enrichmentContent)(element, search_enrichers_1.Enrichment.CallTargets).targets.map(target => {
|
|
46
|
+
metadata.consumerCalls++;
|
|
47
|
+
return {
|
|
48
|
+
range: element.node.info.fullRange,
|
|
49
|
+
target: target,
|
|
50
|
+
searchElement: element
|
|
51
|
+
};
|
|
52
|
+
}))
|
|
53
|
+
// filter by calls that aren't preceded by a randomness producer
|
|
54
|
+
.filter(element => {
|
|
55
|
+
const producers = (0, search_enrichers_1.enrichmentContent)(element.searchElement, search_enrichers_1.Enrichment.LastCall).linkedIds
|
|
56
|
+
.map(e => dataflow.graph.getVertex(e.node.info.id));
|
|
57
|
+
const { assignment, func } = Object.groupBy(producers, f => assignmentArgIndexes.has(f.name) ? 'assignment' : 'func');
|
|
58
|
+
let nonConstant = false;
|
|
59
|
+
// function calls are already taken care of through the LastCall enrichment itself
|
|
60
|
+
for (const f of func ?? []) {
|
|
61
|
+
if (isConstantArgument(dataflow.graph, f, 0)) {
|
|
62
|
+
metadata.callsWithFunctionProducers++;
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
nonConstant = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// assignments have to be queried for their destination
|
|
70
|
+
for (const a of assignment ?? []) {
|
|
71
|
+
const argIdx = assignmentArgIndexes.get(a.name);
|
|
72
|
+
const dest = (0, graph_1.getReferenceOfArgument)(a.args[argIdx]);
|
|
73
|
+
if (dest !== undefined && assignmentProducers.has((0, node_id_1.recoverName)(dest, dataflow.graph.idMap))) {
|
|
74
|
+
if (isConstantArgument(dataflow.graph, a, 1 - argIdx)) {
|
|
75
|
+
metadata.callsWithAssignmentProducers++;
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
nonConstant = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (nonConstant) {
|
|
84
|
+
metadata.callsWithNonConstantProducers++;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
})
|
|
88
|
+
.map(element => ({
|
|
89
|
+
certainty: linter_format_1.LintingCertainty.Definitely,
|
|
90
|
+
function: element.target,
|
|
91
|
+
range: element.range
|
|
92
|
+
})),
|
|
93
|
+
'.meta': metadata
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
info: {
|
|
97
|
+
defaultConfig: {
|
|
98
|
+
randomnessProducers: [{ type: 'function', name: 'set.seed' }, { type: 'assignment', name: '.Random.seed' }],
|
|
99
|
+
randomnessConsumers: ['jitter', 'sample', 'sample.int', 'arima.sim', 'kmeans', 'princomp', 'rcauchy', 'rchisq', 'rexp', 'rgamma', 'rgeom', 'rlnorm', 'rlogis', 'rmultinom', 'rnbinom', 'rnorm', 'rpois', 'runif', 'pointLabel', 'some', 'rbernoulli', 'rdunif', 'generateSeedVectors'],
|
|
100
|
+
},
|
|
101
|
+
tags: [linter_tags_1.LintingRuleTag.Robustness, linter_tags_1.LintingRuleTag.Reproducibility],
|
|
102
|
+
name: 'Seeded Randomness',
|
|
103
|
+
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`.'
|
|
104
|
+
},
|
|
105
|
+
prettyPrint: {
|
|
106
|
+
[linter_format_1.LintingPrettyPrintContext.Query]: (result, _meta) => `Function \`${result.function}\` at ${(0, dfg_1.formatRange)(result.range)}`,
|
|
107
|
+
[linter_format_1.LintingPrettyPrintContext.Full]: (result, _meta) => `Function \`${result.function}\` at ${(0, dfg_1.formatRange)(result.range)} is called without a preceding random seed function like \`set.seed\``
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
function getDefaultAssignments() {
|
|
111
|
+
return default_builtin_config_1.DefaultBuiltinConfig.filter(b => b.type === 'function' && b.processor == 'builtin:assignment');
|
|
112
|
+
}
|
|
113
|
+
function isConstantArgument(graph, call, argIndex) {
|
|
114
|
+
const args = call.args.filter(arg => arg !== r_function_call_1.EmptyArgument && !arg.name).map(graph_1.getReferenceOfArgument);
|
|
115
|
+
const values = (0, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(args[argIndex], { graph: graph, resolve: config_1.VariableResolve.Alias }));
|
|
116
|
+
return values?.elements.every(v => v.type === 'number' ||
|
|
117
|
+
v.type === 'logical' ||
|
|
118
|
+
v.type === 'string' ||
|
|
119
|
+
v.type === 'interval' && v.startInclusive && v.endInclusive && v.start.type === 'number' && v.end.type === 'number' && (0, r_value_1.asValue)(v.start.value).num === (0, r_value_1.asValue)(v.end.value).num)
|
|
120
|
+
?? false;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=seeded-randomness.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { LintingResult } from '../linter-format';
|
|
2
|
+
import type { MergeableRecord } from '../../util/objects';
|
|
3
|
+
import type { SourceRange } from '../../util/range';
|
|
4
|
+
import { LintingRuleTag } from '../linter-tags';
|
|
5
|
+
import type { NormalizedAst, ParentInformation } from '../../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
6
|
+
export interface UnusedDefinitionResult extends LintingResult {
|
|
7
|
+
variableName?: string;
|
|
8
|
+
range: SourceRange;
|
|
9
|
+
}
|
|
10
|
+
export interface UnusedDefinitionConfig extends MergeableRecord {
|
|
11
|
+
/**
|
|
12
|
+
* Whether to include (potentially anonymous) function definitions in the search (e.g., should we report uncalled anonymous functions?).
|
|
13
|
+
*/
|
|
14
|
+
includeFunctionDefinitions: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface UnusedDefinitionMetadata extends MergeableRecord {
|
|
17
|
+
totalConsidered: number;
|
|
18
|
+
}
|
|
19
|
+
export declare const UNUSED_DEFINITION: {
|
|
20
|
+
readonly createSearch: (config: UnusedDefinitionConfig) => import("../../search/flowr-search-builder").FlowrSearchBuilder<"all", ["filter"], ParentInformation, import("../../search/flowr-search").FlowrSearchElements<ParentInformation, [] | import("../../search/flowr-search").FlowrSearchElement<ParentInformation>[]>>;
|
|
21
|
+
readonly processSearchResult: (elements: import("../../search/flowr-search").FlowrSearchElements<ParentInformation, import("../../search/flowr-search").FlowrSearchElement<ParentInformation>[]>, config: UnusedDefinitionConfig, data: {
|
|
22
|
+
normalize: NormalizedAst;
|
|
23
|
+
dataflow: import("../../dataflow/info").DataflowInformation;
|
|
24
|
+
config: import("../../config").FlowrConfigOptions;
|
|
25
|
+
}) => {
|
|
26
|
+
results: UnusedDefinitionResult[];
|
|
27
|
+
".meta": UnusedDefinitionMetadata;
|
|
28
|
+
};
|
|
29
|
+
readonly prettyPrint: {
|
|
30
|
+
readonly query: (result: UnusedDefinitionResult) => string;
|
|
31
|
+
readonly full: (result: UnusedDefinitionResult) => string;
|
|
32
|
+
};
|
|
33
|
+
readonly info: {
|
|
34
|
+
readonly name: "Unused Definitions";
|
|
35
|
+
readonly description: "Checks for unused definitions.";
|
|
36
|
+
readonly tags: readonly [LintingRuleTag.Readability, LintingRuleTag.Smell, LintingRuleTag.QuickFix];
|
|
37
|
+
readonly defaultConfig: {
|
|
38
|
+
readonly includeFunctionDefinitions: true;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UNUSED_DEFINITION = void 0;
|
|
4
|
+
const linter_format_1 = require("../linter-format");
|
|
5
|
+
const flowr_search_builder_1 = require("../../search/flowr-search-builder");
|
|
6
|
+
const range_1 = require("../../util/range");
|
|
7
|
+
const dfg_1 = require("../../util/mermaid/dfg");
|
|
8
|
+
const linter_tags_1 = require("../linter-tags");
|
|
9
|
+
const assert_1 = require("../../util/assert");
|
|
10
|
+
const vertex_1 = require("../../dataflow/graph/vertex");
|
|
11
|
+
const edge_1 = require("../../dataflow/graph/edge");
|
|
12
|
+
const flowr_search_filters_1 = require("../../search/flowr-search-filters");
|
|
13
|
+
const InterestingEdgesVariable = edge_1.EdgeType.Reads | edge_1.EdgeType.Calls | edge_1.EdgeType.DefinesOnCall;
|
|
14
|
+
const InterestingEdgesFunction = edge_1.EdgeType.Reads | edge_1.EdgeType.Calls; // include read as this could print the function definition
|
|
15
|
+
const InterestingEdgesTargets = edge_1.EdgeType.SideEffectOnCall;
|
|
16
|
+
function getDefinitionArguments(def, dfg) {
|
|
17
|
+
return [...dfg.outgoingEdges(def) ?? []].filter(([, { types }]) => (0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.DefinedBy))
|
|
18
|
+
.map(([target]) => target);
|
|
19
|
+
}
|
|
20
|
+
function buildQuickFix(variable, dfg, ast) {
|
|
21
|
+
// first we check whether any of the 'Defined by' targets have any obligations - if so, we can not remove the definition
|
|
22
|
+
// otherwise we can automatically remove the full definition!
|
|
23
|
+
if (variable.info.role === "accessed" /* RoleInParent.Accessed */ || variable.info.role === "for-variable" /* RoleInParent.ForVariable */) {
|
|
24
|
+
// this is an access or a for variable, we can not remove it currently
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const definedBys = getDefinitionArguments(variable.info.id, dfg);
|
|
28
|
+
const hasImportantArgs = definedBys.some(d => dfg.unknownSideEffects.has(d))
|
|
29
|
+
|| definedBys.flatMap(e => [...dfg.outgoingEdges(e) ?? []])
|
|
30
|
+
.some(([target, { types }]) => {
|
|
31
|
+
return (0, edge_1.edgeIncludesType)(types, InterestingEdgesTargets) || dfg.unknownSideEffects.has(target);
|
|
32
|
+
});
|
|
33
|
+
if (hasImportantArgs) {
|
|
34
|
+
return undefined; // we can not remove this definition, it has important arguments
|
|
35
|
+
}
|
|
36
|
+
const totalRangeToRemove = (0, range_1.mergeRanges)(...definedBys.map(d => {
|
|
37
|
+
const vertex = ast.idMap.get(d);
|
|
38
|
+
return vertex?.info.fullRange ?? vertex?.location;
|
|
39
|
+
}), variable.info.fullRange ?? variable.location);
|
|
40
|
+
return [{
|
|
41
|
+
type: 'remove',
|
|
42
|
+
range: totalRangeToRemove,
|
|
43
|
+
description: `Remove unused definition of \`${variable.lexeme}\``
|
|
44
|
+
}];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
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
|
|
48
|
+
*/
|
|
49
|
+
function onlyKeepSupersetOfUnused(elements) {
|
|
50
|
+
const ranges = elements.flatMap(e => e.quickFix?.map(q => q.range) ?? [e.range]);
|
|
51
|
+
if (ranges.length <= 1) {
|
|
52
|
+
return elements; // nothing to filter, only one element
|
|
53
|
+
}
|
|
54
|
+
return elements.filter(e => {
|
|
55
|
+
const otherRange = (0, range_1.mergeRanges)(...(e.quickFix?.map(q => q.range) ?? [e.range]));
|
|
56
|
+
return !ranges.some(r => (0, range_1.rangeCompare)(r, otherRange) !== 0 && (0, range_1.rangeIsSubsetOf)(otherRange, r)); // there is no smaller remove
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
exports.UNUSED_DEFINITION = {
|
|
60
|
+
/* this can be done better once we have types */
|
|
61
|
+
createSearch: config => flowr_search_builder_1.Q.all().filter(config.includeFunctionDefinitions ? flowr_search_filters_1.FlowrFilterCombinator.is(vertex_1.VertexType.VariableDefinition).or(vertex_1.VertexType.FunctionDefinition) : vertex_1.VertexType.VariableDefinition),
|
|
62
|
+
processSearchResult: (elements, config, data) => {
|
|
63
|
+
const metadata = {
|
|
64
|
+
totalConsidered: 0
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
results: onlyKeepSupersetOfUnused(elements.getElements().flatMap(element => {
|
|
68
|
+
metadata.totalConsidered++;
|
|
69
|
+
const dfgVertex = data.dataflow.graph.getVertex(element.node.info.id);
|
|
70
|
+
if (!dfgVertex || (!(0, vertex_1.isVariableDefinitionVertex)(dfgVertex)
|
|
71
|
+
&& (0, vertex_1.isFunctionDefinitionVertex)(dfgVertex) && !config.includeFunctionDefinitions)) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
const ingoingEdges = data.dataflow.graph.ingoingEdges(dfgVertex.id);
|
|
75
|
+
const interestedIn = (0, vertex_1.isVariableDefinitionVertex)(dfgVertex) ? InterestingEdgesVariable : InterestingEdgesFunction;
|
|
76
|
+
const ingoingInteresting = ingoingEdges?.values().some(e => (0, edge_1.edgeIncludesType)(e.types, interestedIn));
|
|
77
|
+
if (ingoingInteresting) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
// found an unused definition
|
|
81
|
+
const variableName = element.node.lexeme;
|
|
82
|
+
return [{
|
|
83
|
+
certainty: linter_format_1.LintingCertainty.Maybe,
|
|
84
|
+
variableName,
|
|
85
|
+
range: element.node.info.fullRange ?? element.node.location ?? (0, range_1.rangeFrom)(-1, -1, -1, -1),
|
|
86
|
+
quickFix: buildQuickFix(element.node, data.dataflow.graph, data.normalize)
|
|
87
|
+
}];
|
|
88
|
+
}).filter(assert_1.isNotUndefined)),
|
|
89
|
+
'.meta': metadata
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
prettyPrint: {
|
|
93
|
+
[linter_format_1.LintingPrettyPrintContext.Query]: result => `Definition of \`${result.variableName}\` at ${(0, dfg_1.formatRange)(result.range)}`,
|
|
94
|
+
[linter_format_1.LintingPrettyPrintContext.Full]: result => `Definition of \`${result.variableName}\` at ${(0, dfg_1.formatRange)(result.range)} is unused`
|
|
95
|
+
},
|
|
96
|
+
info: {
|
|
97
|
+
name: 'Unused Definitions',
|
|
98
|
+
description: 'Checks for unused definitions.',
|
|
99
|
+
tags: [linter_tags_1.LintingRuleTag.Readability, linter_tags_1.LintingRuleTag.Smell, linter_tags_1.LintingRuleTag.QuickFix],
|
|
100
|
+
defaultConfig: {
|
|
101
|
+
includeFunctionDefinitions: true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=unused-definition.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/flowr",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.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": {
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"wiki:cfg": "ts-node src/documentation/print-cfg-wiki.ts",
|
|
40
40
|
"wiki:linter": "ts-node src/documentation/print-linter-wiki.ts",
|
|
41
41
|
"gen:readme": "ts-node src/documentation/print-readme.ts",
|
|
42
|
+
"gen:linter-issue": "ts-node src/documentation/print-linter-issue.ts",
|
|
42
43
|
"build": "tsc --project .",
|
|
43
44
|
"build-dev": "npm run build && npm run build:copy-wasm",
|
|
44
45
|
"build:bundle-flowr": "npm run build && esbuild --bundle dist/src/cli/flowr.js --platform=node --tree-shaking=true --bundle --minify --external:clipboardy --target=node22 --outfile=dist/src/cli/flowr.min.js && npm run build:copy-wasm",
|
|
@@ -175,7 +176,7 @@
|
|
|
175
176
|
"devDependencies": {
|
|
176
177
|
"@commitlint/cli": "^19.7.1",
|
|
177
178
|
"@commitlint/config-angular": "^19.7.1",
|
|
178
|
-
"@eagleoutice/eslint-config-flowr": "^1.0.
|
|
179
|
+
"@eagleoutice/eslint-config-flowr": "^1.0.19",
|
|
179
180
|
"@eslint/eslintrc": "^3.2.0",
|
|
180
181
|
"@eslint/js": "^9.20.0",
|
|
181
182
|
"@j-ulrich/release-it-regex-bumper": "^5.3.0",
|
|
@@ -184,6 +185,7 @@
|
|
|
184
185
|
"@types/n-readlines": "^1.0.6",
|
|
185
186
|
"@types/n3": "^1.21.1",
|
|
186
187
|
"@types/object-hash": "^3.0.6",
|
|
188
|
+
"@types/seedrandom": "^3.0.8",
|
|
187
189
|
"@types/semver": "^7.5.8",
|
|
188
190
|
"@types/tmp": "^0.2.6",
|
|
189
191
|
"@types/ws": "^8.5.14",
|
|
@@ -215,6 +217,7 @@
|
|
|
215
217
|
"object-hash": "^3.0.0",
|
|
216
218
|
"object-sizeof": "^2.6.5",
|
|
217
219
|
"rotating-file-stream": "^3.2.6",
|
|
220
|
+
"seedrandom": "^3.0.5",
|
|
218
221
|
"semver": "^7.7.1",
|
|
219
222
|
"tar": "^7.4.3",
|
|
220
223
|
"tmp": "^0.2.3",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { NormalizedAst } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
|
|
2
2
|
import type { DataflowInformation } from '../dataflow/info';
|
|
3
|
+
import type { FlowrConfigOptions } from '../config';
|
|
3
4
|
export interface BaseQueryFormat {
|
|
4
5
|
/** used to select the query type :) */
|
|
5
6
|
readonly type: string;
|
|
@@ -14,4 +15,5 @@ export interface BaseQueryResult {
|
|
|
14
15
|
export interface BasicQueryData {
|
|
15
16
|
readonly ast: NormalizedAst;
|
|
16
17
|
readonly dataflow: DataflowInformation;
|
|
18
|
+
readonly config: FlowrConfigOptions;
|
|
17
19
|
}
|
|
@@ -9,4 +9,4 @@ import type { BasicQueryData } from '../../base-query-format';
|
|
|
9
9
|
* This happens during the main resolution!
|
|
10
10
|
* 4. Attach `linkTo` calls to the respective calls.
|
|
11
11
|
*/
|
|
12
|
-
export declare function executeCallContextQueries({ dataflow: { graph }, ast }: BasicQueryData, queries: readonly CallContextQuery[]): CallContextQueryResult;
|
|
12
|
+
export declare function executeCallContextQueries({ dataflow: { graph }, ast, config }: BasicQueryData, queries: readonly CallContextQuery[]): CallContextQueryResult;
|
|
@@ -171,7 +171,7 @@ function isParameterDefaultValue(nodeId, ast) {
|
|
|
171
171
|
* This happens during the main resolution!
|
|
172
172
|
* 4. Attach `linkTo` calls to the respective calls.
|
|
173
173
|
*/
|
|
174
|
-
function executeCallContextQueries({ dataflow: { graph }, ast }, queries) {
|
|
174
|
+
function executeCallContextQueries({ dataflow: { graph }, ast, config }, queries) {
|
|
175
175
|
/* omit performance page load */
|
|
176
176
|
const now = Date.now();
|
|
177
177
|
/* the node id and call targets if present */
|
|
@@ -180,7 +180,7 @@ function executeCallContextQueries({ dataflow: { graph }, ast }, queries) {
|
|
|
180
180
|
const { promotedQueries, requiresCfg } = promoteQueryCallNames(queries);
|
|
181
181
|
let cfg = undefined;
|
|
182
182
|
if (requiresCfg) {
|
|
183
|
-
cfg = (0, extract_cfg_1.extractCfg)(ast, graph, []);
|
|
183
|
+
cfg = (0, extract_cfg_1.extractCfg)(ast, config, graph, []);
|
|
184
184
|
}
|
|
185
185
|
const queriesWhichWantAliases = promotedQueries.filter(q => q.includeAliases);
|
|
186
186
|
for (const [nodeId, info] of graph.vertices(true)) {
|