Skip to content

Commit 1dae58f

Browse files
ericmorand-sonarsourceEric Morandvdiez
authored
JS-542 - Implement the deduplication of issues raised from both SonarJS and ESLint (#5089)
Co-authored-by: Eric Morand <eroc.morand@sonarsource.com> Co-authored-by: Victor Diez <victor.diez@sonarsource.com>
1 parent 96f6f7f commit 1dae58f

30 files changed

Lines changed: 657 additions & 140 deletions

packages/bridge/tests/router.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ describe('router', () => {
200200
{ key: 'S3923', configurations: [], fileTypeTarget: ['MAIN'] },
201201
]);
202202
const filePath = path.join(fixtures, 'file.yaml');
203+
const filePathWithLambda = path.join(fixtures, 'file-SomeLambdaFunction.yaml');
203204
const data = { filePath };
204205
const response = (await request(server, '/analyze-yaml', 'POST', data)) as string;
205206
const {
@@ -215,6 +216,8 @@ describe('router', () => {
215216
"Remove this conditional structure or edit its code blocks so that they're not all the same.",
216217
quickFixes: [],
217218
secondaryLocations: [],
219+
ruleESLintKeys: ['no-all-duplicated-branches'],
220+
filePath: filePathWithLambda,
218221
});
219222
});
220223

@@ -238,6 +241,8 @@ describe('router', () => {
238241
"Remove this conditional structure or edit its code blocks so that they're not all the same.",
239242
quickFixes: [],
240243
secondaryLocations: [],
244+
ruleESLintKeys: ['no-all-duplicated-branches'],
245+
filePath,
241246
});
242247
});
243248

packages/jsts/src/linter/issues/issue.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,6 @@ export interface Issue {
4343
cost?: number;
4444
secondaryLocations: Location[];
4545
quickFixes?: QuickFix[];
46+
ruleESLintKeys: Array<string>;
47+
filePath: string;
4648
}

packages/jsts/src/linter/issues/message.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@
1717
import { Linter, SourceCode } from 'eslint';
1818
import { transformFixes } from '../quickfixes/transform.js';
1919
import { Issue } from './issue.js';
20+
import * as ruleMetas from '../../rules/metas.js';
21+
22+
function getESLintKeys(sonarKey: string) {
23+
const ruleMeta = ruleMetas[sonarKey as keyof typeof ruleMetas];
24+
if (!ruleMeta?.eslintId) {
25+
return [];
26+
}
27+
const keys = new Set<string>();
28+
keys.add(ruleMeta.eslintId);
29+
if (ruleMeta.implementation === 'decorated') {
30+
ruleMeta.externalRules.forEach(externalRule => {
31+
keys.add(externalRule.externalRule);
32+
});
33+
}
34+
return Array.from(keys);
35+
}
2036

