From aa64fc5decb105f31ec22ee13a0ea6d08c362091 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sat, 20 Jan 2024 22:21:42 -0800 Subject: [PATCH] Added two new diagnostic rules: `reportAssertTypeFailure` for type mismatches detected by `typing.assert_type` and `reportUnusedExcept` for situations where an except statement is determined to be unreachable. (#7070) --- docs/configuration.md | 6 ++++ .../pyright-internal/src/analyzer/checker.ts | 2 +- .../src/analyzer/typeEvaluator.ts | 2 +- .../src/common/configOptions.ts | 16 ++++++++++ .../src/common/diagnosticRules.ts | 2 ++ packages/vscode-pyright/package.json | 32 +++++++++++++++++++ .../schemas/pyrightconfig.schema.json | 12 +++++++ 7 files changed, 70 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 280aa8bc6..09bf9189f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -90,6 +90,8 @@ The following settings control pyright’s diagnostic output (warnings or errors **reportWildcardImportFromLibrary** [boolean or string, optional]: Generate or suppress diagnostics for a wildcard import from an external library. The use of this language feature is highly discouraged and can result in bugs when the library is updated. The default value for this setting is `"warning"`. + **reportAssertTypeFailure** [boolean or string, optional]: Generate or suppress diagnostics for a type mismatch detected by the `typing.assert_type` call. The default value for this setting is `"error"`. + **reportOptionalSubscript** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to subscript (index) a variable with an Optional type. The default value for this setting is `"error"`. **reportOptionalMemberAccess** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to access a member of a variable with an Optional type. The default value for this setting is `"error"`. @@ -182,6 +184,8 @@ The following settings control pyright’s diagnostic output (warnings or errors **reportUnusedCoroutine** [boolean or string, optional]: Generate or suppress diagnostics for call statements whose return value is not used in any way and is a Coroutine. This identifies a common error where an `await` keyword is mistakenly omitted. The default value for this setting is `"error"`. + **reportUnusedExcept** [boolean or string, optional]: Generate or suppress diagnostics for an `except` clause that will never be reached. The default value for this setting is `"error"`. + **reportUnusedExpression** [boolean or string, optional]: Generate or suppress diagnostics for simple expressions whose results are not used in any way. The default value for this setting is `"none"`. **reportUnnecessaryTypeIgnoreComment** [boolean or string, optional]: Generate or suppress diagnostics for a `# type: ignore` or `# pyright: ignore` comment that would have no effect if removed. The default value for this setting is `"none"`. @@ -323,6 +327,7 @@ The following table lists the default severity levels for each diagnostic rule w | reportUnsupportedDunderAll | "none" | "warning" | "warning" | "error" | | reportUnusedExpression | "none" | "warning" | "warning" | "error" | | reportWildcardImportFromLibrary | "none" | "warning" | "warning" | "error" | +| reportAssertTypeFailure | "none" | "error" | "error" | "error" | | reportGeneralTypeIssues | "none" | "error" | "error" | "error" | | reportOptionalSubscript | "none" | "error" | "error" | "error" | | reportOptionalMemberAccess | "none" | "error" | "error" | "error" | @@ -334,6 +339,7 @@ The following table lists the default severity levels for each diagnostic rule w | reportPrivateImportUsage | "none" | "error" | "error" | "error" | | reportUnboundVariable | "none" | "error" | "error" | "error" | | reportUnusedCoroutine | "none" | "error" | "error" | "error" | +| reportUnusedExcept | "none" | "error" | "error" | "error" | | reportFunctionMemberAccess | "none" | "none" | "error" | "error" | | reportIncompatibleMethodOverride | "none" | "none" | "error" | "error" | | reportIncompatibleVariableOverride | "none" | "none" | "error" | "error" | diff --git a/packages/pyright-internal/src/analyzer/checker.ts b/packages/pyright-internal/src/analyzer/checker.ts index abf8fcbac..1d31c25ed 100644 --- a/packages/pyright-internal/src/analyzer/checker.ts +++ b/packages/pyright-internal/src/analyzer/checker.ts @@ -6902,7 +6902,7 @@ export class Checker extends ParseTreeWalker { // Were all of the exception types overridden? if (typesOfThisExcept.length > 0 && typesOfThisExcept.length === overriddenExceptionCount) { this._evaluator.addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportUnusedExcept, LocMessage.unreachableExcept() + diagAddendum.getString(), except.typeExpression ); diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index 0730edb77..219d21760 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -8113,7 +8113,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions const srcDestTypes = printSrcDestTypes(arg0TypeResult.type, assertedType, { expandTypeAlias: true }); addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportAssertTypeFailure, LocMessage.assertTypeTypeMismatch().format({ expected: srcDestTypes.destType, received: srcDestTypes.sourceType, diff --git a/packages/pyright-internal/src/common/configOptions.ts b/packages/pyright-internal/src/common/configOptions.ts index 4a73cf2b2..ce712cfd7 100644 --- a/packages/pyright-internal/src/common/configOptions.ts +++ b/packages/pyright-internal/src/common/configOptions.ts @@ -159,6 +159,9 @@ export interface DiagnosticRuleSet { // Report use of wildcard import for non-local imports? reportWildcardImportFromLibrary: DiagnosticLevel; + // Report failure of assert_type call? + reportAssertTypeFailure: DiagnosticLevel; + // Report attempts to subscript (index) an Optional type? reportOptionalSubscript: DiagnosticLevel; @@ -310,6 +313,9 @@ export interface DiagnosticRuleSet { // and is not used in any way. reportUnusedCoroutine: DiagnosticLevel; + // Report except clause that is unreachable. + reportUnusedExcept: DiagnosticLevel; + // Report cases where a simple expression result is not used in any way. reportUnusedExpression: DiagnosticLevel; @@ -375,6 +381,7 @@ export function getDiagLevelDiagnosticRules() { DiagnosticRule.reportUnusedVariable, DiagnosticRule.reportDuplicateImport, DiagnosticRule.reportWildcardImportFromLibrary, + DiagnosticRule.reportAssertTypeFailure, DiagnosticRule.reportOptionalSubscript, DiagnosticRule.reportOptionalMemberAccess, DiagnosticRule.reportOptionalCall, @@ -421,6 +428,7 @@ export function getDiagLevelDiagnosticRules() { DiagnosticRule.reportUnsupportedDunderAll, DiagnosticRule.reportUnusedCallResult, DiagnosticRule.reportUnusedCoroutine, + DiagnosticRule.reportUnusedExcept, DiagnosticRule.reportUnusedExpression, DiagnosticRule.reportUnnecessaryTypeIgnoreComment, DiagnosticRule.reportMatchNotExhaustive, @@ -465,6 +473,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet { reportUnusedVariable: 'none', reportDuplicateImport: 'none', reportWildcardImportFromLibrary: 'none', + reportAssertTypeFailure: 'none', reportOptionalSubscript: 'none', reportOptionalMemberAccess: 'none', reportOptionalCall: 'none', @@ -511,6 +520,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet { reportUnsupportedDunderAll: 'none', reportUnusedCallResult: 'none', reportUnusedCoroutine: 'none', + reportUnusedExcept: 'none', reportUnusedExpression: 'none', reportUnnecessaryTypeIgnoreComment: 'none', reportMatchNotExhaustive: 'none', @@ -551,6 +561,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet { reportUnusedVariable: 'none', reportDuplicateImport: 'none', reportWildcardImportFromLibrary: 'warning', + reportAssertTypeFailure: 'error', reportOptionalSubscript: 'error', reportOptionalMemberAccess: 'error', reportOptionalCall: 'error', @@ -597,6 +608,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet { reportUnsupportedDunderAll: 'warning', reportUnusedCallResult: 'none', reportUnusedCoroutine: 'error', + reportUnusedExcept: 'error', reportUnusedExpression: 'warning', reportUnnecessaryTypeIgnoreComment: 'none', reportMatchNotExhaustive: 'none', @@ -637,6 +649,7 @@ export function getStandardDiagnosticRuleSet(): DiagnosticRuleSet { reportUnusedVariable: 'none', reportDuplicateImport: 'none', reportWildcardImportFromLibrary: 'warning', + reportAssertTypeFailure: 'error', reportOptionalSubscript: 'error', reportOptionalMemberAccess: 'error', reportOptionalCall: 'error', @@ -683,6 +696,7 @@ export function getStandardDiagnosticRuleSet(): DiagnosticRuleSet { reportUnsupportedDunderAll: 'warning', reportUnusedCallResult: 'none', reportUnusedCoroutine: 'error', + reportUnusedExcept: 'error', reportUnusedExpression: 'warning', reportUnnecessaryTypeIgnoreComment: 'none', reportMatchNotExhaustive: 'none', @@ -723,6 +737,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet { reportUnusedVariable: 'error', reportDuplicateImport: 'error', reportWildcardImportFromLibrary: 'error', + reportAssertTypeFailure: 'error', reportOptionalSubscript: 'error', reportOptionalMemberAccess: 'error', reportOptionalCall: 'error', @@ -769,6 +784,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet { reportUnsupportedDunderAll: 'error', reportUnusedCallResult: 'none', reportUnusedCoroutine: 'error', + reportUnusedExcept: 'error', reportUnusedExpression: 'error', reportUnnecessaryTypeIgnoreComment: 'none', reportMatchNotExhaustive: 'error', diff --git a/packages/pyright-internal/src/common/diagnosticRules.ts b/packages/pyright-internal/src/common/diagnosticRules.ts index 00f327655..512411bda 100644 --- a/packages/pyright-internal/src/common/diagnosticRules.ts +++ b/packages/pyright-internal/src/common/diagnosticRules.ts @@ -35,6 +35,7 @@ export enum DiagnosticRule { reportUnusedVariable = 'reportUnusedVariable', reportDuplicateImport = 'reportDuplicateImport', reportWildcardImportFromLibrary = 'reportWildcardImportFromLibrary', + reportAssertTypeFailure = 'reportAssertTypeFailure', reportOptionalSubscript = 'reportOptionalSubscript', reportOptionalMemberAccess = 'reportOptionalMemberAccess', reportOptionalCall = 'reportOptionalCall', @@ -81,6 +82,7 @@ export enum DiagnosticRule { reportUnsupportedDunderAll = 'reportUnsupportedDunderAll', reportUnusedCallResult = 'reportUnusedCallResult', reportUnusedCoroutine = 'reportUnusedCoroutine', + reportUnusedExcept = 'reportUnusedExcept', reportUnusedExpression = 'reportUnusedExpression', reportUnnecessaryTypeIgnoreComment = 'reportUnnecessaryTypeIgnoreComment', reportMatchNotExhaustive = 'reportMatchNotExhaustive', diff --git a/packages/vscode-pyright/package.json b/packages/vscode-pyright/package.json index 393ac769c..ebd10e3cd 100644 --- a/packages/vscode-pyright/package.json +++ b/packages/vscode-pyright/package.json @@ -415,6 +415,22 @@ false ] }, + "reportAssertTypeFailure": { + "type": [ + "string", + "boolean" + ], + "description": "Diagnostics for a type mismatch detected by a typing.assert_type call.", + "default": "error", + "enum": [ + "none", + "information", + "warning", + "error", + true, + false + ] + }, "reportOptionalSubscript": { "type": [ "string", @@ -1151,6 +1167,22 @@ false ] }, + "reportUnusedExcept": { + "type": [ + "string", + "boolean" + ], + "description": "Diagnostics for unreachable except clause.", + "default": "error", + "enum": [ + "none", + "information", + "warning", + "error", + true, + false + ] + }, "reportUnusedExpression": { "type": [ "string", diff --git a/packages/vscode-pyright/schemas/pyrightconfig.schema.json b/packages/vscode-pyright/schemas/pyrightconfig.schema.json index e207e5ab1..b55ea1f41 100644 --- a/packages/vscode-pyright/schemas/pyrightconfig.schema.json +++ b/packages/vscode-pyright/schemas/pyrightconfig.schema.json @@ -251,6 +251,12 @@ "title": "Controls reporting of wildcard import from external library", "default": "warning" }, + "reportAssertTypeFailure": { + "$id": "#/properties/reportAssertTypeFailure", + "$ref": "#/definitions/diagnostic", + "title": "Controls reporting of type mismatch detected by typing.assert_type call", + "default": "error" + }, "reportOptionalSubscript": { "$id": "#/properties/reportOptionalSubscript", "$ref": "#/definitions/diagnostic", @@ -527,6 +533,12 @@ "title": "Controls reporting of call expressions that returns Coroutine whose results are not consumed", "default": "error" }, + "reportUnusedExcept": { + "$id": "#/properties/reportUnusedExcept", + "$ref": "#/definitions/diagnostic", + "title": "Controls reporting of unreachable except clauses", + "default": "error" + }, "reportUnusedExpression": { "$id": "#/properties/reportUnusedExpression", "$ref": "#/definitions/diagnostic",