Skip to content

Commit e308d9a

Browse files
authored
feat: provide involved node-id for most linters! (#2206)
* feat: provide involved node-id for most linters! * refactor: fix type
1 parent 8359a85 commit e308d9a

File tree

12 files changed

+62
-28
lines changed

12 files changed

+62
-28
lines changed

src/linter/linter-format.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { SourceRange } from '../util/range';
1111
import type { DataflowInformation } from '../dataflow/info';
1212
import type { ControlFlowInformation } from '../control-flow/control-flow-graph';
1313
import type { ReadonlyFlowrAnalysisProvider } from '../project/flowr-analyzer';
14+
import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
1415

1516
export interface LinterRuleInformation<Config extends MergeableRecord = never> {
1617
/** Human-Readable name of the linting rule. */
@@ -94,11 +95,16 @@ export type LintQuickFix = LintQuickFixReplacement | LintQuickFixRemove;
9495
* A linting result for a single linting rule match.
9596
*/
9697
export interface LintingResult {
97-
readonly certainty: LintingResultCertainty
98+
/** The certainty of the linting result. */
99+
readonly certainty: LintingResultCertainty
98100
/**
99101
* If available, what to do to fix the linting result.
100102
*/
101-
readonly quickFix?: LintQuickFix[]
103+
readonly quickFix?: LintQuickFix[]
104+
/**
105+
* The node ID involved in this linting result, if applicable.
106+
*/
107+
readonly involvedId: NodeId | undefined
102108
}
103109

104110

src/linter/rules/dataframe-access-validation.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,11 @@ export const DATA_FRAME_ACCESS_VALIDATION = {
115115
}))
116116
.map(({ node, operand, ...accessed }) => ({
117117
...accessed,
118-
access: node?.lexeme ?? '???',
118+
involvedId: node?.info.id,
119+
access: node?.lexeme ?? '???',
119120
...(operand?.type === RType.Symbol ? { operand: operand.content } : {}),
120-
range: node?.info.fullRange ?? node?.location ?? rangeFrom(-1, -1, -1, -1),
121-
certainty: LintingResultCertainty.Certain
121+
range: node?.info.fullRange ?? node?.location ?? rangeFrom(-1, -1, -1, -1),
122+
certainty: LintingResultCertainty.Certain
122123
}));
123124

124125
return { results, '.meta': metadata };

src/linter/rules/dead-code.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ export const DEAD_CODE = {
4444
.map(element => element.node.info.fullRange ?? element.node.location)
4545
.filter(isNotUndefined)))
4646
.map(range => ({
47-
certainty: LintingResultCertainty.Certain,
47+
certainty: LintingResultCertainty.Certain,
48+
involvedId: undefined,
4849
range
4950
})),
5051
'.meta': meta

src/linter/rules/file-path-validity.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ export const FILE_PATH_VALIDITY = {
7070
metadata.totalUnknown++;
7171
if(config.includeUnknown) {
7272
return [{
73+
involvedId: matchingRead.nodeId,
7374
range,
74-
filePath: Unknown,
75-
certainty: LintingResultCertainty.Uncertain
75+
filePath: Unknown,
76+
certainty: LintingResultCertainty.Uncertain
7677
}];
7778
} else {
7879
return [];
@@ -98,9 +99,10 @@ export const FILE_PATH_VALIDITY = {
9899
}
99100

100101
return [{
102+
involvedId: matchingRead.nodeId,
101103
range,
102-
filePath: matchingRead.value as string,
103-
certainty: writesBefore && writesBefore.length && writesBefore.every(w => w === Ternary.Maybe) ? LintingResultCertainty.Uncertain : LintingResultCertainty.Certain
104+
filePath: matchingRead.value as string,
105+
certainty: writesBefore && writesBefore.length && writesBefore.every(w => w === Ternary.Maybe) ? LintingResultCertainty.Uncertain : LintingResultCertainty.Certain
104106
}];
105107
}),
106108
'.meta': metadata