2137
/**
2238
* Converts an ESLint message into a SonarQube issue
@@ -29,9 +45,14 @@ import { Issue } from './issue.js';
2945
*
3046
* @param source the source code
3147
* @param message the ESLint message to convert
48+
* @param filePath the path to the file where the issue was found
3249
* @returns the converted SonarQube issue
3350
*/
34-
export function convertMessage(source: SourceCode, message: Linter.LintMessage): Issue | null {
51+
export function convertMessage(
52+
source: SourceCode,
53+
message: Linter.LintMessage,
54+
filePath: string,
55+
): Issue | null {
3556
/**
3657
* The property `ruleId` equals `null` on parsing errors and not applied directives.
3758
* The first should not happen because we lint ready SourceCode instances and not file contents.
@@ -40,14 +61,17 @@ export function convertMessage(source: SourceCode, message: Linter.LintMessage):
4061
if (!message.ruleId?.startsWith('sonarjs/')) {
4162
return null;
4263
}
64+
const ruleId = message.ruleId.slice(8); // remove "sonarjs/" prefix
4365
return {
44-
ruleId: message.ruleId.slice(8), // remove "sonarjs/" prefix
66+
ruleId,
4567
line: message.line,
4668
column: message.column,
4769
endLine: message.endLine,
4870
endColumn: message.endColumn,
4971
message: message.message,
5072
quickFixes: transformFixes(source, message),
5173
secondaryLocations: [],
74+
ruleESLintKeys: getESLintKeys(ruleId),
75+
filePath,
5276
};
5377
}

packages/jsts/src/linter/issues/transform.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export type LintingResult = {
6767
*/
6868
export function transformMessages(
6969
messages: Linter.LintMessage[],
70-
ctx: { sourceCode: SourceCode; rules: Record<string, Rule.RuleModule> },
70+
ctx: { sourceCode: SourceCode; rules: Record<string, Rule.RuleModule>; filePath: string },
7171
): LintingResult {
7272
const issues: Issue[] = [];
7373
const ucfgPaths: string[] = [];
@@ -76,7 +76,7 @@ export function transformMessages(
7676
if (message.ruleId === 'sonarjs/ucfg') {
7777
ucfgPaths.push(message.message);
7878
} else {
79-
let issue = convertMessage(ctx.sourceCode, message);
79+
let issue = convertMessage(ctx.sourceCode, message, ctx.filePath);
8080
if (issue !== null) {
8181
issue = normalizeLocation(decodeSonarRuntime(ctx.rules[issue.ruleId], issue));
8282
issues.push(issue);

packages/jsts/src/linter/wrapper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ export class LinterWrapper {
183183
};
184184

185185
const messages = this.linter.verify(sourceCode, config, createOptions(filePath));
186-
return transformMessages(messages, { sourceCode, rules });
186+
return transformMessages(messages, { sourceCode, rules, filePath });
187187
}
188188

189189
/**

packages/jsts/tests/analysis/analyzer.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,8 @@ describe('analyzeJSTS', () => {
457457
message: 'Octal literals should not be used.',
458458
quickFixes: [],
459459
secondaryLocations: [],
460+
ruleESLintKeys: ['no-octal'],
461+
filePath,
460462
},
461463
]);
462464
});

packages/jsts/tests/linter/issues/extract.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ describe('extract', () => {
3636
references: [{ startLine: 10, startCol: 20, endLine: 30, endCol: 40 }],
3737
}),
3838
secondaryLocations: [],
39+
ruleESLintKeys: [],
40+
filePath: 'foo.js',
3941
},
4042
];
4143
expect(extractHighlightedSymbols(issues)).toEqual({
@@ -56,6 +58,8 @@ describe('extract', () => {
5658
column: 2,
5759
message: '42',
5860
secondaryLocations: [],
61+
ruleESLintKeys: [],
62+
filePath: 'foo.js',
5963
},
6064
];
6165
expect(extractCognitiveComplexity(issues)).toEqual(42);
@@ -69,6 +73,8 @@ describe('extract', () => {
6973
column: 2,
7074
message: 'nan',
7175
secondaryLocations: [],
76+
ruleESLintKeys: [],
77+
filePath: 'foo.js',
7278
},
7379
];
7480
expect(extractCognitiveComplexity(issues)).toEqual(undefined);
@@ -89,20 +95,26 @@ describe('extract', () => {
8995
references: [{ startLine: 10, startCol: 20, endLine: 30, endCol: 40 }],
9096
}),
9197
secondaryLocations: [],
98+
ruleESLintKeys: [],
99+
filePath: 'foo.js',
92100
},
93101
{
94102
ruleId: 'non-extracted-rule',
95103
line: 1,
96104
column: 2,
97105
message: 'non-extract-message',
98106
secondaryLocations: [],
107+
ruleESLintKeys: [],
108+
filePath: 'foo.js',
99109
},
100110
{
101111
ruleId: cognitiveComplexityRule.ruleId,
102112
line: 1,
103113
column: 2,
104114
message: '42',
105115
secondaryLocations: [],
116+
ruleESLintKeys: [],
117+
filePath: 'foo.js',
106118
},
107119
];
108120
extractHighlightedSymbols(issues);
@@ -114,6 +126,8 @@ describe('extract', () => {
114126
column: 2,
115127
message: 'non-extract-message',
116128
secondaryLocations: [],
129+
ruleESLintKeys: [],
130+
filePath: 'foo.js',
117131
},
118132
]);
119133
});

packages/jsts/tests/linter/issues/message.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ describe('convertMessage', () => {
3636
rules: { [`sonarjs/${ruleId}`]: 'error' },
3737
});
3838

39-
expect(convertMessage(sourceCode, message)).toEqual({
39+
expect(convertMessage(sourceCode, message, 'foo.bar')).toEqual({
4040
ruleId,
4141
line: 1,
4242
column: 9,
@@ -60,10 +60,12 @@ describe('convertMessage', () => {
6060
},
6161
],
6262
secondaryLocations: [],
63+
ruleESLintKeys: ['no-extra-semi'],
64+
filePath: 'foo.bar',
6365
});
6466
});
6567

6668
it('should return null when an ESLint message is missing a rule id', () => {
67-
expect(convertMessage({} as SourceCode, {} as Linter.LintMessage)).toEqual(null);
69+
expect(convertMessage({} as SourceCode, {} as Linter.LintMessage, '')).toEqual(null);
6870
});
6971
});

packages/jsts/tests/linter/issues/transform.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ describe('transformMessages', () => {
4444
const [issue] = transformMessages(messages, {
4545
sourceCode,
4646
rules: { [ruleId]: rules[ruleId] },
47+
filePath: 'foo.js',
4748
}).issues;
4849
expect(issue).toEqual(
4950
expect.objectContaining({
@@ -73,6 +74,7 @@ describe('transformMessages', () => {
7374
const [issue] = transformMessages(messages, {
7475
sourceCode,
7576
rules: { [ruleId]: rules[ruleId] },
77+
filePath: 'foo.js',
7678
}).issues;
7779
expect(issue).toEqual(
7880
expect.objectContaining({
@@ -101,6 +103,7 @@ describe('transformMessages', () => {
101103
const [issue] = transformMessages(messages, {
102104
sourceCode,
103105
rules: { [ruleId]: rules[ruleId] },
106+
filePath: 'foo.js',
104107
}).issues;
105108
expect(issue).toEqual(
106109
expect.objectContaining({
@@ -141,6 +144,7 @@ describe('transformMessages', () => {
141144
const [{ secondaryLocations }] = transformMessages(messages, {
142145
sourceCode,
143146
rules: { [ruleId]: rules[ruleId] },
147+
filePath: 'foo.js',
144148
}).issues;
145149
expect(secondaryLocations).toEqual([
146150
{
@@ -168,6 +172,7 @@ describe('transformMessages', () => {
168172
const { issues, ucfgPaths } = transformMessages(messages as Linter.LintMessage[], {
169173
sourceCode,
170174
rules: {},
175+
filePath: 'foo.js',
171176
});
172177
expect(ucfgPaths.length).toEqual(1);
173178
expect(issues.length).toEqual(0);

sonar-plugin/bridge/src/main/java/org/sonar/plugins/javascript/bridge/BridgeServer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ record Issue(
161161
String ruleId,
162162
List<IssueLocation> secondaryLocations,
163163
Double cost,
164-
List<QuickFix> quickFixes
164+
List<QuickFix> quickFixes,
165+
List<String> ruleESLintKeys,
166+
String filePath
165167
) {}
166168

167169
record QuickFix(String message, List<QuickFixEdit> edits) {}

0 commit comments

Comments
 (0)