-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathlinter.ts
More file actions
101 lines (93 loc) · 4.47 KB
/
linter.ts
File metadata and controls
101 lines (93 loc) · 4.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import {
type LintingRuleConfig,
type LintingRuleMetadata,
type LintingRuleNames,
type LintingRuleResult
, LintingRules } from '../../../src/linter/linter-rules';
import { type TestLabel , decorateLabelContext } from './label';
import { assert, test } from 'vitest';
import { fileProtocol, requestFromInput } from '../../../src/r-bridge/retriever';
import { type NormalizedAst , deterministicCountingIdGenerator } from '../../../src/r-bridge/lang-4.x/ast/model/processing/decorate';
import { executeLintingRule } from '../../../src/linter/linter-executor';
import { type LintingRule , isLintingResultsError, LintingPrettyPrintContext } from '../../../src/linter/linter-format';
import { log } from '../../../src/util/log';
import type { DeepPartial } from 'ts-essentials';
import type { KnownParser } from '../../../src/r-bridge/parser';
import { type FlowrLaxSourcingOptions , DropPathsOption } from '../../../src/config';
import type { DataflowInformation } from '../../../src/dataflow/info';
import { graphToMermaidUrl } from '../../../src/util/mermaid/dfg';
import { FlowrAnalyzerBuilder } from '../../../src/project/flowr-analyzer-builder';
import type { FlowrFileProvider } from '../../../src/project/context/flowr-file';
import { FlowrInlineTextFile } from '../../../src/project/context/flowr-file';
function cleanUpLintingResult<Name extends LintingRuleNames>(
result: LintingRuleResult<Name> | Omit<LintingRuleResult<Name>, 'involvedId'>
): Omit<LintingRuleResult<Name>, 'involvedId'> {
if('involvedId' in result) {
const { involvedId: _drop, ...rest } = result;
return rest;
}
return result;
}
/**
*
*/
export function assertLinter<Name extends LintingRuleNames>(
name: string | TestLabel,
parser: KnownParser,
code: string,
ruleName: Name,
expected: Omit<LintingRuleResult<Name>, 'involvedId'>[] | ((df: DataflowInformation, ast: NormalizedAst) => Omit<LintingRuleResult<Name>, 'involvedId'>[]),
expectedMetadata?: LintingRuleMetadata<Name>,
lintingRuleConfig?: DeepPartial<LintingRuleConfig<Name>> & { useAsFilePath?: string, addFiles?: FlowrFileProvider[] }
) {
test(decorateLabelContext(name, ['linter']), async() => {
const analyzer = await new FlowrAnalyzerBuilder()
.setInput({
getId: deterministicCountingIdGenerator(0)
})
.setParser(parser)
.amendConfig(c => {
(c.solver.resolveSource as FlowrLaxSourcingOptions) = {
...c.solver.resolveSource as FlowrLaxSourcingOptions,
dropPaths: DropPathsOption.All
};
})
.build();
if(lintingRuleConfig?.useAsFilePath) {
analyzer.addFile(new FlowrInlineTextFile(lintingRuleConfig.useAsFilePath, code));
}
if(lintingRuleConfig?.addFiles) {
analyzer.addFile(...lintingRuleConfig.addFiles);
}
analyzer.addRequest(lintingRuleConfig?.useAsFilePath ?
requestFromInput(fileProtocol + lintingRuleConfig.useAsFilePath) :
requestFromInput(code)
);
const rule = LintingRules[ruleName] as unknown as LintingRule<LintingRuleResult<Name>, LintingRuleMetadata<Name>, LintingRuleConfig<Name>>;
const results = await executeLintingRule(ruleName, analyzer, lintingRuleConfig);
if(isLintingResultsError(results)) {
throw new Error(results.error);
}
for(const [type, printer] of Object.entries({
text: (result: LintingRuleResult<Name>, metadata: LintingRuleMetadata<Name>) => `${rule.prettyPrint[LintingPrettyPrintContext.Query](result, metadata)} (${result.certainty})${result.quickFix ? ` (${result.quickFix.length} quick fix(es) available)` : ''}`,
json: (result: LintingRuleResult<Name>, metadata: LintingRuleMetadata<Name>) => JSON.stringify({ result, metadata })
})) {
log.info(`${type}:\n${results.results.map(r => ` ${printer(r, results['.meta'])}`).join('\n')}`);
}
if(typeof expected === 'function') {
expected = expected(await analyzer.dataflow(), await analyzer.normalize());
}
try {
assert.deepEqual(results.results.map(cleanUpLintingResult), expected.map(cleanUpLintingResult), `Expected ${ruleName} to return ${JSON.stringify(expected)}, but got ${JSON.stringify(results)}`);
} catch(e) {
console.error('dfg:', graphToMermaidUrl((await analyzer.dataflow()).graph));
throw e;
}
if(expectedMetadata !== undefined) {
// eslint-disable-next-line unused-imports/no-unused-vars
const { searchTimeMs, processTimeMs, ...strippedMeta } = results['.meta'];
assert.deepEqual(strippedMeta as unknown as LintingRuleMetadata<Name>, expectedMetadata,
`Expected ${ruleName} to have metadata ${JSON.stringify(expectedMetadata)}, but got ${JSON.stringify(results['.meta'])}`);
}
});
}