Added new diagnostic rule "reportInvalidTypeVarUse" that controls reporting of TypeVars that appear only once in a function signature. By default it is off in basic type checking mode but on in strict mode.

This commit is contained in:
Eric Traut 2020-10-19 13:30:06 -07:00
parent 1c91ad026a
commit bb04ffa6be
8 changed files with 45 additions and 4 deletions

View File

@ -112,6 +112,8 @@ The following settings control pyrights diagnostic output (warnings or errors
**reportMissingTypeArgument** [boolean or string, optional]: Generate or suppress diagnostics when a generic class is used without providing explicit or implicit type arguments. The default value for this setting is 'none'.
**reportInvalidTypeVarUse** [boolean or string, optional]: Generate or suppress diagnostics when a TypeVar is used inappropriately (e.g. if a TypeVar appears only once) within a generic function signature. The default value for this setting is 'none'.
**reportCallInDefaultInitializer** [boolean or string, optional]: Generate or suppress diagnostics for function calls within a default value initialization expression. Such calls can mask expensive operations that are performed at module initialization time. The default value for this setting is 'none'.
**reportUnnecessaryIsInstance** [boolean or string, optional]: Generate or suppress diagnostics for 'isinstance' or 'issubclass' calls where the result is statically determined to be always true or always false. Such calls are often indicative of a programming error. The default value for this setting is 'none'.

View File

@ -796,6 +796,11 @@ export class Checker extends ParseTreeWalker {
// Verifies that each local type variable is used more than once.
private _validateFunctionTypeVarUsage(node: FunctionNode) {
// Skip this check entirely if it's disabled.
if (this._fileInfo.diagnosticRuleSet.reportInvalidTypeVarUse === 'none') {
return;
}
const localTypeVarUsage = new Map<string, NameNode[]>();
const nameWalker = new ParseTreeUtils.NameNodeWalker((nameNode) => {
@ -827,8 +832,8 @@ export class Checker extends ParseTreeWalker {
localTypeVarUsage.forEach((nameNodes) => {
if (nameNodes.length === 1) {
this._evaluator.addDiagnostic(
this._fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
this._fileInfo.diagnosticRuleSet.reportInvalidTypeVarUse,
DiagnosticRule.reportInvalidTypeVarUse,
Localizer.Diagnostic.typeVarUsedOnlyOnce().format({
name: nameNodes[0].value,
}),

View File

@ -192,6 +192,9 @@ export interface DiagnosticRuleSet {
// Report usage of generic class without explicit type arguments?
reportMissingTypeArgument: DiagnosticLevel;
// Report improper usage of type variables within function signatures?
reportInvalidTypeVarUse: DiagnosticLevel;
// Report usage of function call within default value
// initialization expression?
reportCallInDefaultInitializer: DiagnosticLevel;
@ -277,6 +280,7 @@ export function getDiagLevelDiagnosticRules() {
DiagnosticRule.reportUnknownVariableType,
DiagnosticRule.reportUnknownMemberType,
DiagnosticRule.reportMissingTypeArgument,
DiagnosticRule.reportInvalidTypeVarUse,
DiagnosticRule.reportCallInDefaultInitializer,
DiagnosticRule.reportUnnecessaryIsInstance,
DiagnosticRule.reportUnnecessaryCast,
@ -338,6 +342,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet {
reportUnknownVariableType: 'none',
reportUnknownMemberType: 'none',
reportMissingTypeArgument: 'none',
reportInvalidTypeVarUse: 'none',
reportCallInDefaultInitializer: 'none',
reportUnnecessaryIsInstance: 'none',
reportUnnecessaryCast: 'none',
@ -395,6 +400,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet {
reportUnknownVariableType: 'none',
reportUnknownMemberType: 'none',
reportMissingTypeArgument: 'none',
reportInvalidTypeVarUse: 'none',
reportCallInDefaultInitializer: 'none',
reportUnnecessaryIsInstance: 'none',
reportUnnecessaryCast: 'none',
@ -452,6 +458,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
reportUnknownVariableType: 'error',
reportUnknownMemberType: 'error',
reportMissingTypeArgument: 'error',
reportInvalidTypeVarUse: 'error',
reportCallInDefaultInitializer: 'none',
reportUnnecessaryIsInstance: 'error',
reportUnnecessaryCast: 'error',
@ -1020,6 +1027,13 @@ export class ConfigOptions {
defaultSettings.reportMissingTypeArgument
),
// Read the "reportInvalidTypeVarUse" entry.
reportInvalidTypeVarUse: this._convertDiagnosticLevel(
configObj.reportInvalidTypeVarUse,
DiagnosticRule.reportInvalidTypeVarUse,
defaultSettings.reportInvalidTypeVarUse
),
// Read the "reportCallInDefaultInitializer" entry.
reportCallInDefaultInitializer: this._convertDiagnosticLevel(
configObj.reportCallInDefaultInitializer,

View File

@ -49,6 +49,7 @@ export enum DiagnosticRule {
reportUnknownVariableType = 'reportUnknownVariableType',
reportUnknownMemberType = 'reportUnknownMemberType',
reportMissingTypeArgument = 'reportMissingTypeArgument',
reportInvalidTypeVarUse = 'reportInvalidTypeVarUse',
reportCallInDefaultInitializer = 'reportCallInDefaultInitializer',
reportUnnecessaryIsInstance = 'reportUnnecessaryIsInstance',
reportUnnecessaryCast = 'reportUnnecessaryCast',

View File

@ -2,6 +2,8 @@
# a generic function. A TypeVar must appear at least twice to be
# considered legitimate.
# pyright: reportInvalidTypeVarUse=true
from typing import Dict, Generic, List, TypeVar

View File

@ -878,7 +878,7 @@ test('ParamSpec1', () => {
configOptions.defaultPythonVersion = PythonVersion.V3_10;
const results = TestUtils.typeAnalyzeSampleFiles(['paramSpec1.py'], configOptions);
TestUtils.validateResults(results, 8);
TestUtils.validateResults(results, 7);
});
test('ParamSpec2', () => {
@ -906,7 +906,7 @@ test('ParamSpec4', () => {
configOptions.defaultPythonVersion = PythonVersion.V3_10;
const results = TestUtils.typeAnalyzeSampleFiles(['paramSpec4.py'], configOptions);
TestUtils.validateResults(results, 7);
TestUtils.validateResults(results, 5);
});
test('ClassVar1', () => {

View File

@ -477,6 +477,17 @@
"error"
]
},
"reportInvalidTypeVarUse": {
"type": "string",
"description": "Diagnostics for improper use of type variables in a function signature.",
"default": "none",
"enum": [
"none",
"information",
"warning",
"error"
]
},
"reportCallInDefaultInitializer": {
"type": "string",
"description": "Diagnostics for function calls within a default value initialization expression. Such calls can mask expensive operations that are performed at module initialization time.",

View File

@ -321,6 +321,12 @@
"title": "Controls reporting generic class reference with missing type arguments",
"default": "none"
},
"reportInvalidTypeVarUse": {
"$id": "#/properties/reportInvalidTypeVarUse",
"$ref": "#/definitions/diagnostic",
"title": "Controls reporting improper use of type variables within function signatures",
"default": "none"
},
"reportCallInDefaultInitializer": {
"$id": "#/properties/reportCallInDefaultInitializer",
"$ref": "#/definitions/diagnostic",