src/linter/rules/function-finder-util.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,10 @@ export const functionFinderUtil = {
7878
return {
7979
results:
8080
results.map(element => ({
81-
certainty: LintingResultCertainty.Certain,
82-
function: element.target,
83-
range: element.range
81+
certainty: LintingResultCertainty.Certain,
82+
involvedId: element.node.info.id,
83+
function: element.target,
84+
range: element.range
8485
})),
8586
'.meta': metadata
8687
};

src/linter/rules/naming-convention.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ export const NAMING_CONVENTION = {
212212
const fix = fixCasing(m.name, casing);
213213
return {
214214
...m,
215-
quickFix: fix ? createNamingConventionQuickFixes(data.dataflow.graph, id, fix, casing) : undefined
215+
involvedId: id,
216+
quickFix: fix ? createNamingConventionQuickFixes(data.dataflow.graph, id, fix, casing) : undefined
216217
};
217218
});
218219
return {

src/linter/rules/seeded-randomness.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const SEEDED_RANDOMNESS = {
8484
.flatMap(element => enrichmentContent(element, Enrichment.CallTargets).targets.map(target => {
8585
metadata.consumerCalls++;
8686
return {
87+
involvedId: element.node.info.id,
8788
range: element.node.info.fullRange as SourceRange,
8889
target: target as Identifier,
8990
searchElement: element
@@ -147,9 +148,10 @@ export const SEEDED_RANDOMNESS = {
147148
metadata.callsWithOtherBranchProducers++;
148149
}
149150
return [{
150-
certainty: cdsOfProduces.size > 0 ? LintingResultCertainty.Uncertain : LintingResultCertainty.Certain,
151-
function: element.target,
152-
range: element.range
151+
involvedId: element.involvedId,
152+
certainty: cdsOfProduces.size > 0 ? LintingResultCertainty.Uncertain : LintingResultCertainty.Certain,
153+
function: element.target,
154+
range: element.range
153155
}];
154156
}),
155157
'.meta': metadata

src/linter/rules/unused-definition.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,11 @@ export const UNUSED_DEFINITION = {
122122
// found an unused definition
123123
const variableName = element.node.lexeme;
124124
return [{
125-
certainty: LintingResultCertainty.Uncertain,
125+
certainty: LintingResultCertainty.Uncertain,
126126
variableName,
127-
range: element.node.info.fullRange ?? element.node.location ?? rangeFrom(-1, -1, -1, -1),
128-
quickFix: buildQuickFix(element.node, data.dataflow.graph, data.normalize)
127+
involvedId: element.node.info.id,
128+
range: element.node.info.fullRange ?? element.node.location ?? rangeFrom(-1, -1, -1, -1),
129+
quickFix: buildQuickFix(element.node, data.dataflow.graph, data.normalize)
129130
}] satisfies UnusedDefinitionResult[];
130131
}).filter(isNotUndefined)),
131132
'.meta': metadata

src/linter/rules/useless-loop.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ export const USELESS_LOOP = {
3434
}).filter(loop =>
3535
onlyLoopsOnce(loop.node.info.id, dataflow.graph, cfg, normalize, analyzer.inspectContext())
3636
).map(res => ({
37-
certainty: LintingResultCertainty.Certain,
38-
name: res.node.lexeme as string,
39-
range: res.node.info.fullRange as SourceRange
37+
certainty: LintingResultCertainty.Certain,
38+
name: res.node.lexeme as string,
39+
range: res.node.info.fullRange as SourceRange,
40+
involvedId: res.node.info.id
4041
} satisfies UselessLoopResult));
4142

4243
return {

src/queries/catalog/linter-query/linter-query-format.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import type { FlowrConfigOptions } from '../../../config';
2424
import type { ReplOutput } from '../../../cli/repl/commands/repl-main';
2525
import type { CommandCompletions } from '../../../cli/repl/core';
2626
import { fileProtocol } from '../../../r-bridge/retriever';
27-
import { getGuardIssueUrl } from '../../../util/assert';
27+
import { getGuardIssueUrl, isNotUndefined } from '../../../util/assert';
2828

2929
export interface LinterQuery extends BaseQueryFormat {
3030
readonly type: 'linter';
@@ -141,7 +141,15 @@ export const LinterQueryDefinition = {
141141
})
142142
).description('The rules to lint for. If unset, all rules will be included.')
143143
}).description('The linter query lints for the given set of rules and returns the result.'),
144-
flattenInvolvedNodes: () => []
144+
flattenInvolvedNodes: (queryResults) => {
145+
const out = queryResults as LinterQueryResult;
146+
return Object.values(out.results).flatMap(v => {
147+
if(isLintingResultsError(v)) {
148+
return [];
149+
}
150+
return v.results.map(v => v.involvedId);
151+
}).filter(isNotUndefined);
152+
}
145153
} as const satisfies SupportedQuery<'linter'>;
146154

147155
function addLintingRuleResult<Name extends LintingRuleNames>(ruleName: Name, results: LintingResults<Name>, result: string[]) {

0 commit comments

Comments
 (0)