From f9a944fa08c02f2e45c1438a1cea3323542e2782 Mon Sep 17 00:00:00 2001 From: detachhead Date: Thu, 29 Aug 2024 23:05:57 +1000 Subject: [PATCH] report baselined errors that can be represented with a hint diagnostic (eg. unused, deprecated) as such --- packages/pyright-internal/src/baseline.ts | 60 +++++++++++++------ .../pyright-internal/src/common/diagnostic.ts | 14 ++++- .../src/languageServerBase.ts | 3 +- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/packages/pyright-internal/src/baseline.ts b/packages/pyright-internal/src/baseline.ts index d30d68fba..d2c1890c0 100644 --- a/packages/pyright-internal/src/baseline.ts +++ b/packages/pyright-internal/src/baseline.ts @@ -3,7 +3,8 @@ import { FileDiagnostics } from './common/diagnosticSink'; import { Range } from './common/textRange'; import { mkdirSync, readFileSync, writeFileSync } from 'fs'; import { Uri } from './common/uri/uri'; -import { DiagnosticCategory } from './common/diagnostic'; +import { convertLevelToCategory, Diagnostic, DiagnosticCategory } from './common/diagnostic'; +import { extraOptionDiagnosticRules } from './common/configOptions'; interface BaselineFile { files: { @@ -79,33 +80,54 @@ export const getBaselinedErrors = (rootDir: Uri): BaselineFile => { return JSON.parse(baselineFileContents); }; +interface FileDiagnosticsWithBaselineInfo extends FileDiagnostics { + containsNewErrors: boolean; +} + export const filterOutBaselinedDiagnostics = ( rootDir: Uri, filesWithDiagnostics: readonly FileDiagnostics[] -): FileDiagnostics[] => { +): FileDiagnosticsWithBaselineInfo[] => { const baselineFile = getBaselinedErrors(rootDir); return filesWithDiagnostics.map((fileWithDiagnostics) => { const baselinedErrorsForFile = baselineFile.files[rootDir.getRelativePath(fileWithDiagnostics.fileUri)!.toString()]; if (!baselinedErrorsForFile) { - return fileWithDiagnostics; + return { ...fileWithDiagnostics, containsNewErrors: true }; } - return { - ...fileWithDiagnostics, - diagnostics: fileWithDiagnostics.diagnostics.filter((diagnostic) => { - const matchedIndex = baselinedErrorsForFile.findIndex( - (baselinedError) => - baselinedError.code === diagnostic.getRule() && - baselinedError.range.start.character === diagnostic.range.start.character && - baselinedError.range.end.character === diagnostic.range.end.character - ); - if (matchedIndex >= 0) { - baselinedErrorsForFile.splice(matchedIndex, 1); - return false; - } else { - return true; + const filteredDiagnostics = []; + let containsNewErrors = false; + for (let diagnostic of fileWithDiagnostics.diagnostics) { + const diagnosticRule = diagnostic.getRule() as DiagnosticRule | undefined; + const matchedIndex = baselinedErrorsForFile.findIndex( + (baselinedError) => + baselinedError.code === diagnosticRule && + baselinedError.range.start.character === diagnostic.range.start.character && + baselinedError.range.end.character === diagnostic.range.end.character + ); + if (matchedIndex >= 0) { + baselinedErrorsForFile.splice(matchedIndex, 1); + // if the baselined error can be reported as a hint (eg. unreachable/deprecated), keep it and change its diagnostic to that instead + if (diagnosticRule) { + for (const { name, get } of extraOptionDiagnosticRules) { + if (get().includes(diagnosticRule)) { + diagnostic = new Diagnostic( + convertLevelToCategory(name), + diagnostic.message, + diagnostic.range, + diagnostic.priority + ); + filteredDiagnostics.push(diagnostic); + // none of these rules should have multiple extra diagnostic levels so we break after the first match + break; + } + } } - }), - }; + } else { + containsNewErrors = true; + filteredDiagnostics.push(diagnostic); + } + } + return { ...fileWithDiagnostics, diagnostics: filteredDiagnostics, containsNewErrors }; }); }; diff --git a/packages/pyright-internal/src/common/diagnostic.ts b/packages/pyright-internal/src/common/diagnostic.ts index a9ae6ea69..bc0c3cf36 100644 --- a/packages/pyright-internal/src/common/diagnostic.ts +++ b/packages/pyright-internal/src/common/diagnostic.ts @@ -9,7 +9,7 @@ import { Commands } from '../commands/commands'; import { appendArray } from './collectionUtils'; -import { DiagnosticLevel } from './configOptions'; +import { LspDiagnosticLevel } from './configOptions'; import { Range, TextRange } from './textRange'; import { Uri } from './uri/uri'; @@ -43,7 +43,7 @@ export const enum DiagnosticCategory { TaskItem, } -export function convertLevelToCategory(level: DiagnosticLevel) { +export function convertLevelToCategory(level: LspDiagnosticLevel) { switch (level) { case 'error': return DiagnosticCategory.Error; @@ -54,7 +54,17 @@ export function convertLevelToCategory(level: DiagnosticLevel) { case 'information': return DiagnosticCategory.Information; + case 'unreachable': + return DiagnosticCategory.UnreachableCode; + + case 'unused': + return DiagnosticCategory.UnusedCode; + + case 'deprecated': + return DiagnosticCategory.Deprecated; + default: + level satisfies 'none'; throw new Error(`${level} is not expected`); } } diff --git a/packages/pyright-internal/src/languageServerBase.ts b/packages/pyright-internal/src/languageServerBase.ts index 7d7ca3e91..ac61af510 100644 --- a/packages/pyright-internal/src/languageServerBase.ts +++ b/packages/pyright-internal/src/languageServerBase.ts @@ -1180,8 +1180,7 @@ export abstract class LanguageServerBase implements LanguageServerInterface, Dis const baselineFile = getBaselinedErrors(rootUri); const fileKey = rootUri.getRelativePath(fileUri)!; const diagnosticsForFile = this.documentsWithDiagnostics[params.textDocument.uri]; - const newDiagnostics = filterOutBaselinedDiagnostics(rootUri, [diagnosticsForFile])[0]; - if (newDiagnostics.diagnostics.length) { + if (diagnosticsForFile && filterOutBaselinedDiagnostics(rootUri, [diagnosticsForFile])[0].containsNewErrors) { // there are new diagnostics that haven't been baselined, so we don't want to write them // because the user will have to either fix the diagnostics or explicitly write them to the // baseline themselves