Added two new diagnostic rules: reportInvalidTypeArguments reports invalid type arg usage and reportRedeclaration reports attempts to redeclare the type of a symbol. This partially addresses #6973. (#7073)

This commit is contained in:
Eric Traut 2024-01-21 00:33:05 -08:00 committed by GitHub
parent 7a67f4fbdb
commit ec6052ea9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 83 additions and 15 deletions

View File

@ -94,6 +94,8 @@ The following settings control pyrights diagnostic output (warnings or errors
<a name="reportInconsistentOverload"></a> **reportInconsistentOverload** [boolean or string, optional]: Generate or suppress diagnostics for an overloaded function that has overload signatures that are inconsistent with each other or with the implementation. The default value for this setting is `"error"`.
<a name="reportInvalidTypeArguments"></a> **reportInvalidTypeArguments** [boolean or string, optional]: Generate or suppress diagnostics for invalid type argument usage. The default value for this setting is `"error"`.
<a name="reportNoOverloadImplementation"></a> **reportNoOverloadImplementation** [boolean or string, optional]: Generate or suppress diagnostics for an overloaded function or method if the implementation is not provided. The default value for this setting is `"error"`.
<a name="reportOptionalSubscript"></a> **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"`.
@ -108,6 +110,8 @@ The following settings control pyrights diagnostic output (warnings or errors
<a name="reportOptionalOperand"></a> **reportOptionalOperand** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to use an Optional type as an operand to a unary operator (like `~` or `not`) or the left-hand operator of a binary operator (like `*`, `==`, `or`). The default value for this setting is `"error"`.
<a name="reportRedeclaration"></a> **reportRedeclaration** [boolean or string, optional]: Generate or suppress diagnostics for a symbol that has more than one type declaration. The default value for this setting is `"error"`.
<a name="reportTypedDictNotRequiredAccess"></a> **reportTypedDictNotRequiredAccess** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to access a non-required field within a TypedDict without first checking whether it is present. The default value for this setting is `"error"`.
<a name="reportUntypedFunctionDecorator"></a> **reportUntypedFunctionDecorator** [boolean or string, optional]: Generate or suppress diagnostics for function decorators that have no type annotations. These obscure the function type, defeating many type analysis features. The default value for this setting is `"none"`.
@ -336,6 +340,7 @@ The following table lists the default severity levels for each diagnostic rule w
| reportAssertTypeFailure | "none" | "error" | "error" | "error" |
| reportGeneralTypeIssues | "none" | "error" | "error" | "error" |
| reportInconsistentOverload | "none" | "error" | "error" | "error" |
| reportInvalidTypeArguments | "none" | "error" | "error" | "error" |
| reportNoOverloadImplementation | "none" | "error" | "error" | "error" |
| reportOptionalSubscript | "none" | "error" | "error" | "error" |
| reportOptionalMemberAccess | "none" | "error" | "error" | "error" |
@ -343,6 +348,7 @@ The following table lists the default severity levels for each diagnostic rule w
| reportOptionalIterable | "none" | "error" | "error" | "error" |
| reportOptionalContextManager | "none" | "error" | "error" | "error" |
| reportOptionalOperand | "none" | "error" | "error" | "error" |
| reportRedeclaration | "none" | "error" | "error" | "error" |
| reportTypedDictNotRequiredAccess | "none" | "error" | "error" | "error" |
| reportPrivateImportUsage | "none" | "error" | "error" | "error" |
| reportUnboundVariable | "none" | "error" | "error" | "error" |

View File

@ -3193,7 +3193,7 @@ export class Checker extends ParseTreeWalker {
decls.forEach((decl) => {
if (decl !== typeAliasDecl) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportRedeclaration,
LocMessage.typeAliasRedeclared().format({ name }),
decl.node
);
@ -3317,7 +3317,7 @@ export class Checker extends ParseTreeWalker {
if (!duplicateIsOk) {
const diag = this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportRedeclaration,
LocMessage.obscuredClassDeclaration().format({ name }),
otherDecl.node.name
);
@ -3361,7 +3361,7 @@ export class Checker extends ParseTreeWalker {
if (!duplicateIsOk) {
const diag = this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportRedeclaration,
otherDecl.isMethod
? LocMessage.obscuredMethodDeclaration().format({ name })
: LocMessage.obscuredFunctionDeclaration().format({ name }),
@ -3382,7 +3382,7 @@ export class Checker extends ParseTreeWalker {
if (!duplicateIsOk) {
const message = LocMessage.obscuredParameterDeclaration();
const diag = this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportRedeclaration,
message.format({ name }),
otherDecl.node.name
);
@ -3410,7 +3410,7 @@ export class Checker extends ParseTreeWalker {
if (!duplicateIsOk) {
const diag = this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportRedeclaration,
LocMessage.obscuredVariableDeclaration().format({ name }),
otherDecl.node
);
@ -3420,7 +3420,7 @@ export class Checker extends ParseTreeWalker {
}
} else if (otherDecl.type === DeclarationType.TypeAlias) {
const diag = this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportRedeclaration,
LocMessage.obscuredTypeAliasDeclaration().format({ name }),
otherDecl.node.name
);

View File

@ -6879,7 +6879,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if ((flags & EvaluatorFlags.ExpectingTypeAnnotation) !== 0) {
// If the class doesn't derive from Generic, a type argument should not be allowed.
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportInvalidTypeArguments,
LocMessage.typeArgsExpectingNone().format({
name: printType(ClassType.cloneAsInstance(concreteSubtype)),
}),
@ -6981,7 +6981,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (concreteSubtype.typeArguments) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportInvalidTypeArguments,
LocMessage.classAlreadySpecialized().format({
type: printType(convertToInstance(concreteSubtype), { expandTypeAlias: true }),
}),
@ -14712,7 +14712,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// Self doesn't support any type arguments.
if (typeArgs) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportInvalidTypeArguments,
LocMessage.typeArgsExpectingNone().format({
name: classType.details.name,
}),
@ -15193,7 +15193,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// is allowed if it's an unpacked variadic type var or tuple. None is also allowed
// since it is used to define NoReturn in typeshed stubs).
if (types.length === 1 && !allowSingleTypeArg && !isNoneInstance(types[0])) {
addDiagnostic(DiagnosticRule.reportGeneralTypeIssues, LocMessage.unionTypeArgCount(), errorNode);
addDiagnostic(DiagnosticRule.reportInvalidTypeArguments, LocMessage.unionTypeArgCount(), errorNode);
}
let unionType = combineTypes(types);
@ -19577,7 +19577,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (typeArgs[0].inlinedTypeDict) {
if (typeArgs.length > 1) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportInvalidTypeArguments,
LocMessage.typeArgsTooMany().format({
name: classType.aliasName || classType.details.name,
expected: 1,
@ -19592,7 +19592,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (!ClassType.isPartiallyEvaluated(classType) && !ClassType.isTupleClass(classType)) {
if (typeParameters.length === 0) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportInvalidTypeArguments,
LocMessage.typeArgsExpectingNone().format({
name: classType.aliasName || classType.details.name,
}),
@ -19600,7 +19600,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
);
} else if (typeParameters.length !== 1 || !isParamSpec(typeParameters[0])) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportInvalidTypeArguments,
LocMessage.typeArgsTooMany().format({
name: classType.aliasName || classType.details.name,
expected: typeParameters.length,
@ -19614,7 +19614,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
} else if (typeArgCount < minTypeArgCount) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportInvalidTypeArguments,
LocMessage.typeArgsTooFew().format({
name: classType.aliasName || classType.details.name,
expected: minTypeArgCount,
@ -19797,7 +19797,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (!isClassInstance(typeArgType) || !ClassType.isPartiallyEvaluated(typeArgType)) {
assert(typeArgs !== undefined);
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportInvalidTypeArguments,
LocMessage.typeVarAssignmentMismatch().format({
type: printType(typeArgType),
name: TypeVarType.getReadableName(typeParameters[index]),

View File

@ -165,6 +165,9 @@ export interface DiagnosticRuleSet {
// Report inconsistencies with function overload signatures?
reportInconsistentOverload: DiagnosticLevel;
// Report invalid type argument usage?
reportInvalidTypeArguments: DiagnosticLevel;
// Report missing overloaded function implementation?
reportNoOverloadImplementation: DiagnosticLevel;
@ -186,6 +189,9 @@ export interface DiagnosticRuleSet {
// Report attempts to use an Optional type in a binary or unary operation?
reportOptionalOperand: DiagnosticLevel;
// Report attempts to redeclare the type of a symbol?
reportRedeclaration: DiagnosticLevel;
// Report accesses to non-required TypedDict fields?
reportTypedDictNotRequiredAccess: DiagnosticLevel;
@ -392,6 +398,7 @@ export function getDiagLevelDiagnosticRules() {
DiagnosticRule.reportWildcardImportFromLibrary,
DiagnosticRule.reportAssertTypeFailure,
DiagnosticRule.reportInconsistentOverload,
DiagnosticRule.reportInvalidTypeArguments,
DiagnosticRule.reportNoOverloadImplementation,
DiagnosticRule.reportOptionalSubscript,
DiagnosticRule.reportOptionalMemberAccess,
@ -399,6 +406,7 @@ export function getDiagLevelDiagnosticRules() {
DiagnosticRule.reportOptionalIterable,
DiagnosticRule.reportOptionalContextManager,
DiagnosticRule.reportOptionalOperand,
DiagnosticRule.reportRedeclaration,
DiagnosticRule.reportTypedDictNotRequiredAccess,
DiagnosticRule.reportUntypedFunctionDecorator,
DiagnosticRule.reportUntypedClassDecorator,
@ -487,6 +495,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet {
reportWildcardImportFromLibrary: 'none',
reportAssertTypeFailure: 'none',
reportInconsistentOverload: 'none',
reportInvalidTypeArguments: 'none',
reportNoOverloadImplementation: 'none',
reportOptionalSubscript: 'none',
reportOptionalMemberAccess: 'none',
@ -494,6 +503,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet {
reportOptionalIterable: 'none',
reportOptionalContextManager: 'none',
reportOptionalOperand: 'none',
reportRedeclaration: 'none',
reportTypedDictNotRequiredAccess: 'none',
reportUntypedFunctionDecorator: 'none',
reportUntypedClassDecorator: 'none',
@ -578,6 +588,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet {
reportWildcardImportFromLibrary: 'warning',
reportAssertTypeFailure: 'error',
reportInconsistentOverload: 'error',
reportInvalidTypeArguments: 'error',
reportNoOverloadImplementation: 'error',
reportOptionalSubscript: 'error',
reportOptionalMemberAccess: 'error',
@ -585,6 +596,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet {
reportOptionalIterable: 'error',
reportOptionalContextManager: 'error',
reportOptionalOperand: 'error',
reportRedeclaration: 'error',
reportTypedDictNotRequiredAccess: 'error',
reportUntypedFunctionDecorator: 'none',
reportUntypedClassDecorator: 'none',
@ -669,6 +681,7 @@ export function getStandardDiagnosticRuleSet(): DiagnosticRuleSet {
reportWildcardImportFromLibrary: 'warning',
reportAssertTypeFailure: 'error',
reportInconsistentOverload: 'error',
reportInvalidTypeArguments: 'error',
reportNoOverloadImplementation: 'error',
reportOptionalSubscript: 'error',
reportOptionalMemberAccess: 'error',
@ -676,6 +689,7 @@ export function getStandardDiagnosticRuleSet(): DiagnosticRuleSet {
reportOptionalIterable: 'error',
reportOptionalContextManager: 'error',
reportOptionalOperand: 'error',
reportRedeclaration: 'error',
reportTypedDictNotRequiredAccess: 'error',
reportUntypedFunctionDecorator: 'none',
reportUntypedClassDecorator: 'none',
@ -760,6 +774,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
reportWildcardImportFromLibrary: 'error',
reportAssertTypeFailure: 'error',
reportInconsistentOverload: 'error',
reportInvalidTypeArguments: 'error',
reportNoOverloadImplementation: 'error',
reportOptionalSubscript: 'error',
reportOptionalMemberAccess: 'error',
@ -767,6 +782,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
reportOptionalIterable: 'error',
reportOptionalContextManager: 'error',
reportOptionalOperand: 'error',
reportRedeclaration: 'error',
reportTypedDictNotRequiredAccess: 'error',
reportUntypedFunctionDecorator: 'error',
reportUntypedClassDecorator: 'error',

View File

@ -37,6 +37,7 @@ export enum DiagnosticRule {
reportWildcardImportFromLibrary = 'reportWildcardImportFromLibrary',
reportAssertTypeFailure = 'reportAssertTypeFailure',
reportInconsistentOverload = 'reportInconsistentOverload',
reportInvalidTypeArguments = 'reportInvalidTypeArguments',
reportNoOverloadImplementation = 'reportNoOverloadImplementation',
reportOptionalSubscript = 'reportOptionalSubscript',
reportOptionalMemberAccess = 'reportOptionalMemberAccess',
@ -44,6 +45,7 @@ export enum DiagnosticRule {
reportOptionalIterable = 'reportOptionalIterable',
reportOptionalContextManager = 'reportOptionalContextManager',
reportOptionalOperand = 'reportOptionalOperand',
reportRedeclaration = 'reportRedeclaration',
reportTypedDictNotRequiredAccess = 'reportTypedDictNotRequiredAccess',
reportUntypedFunctionDecorator = 'reportUntypedFunctionDecorator',
reportUntypedClassDecorator = 'reportUntypedClassDecorator',

View File

@ -447,6 +447,22 @@
false
]
},
"reportInvalidTypeArguments": {
"type": [
"string",
"boolean"
],
"description": "Diagnostics for invalid type argument usage.",
"default": "error",
"enum": [
"none",
"information",
"warning",
"error",
true,
false
]
},
"reportNoOverloadImplementation": {
"type": [
"string",
@ -559,6 +575,22 @@
false
]
},
"reportRedeclaration": {
"type": [
"string",
"boolean"
],
"description": "Diagnostics for an attempt to declare the type of a symbol multiple times.",
"default": "error",
"enum": [
"none",
"information",
"warning",
"error",
true,
false
]
},
"reportTypedDictNotRequiredAccess": {
"type": [
"string",

View File

@ -263,6 +263,12 @@
"title": "Controls reporting of inconsistencies between function overload signatures",
"default": "error"
},
"reportInvalidTypeArguments": {
"$id": "#/properties/reportInvalidTypeArguments",
"$ref": "#/definitions/diagnostic",
"title": "Controls reporting of invalid type argument usage",
"default": "error"
},
"reportNoOverloadImplementation": {
"$id": "#/properties/reportNoOverloadImplementation",
"$ref": "#/definitions/diagnostic",
@ -305,6 +311,12 @@
"title": "Controls reporting of attempts to use an Optional type as an operand for a binary or unary operator",
"default": "error"
},
"reportRedeclaration": {
"$id": "#/properties/reportRedeclaration",
"$ref": "#/definitions/diagnostic",
"title": "Controls reporting of attempts to declare the type of a symbol multiple times",
"default": "error"
},
"reportTypedDictNotRequiredAccess": {
"$id": "#/properties/reportTypedDictNotRequiredAccess",
"$ref": "#/definitions/diagnostic",