Added three new diagnostic rules: reportArgumentType covers argument type compatibility checks, reportAssignmentType covers type compatibility checks for assignments, and reportReturnType covers type compatibility checks for return and yield statements. This partially addresses #6973. (#7077)

This commit is contained in:
Eric Traut 2024-01-21 02:20:52 -08:00 committed by GitHub
parent 6ac1a7eebf
commit 197ecd7bc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 128 additions and 34 deletions

View File

@ -92,8 +92,12 @@ The following settings control pyrights diagnostic output (warnings or errors
<a name="reportAbstractUsage"></a> **reportAbstractUsage** [boolean or string, optional]: Generate or suppress diagnostics for the attempted instantiate an abstract or protocol class or use of an abstract method. The default value for this setting is `"error"`.
<a name="reportArgumentType"></a> **reportArgumentType** [boolean or string, optional]: Generate or suppress diagnostics for argument type incompatibilities when evaluating a call expression. The default value for this setting is `"error"`.
<a name="reportAssertTypeFailure"></a> **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"`.
<a name="reportAssignmentType"></a> **reportAssignmentType** [boolean or string, optional]: Generate or suppress diagnostics for assignment type incompatibility. The default value for this setting is `"error"`.
<a name="reportAttributeAccessIssue"></a> **reportAttributeAccessIssue** [boolean or string, optional]: Generate or suppress diagnostics related to attribute accesses. The default value for this setting is `"error"`.
<a name="reportCallIssue"></a> **reportCallIssue** [boolean or string, optional]: Generate or suppress diagnostics related to call expressions and arguments passed to a call target. The default value for this setting is `"error"`.
@ -122,6 +126,8 @@ The following settings control pyrights diagnostic output (warnings or errors
<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="reportReturnType"></a> **reportReturnType** [boolean or string, optional]: Generate or suppress diagnostics related to function return type compatibility. 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"`.
@ -348,7 +354,9 @@ The following table lists the default severity levels for each diagnostic rule w
| reportUnusedExpression | "none" | "warning" | "warning" | "error" |
| reportWildcardImportFromLibrary | "none" | "warning" | "warning" | "error" |
| reportAbstractUsage | "none" | "error" | "error" | "error" |
| reportArgumentType | "none" | "error" | "error" | "error" |
| reportAssertTypeFailure | "none" | "error" | "error" | "error" |
| reportAssignmentType | "none" | "error" | "error" | "error" |
| reportAttributeAccessIssue | "none" | "error" | "error" | "error" |
| reportCallIssue | "none" | "error" | "error" | "error" |
| reportGeneralTypeIssues | "none" | "error" | "error" | "error" |
@ -364,6 +372,7 @@ The following table lists the default severity levels for each diagnostic rule w
| reportOptionalContextManager | "none" | "error" | "error" | "error" |
| reportOptionalOperand | "none" | "error" | "error" | "error" |
| reportRedeclaration | "none" | "error" | "error" | "error" |
| reportReturnType | "none" | "error" | "error" | "error" |
| reportTypedDictNotRequiredAccess | "none" | "error" | "error" | "error" |
| reportPrivateImportUsage | "none" | "error" | "error" | "error" |
| reportUnboundVariable | "none" | "error" | "error" | "error" |

View File

@ -1016,7 +1016,7 @@ export class Checker extends ParseTreeWalker {
}
this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportReturnType,
LocMessage.returnTypeMismatch().format({
exprType: this._evaluator.printType(returnType),
returnType: this._evaluator.printType(declaredReturnType),
@ -3673,7 +3673,7 @@ export class Checker extends ParseTreeWalker {
if (!isValidType) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
isInstanceCheck
? LocMessage.isInstanceInvalidType().format({
type: this._evaluator.printType(arg1Type),
@ -4660,7 +4660,7 @@ export class Checker extends ParseTreeWalker {
!FunctionType.isAsync(functionType)
) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportReturnType,
LocMessage.noReturnReturnsNone(),
returnAnnotation
);
@ -4677,7 +4677,7 @@ export class Checker extends ParseTreeWalker {
// the return type matches. This check can also be skipped for an overload.
if (!ParseTreeUtils.isSuiteEmpty(node.suite) && !FunctionType.isOverloaded(functionType)) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportReturnType,
LocMessage.returnMissing().format({
returnType: this._evaluator.printType(declaredReturnType),
}) + diagAddendum.getString(),
@ -4856,7 +4856,7 @@ export class Checker extends ParseTreeWalker {
) {
if (!this._evaluator.assignType(declaredValueType, assignedValueType, diag)) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportAssignmentType,
LocMessage.typeAssignmentMismatch().format(
this._evaluator.printSrcDestTypes(assignedValueType, declaredValueType)
) + diag.getString(),
@ -6816,7 +6816,7 @@ export class Checker extends ParseTreeWalker {
: LocMessage.generatorSyncReturnType();
this._evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportReturnType,
errorMessage.format({ yieldType: this._evaluator.printType(yieldType) }) +
(expectedDiagAddendum?.getString() ?? diagAddendum.getString()),
node.expression ?? node,

View File

@ -257,7 +257,7 @@ function applyPartialTransformToFunction(
if (!evaluator.assignType(paramType, argTypeResult.type, diag, typeVarContext)) {
if (errorNode) {
evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
LocMessage.argAssignmentParamFunction().format({
argType: evaluator.printType(argTypeResult.type),
paramType: evaluator.printType(paramType),
@ -303,7 +303,7 @@ function applyPartialTransformToFunction(
if (!evaluator.assignType(paramType, argTypeResult.type, diag, typeVarContext)) {
if (errorNode) {
evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
LocMessage.argAssignmentParamFunction().format({
argType: evaluator.printType(argTypeResult.type),
paramType: evaluator.printType(paramType),
@ -353,7 +353,7 @@ function applyPartialTransformToFunction(
if (!evaluator.assignType(paramType, argTypeResult.type, diag, typeVarContext)) {
if (errorNode) {
evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
LocMessage.argAssignmentParamFunction().format({
argType: evaluator.printType(argTypeResult.type),
paramType: evaluator.printType(paramType),
@ -393,7 +393,7 @@ function applyPartialTransformToFunction(
if (!evaluator.assignType(paramType, argTypeResult.type, diag, typeVarContext)) {
if (errorNode) {
evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
LocMessage.argAssignmentParamFunction().format({
argType: evaluator.printType(argTypeResult.type),
paramType: evaluator.printType(paramType),

View File

@ -629,7 +629,7 @@ function validateFallbackConstructorCall(
if (argList.length > 0 && argList.some((arg) => arg.argumentCategory === ArgumentCategory.Simple)) {
if (!type.includeSubclasses) {
evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportCallIssue,
LocMessage.constructorNoArgs().format({ type: type.aliasName || type.details.name }),
errorNode
);

View File

@ -87,7 +87,7 @@ export function createNamedTupleType(
const nameArg = argList[0];
if (nameArg.argumentCategory !== ArgumentCategory.Simple) {
evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
LocMessage.namedTupleFirstArg(),
argList[0].valueExpression || errorNode
);
@ -257,7 +257,7 @@ export function createNamedTupleType(
);
} else {
evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
LocMessage.namedTupleNameType(),
entry
);

View File

@ -3267,7 +3267,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportAssignmentType,
LocMessage.typeAssignmentMismatch().format(printSrcDestTypes(type, declaredType)) +
diagAddendum.getString(),
srcExpression ?? nameNode,
@ -3645,7 +3645,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (!diagAddendum.isEmpty()) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportAssignmentType,
(target.nodeType === ParseNodeType.List
? LocMessage.listAssignmentMismatch()
: LocMessage.tupleAssignmentMismatch()
@ -8259,7 +8259,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (!isAnyOrUnknown(concreteTargetClassType) && !isInstantiableClass(concreteTargetClassType)) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
LocMessage.superCallFirstArg().format({ type: printType(targetClassType) }),
node.arguments[0].valueExpression
);
@ -8328,7 +8328,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (reportError) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
LocMessage.superCallSecondArg().format({ type: printType(targetClassType) }),
node.arguments[1].valueExpression
);
@ -12006,7 +12006,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// printing types if the diagnostic is disabled.
const fileInfo = AnalyzerNodeInfo.getFileInfo(argParam.errorNode);
if (
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues !== 'none' &&
fileInfo.diagnosticRuleSet.reportArgumentType !== 'none' &&
!isDiagnosticSuppressedForNode(argParam.errorNode) &&
!isTypeIncomplete
) {
@ -12052,7 +12052,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
message + diag.getString(),
argParam.errorNode,
diag.getEffectiveTextRange() ?? argParam.errorNode
@ -12660,11 +12660,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
if (!className) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
LocMessage.newTypeBadName(),
argList[0].node ?? errorNode
);
addDiagnostic(DiagnosticRule.reportArgumentType, LocMessage.newTypeBadName(), argList[0].node ?? errorNode);
return undefined;
}
@ -17423,7 +17419,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (!assignType(annotatedType, defaultValueType, diagAddendum, typeVarContext)) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
LocMessage.paramAssignmentMismatch().format({
sourceType: printType(defaultValueType),
paramType: printType(annotatedType),
@ -18608,7 +18604,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (!assignType(declaredType, importedSymbolType, diagAddendum)) {
addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportAssignmentType,
LocMessage.typeAssignmentMismatchWildcard().format({
...printSrcDestTypes(importedSymbolType, declaredType),
name,

View File

@ -88,7 +88,7 @@ export function createTypedDictType(
nameArg.valueExpression.nodeType !== ParseNodeType.StringList
) {
evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
DiagnosticRule.reportArgumentType,
LocMessage.typedDictFirstArg(),
argList[0].valueExpression || errorNode
);
@ -171,11 +171,7 @@ export function createTypedDictType(
classFields.set(entry.name.value, newSymbol);
}
} else {
evaluator.addDiagnostic(
DiagnosticRule.reportGeneralTypeIssues,
LocMessage.typedDictSecondArgDict(),
errorNode
);
evaluator.addDiagnostic(DiagnosticRule.reportArgumentType, LocMessage.typedDictSecondArgDict(), errorNode);
}
}

View File

@ -162,9 +162,15 @@ export interface DiagnosticRuleSet {
// Report use of abstract method or variable?
reportAbstractUsage: DiagnosticLevel;
// Report argument type incompatibilities?
reportArgumentType: DiagnosticLevel;
// Report failure of assert_type call?
reportAssertTypeFailure: DiagnosticLevel;
// Report type incompatibility for assignments?
reportAssignmentType: DiagnosticLevel;
// Report issues related to attribute access expressions?
reportAttributeAccessIssue: DiagnosticLevel;
@ -207,6 +213,9 @@ export interface DiagnosticRuleSet {
// Report attempts to redeclare the type of a symbol?
reportRedeclaration: DiagnosticLevel;
// Report return type mismatches?
reportReturnType: DiagnosticLevel;
// Report accesses to non-required TypedDict fields?
reportTypedDictNotRequiredAccess: DiagnosticLevel;
@ -412,7 +421,9 @@ export function getDiagLevelDiagnosticRules() {
DiagnosticRule.reportDuplicateImport,
DiagnosticRule.reportWildcardImportFromLibrary,
DiagnosticRule.reportAbstractUsage,
DiagnosticRule.reportArgumentType,
DiagnosticRule.reportAssertTypeFailure,
DiagnosticRule.reportAssignmentType,
DiagnosticRule.reportAttributeAccessIssue,
DiagnosticRule.reportCallIssue,
DiagnosticRule.reportInconsistentOverload,
@ -427,6 +438,7 @@ export function getDiagLevelDiagnosticRules() {
DiagnosticRule.reportOptionalContextManager,
DiagnosticRule.reportOptionalOperand,
DiagnosticRule.reportRedeclaration,
DiagnosticRule.reportReturnType,
DiagnosticRule.reportTypedDictNotRequiredAccess,
DiagnosticRule.reportUntypedFunctionDecorator,
DiagnosticRule.reportUntypedClassDecorator,
@ -514,7 +526,9 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet {
reportDuplicateImport: 'none',
reportWildcardImportFromLibrary: 'none',
reportAbstractUsage: 'none',
reportArgumentType: 'none',
reportAssertTypeFailure: 'none',
reportAssignmentType: 'none',
reportAttributeAccessIssue: 'none',
reportCallIssue: 'none',
reportInconsistentOverload: 'none',
@ -529,6 +543,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet {
reportOptionalContextManager: 'none',
reportOptionalOperand: 'none',
reportRedeclaration: 'none',
reportReturnType: 'none',
reportTypedDictNotRequiredAccess: 'none',
reportUntypedFunctionDecorator: 'none',
reportUntypedClassDecorator: 'none',
@ -612,7 +627,9 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet {
reportDuplicateImport: 'none',
reportWildcardImportFromLibrary: 'warning',
reportAbstractUsage: 'error',
reportArgumentType: 'error',
reportAssertTypeFailure: 'error',
reportAssignmentType: 'error',
reportAttributeAccessIssue: 'error',
reportCallIssue: 'error',
reportInconsistentOverload: 'error',
@ -627,6 +644,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet {
reportOptionalContextManager: 'error',
reportOptionalOperand: 'error',
reportRedeclaration: 'error',
reportReturnType: 'error',
reportTypedDictNotRequiredAccess: 'error',
reportUntypedFunctionDecorator: 'none',
reportUntypedClassDecorator: 'none',
@ -710,7 +728,9 @@ export function getStandardDiagnosticRuleSet(): DiagnosticRuleSet {
reportDuplicateImport: 'none',
reportWildcardImportFromLibrary: 'warning',
reportAbstractUsage: 'error',
reportArgumentType: 'error',
reportAssertTypeFailure: 'error',
reportAssignmentType: 'error',
reportAttributeAccessIssue: 'error',
reportCallIssue: 'error',
reportInconsistentOverload: 'error',
@ -725,6 +745,7 @@ export function getStandardDiagnosticRuleSet(): DiagnosticRuleSet {
reportOptionalContextManager: 'error',
reportOptionalOperand: 'error',
reportRedeclaration: 'error',
reportReturnType: 'error',
reportTypedDictNotRequiredAccess: 'error',
reportUntypedFunctionDecorator: 'none',
reportUntypedClassDecorator: 'none',
@ -808,7 +829,9 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
reportDuplicateImport: 'error',
reportWildcardImportFromLibrary: 'error',
reportAbstractUsage: 'error',
reportArgumentType: 'error',
reportAssertTypeFailure: 'error',
reportAssignmentType: 'error',
reportAttributeAccessIssue: 'error',
reportCallIssue: 'error',
reportInconsistentOverload: 'error',
@ -823,6 +846,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
reportOptionalContextManager: 'error',
reportOptionalOperand: 'error',
reportRedeclaration: 'error',
reportReturnType: 'error',
reportTypedDictNotRequiredAccess: 'error',
reportUntypedFunctionDecorator: 'error',
reportUntypedClassDecorator: 'error',

View File

@ -36,7 +36,9 @@ export enum DiagnosticRule {
reportDuplicateImport = 'reportDuplicateImport',
reportWildcardImportFromLibrary = 'reportWildcardImportFromLibrary',
reportAbstractUsage = 'reportAbstractUsage',
reportArgumentType = 'reportArgumentType',
reportAssertTypeFailure = 'reportAssertTypeFailure',
reportAssignmentType = 'reportAssignmentType',
reportAttributeAccessIssue = 'reportAttributeAccessIssue',
reportCallIssue = 'reportCallIssue',
reportInconsistentOverload = 'reportInconsistentOverload',
@ -51,6 +53,7 @@ export enum DiagnosticRule {
reportOptionalContextManager = 'reportOptionalContextManager',
reportOptionalOperand = 'reportOptionalOperand',
reportRedeclaration = 'reportRedeclaration',
reportReturnType = 'reportReturnType',
reportTypedDictNotRequiredAccess = 'reportTypedDictNotRequiredAccess',
reportUntypedFunctionDecorator = 'reportUntypedFunctionDecorator',
reportUntypedClassDecorator = 'reportUntypedClassDecorator',

View File

@ -19,4 +19,4 @@ def func1(self, x: int | None) -> str:
# One of these is unnecessary
v5 = x + "hi" # test # pyright: ignore [reportOperatorIssue, foo]
return 3 # pyright: ignore [reportGeneralTypeIssues]
return 3 # pyright: ignore [reportReturnType]

View File

@ -431,12 +431,44 @@
false
]
},
"reportArgumentType": {
"type": [
"string",
"boolean"
],
"description": "Diagnostics for a type incompatibility for an argument to a call.",
"default": "error",
"enum": [
"none",
"information",
"warning",
"error",
true,
false
]
},
"reportAssertTypeFailure": {
"type": [
"string",
"boolean"
],
"description": "Diagnostics for a type mismatch detected by a typing.assert_type call.",
"description": "Diagnostics for a type incompatibility detected by a typing.assert_type call.",
"default": "error",
"enum": [
"none",
"information",
"warning",
"error",
true,
false
]
},
"reportAssignmentType": {
"type": [
"string",
"boolean"
],
"description": "Diagnostics for type incompatibilities for assignments.",
"default": "error",
"enum": [
"none",
@ -671,6 +703,22 @@
false
]
},
"reportReturnType": {
"type": [
"string",
"boolean"
],
"description": "Diagnostics related to function return type compatibility.",
"default": "error",
"enum": [
"none",
"information",
"warning",
"error",
true,
false
]
},
"reportTypedDictNotRequiredAccess": {
"type": [
"string",

View File

@ -257,12 +257,24 @@
"title": "Controls reporting of attempted instantiation of abstract class",
"default": "error"
},
"reportArgumentType": {
"$id": "#/properties/reportArgumentType",
"$ref": "#/definitions/diagnostic",
"title": "Controls reporting of incompatible argument type",
"default": "error"
},
"reportAssertTypeFailure": {
"$id": "#/properties/reportAssertTypeFailure",
"$ref": "#/definitions/diagnostic",
"title": "Controls reporting of type mismatch detected by typing.assert_type call",
"default": "error"
},
"reportAssignmentType": {
"$id": "#/properties/reportAssignmentType",
"$ref": "#/definitions/diagnostic",
"title": "Controls reporting of type incompatibilities for assignments",
"default": "error"
},
"reportAttributeAccessIssue": {
"$id": "#/properties/reportAttributeAccessIssue",
"$ref": "#/definitions/diagnostic",
@ -347,6 +359,12 @@
"title": "Controls reporting of attempts to declare the type of a symbol multiple times",
"default": "error"
},
"reportReturnType": {
"$id": "#/properties/reportReturnType",
"$ref": "#/definitions/diagnostic",
"title": "Controls reporting of function return type incompatibility",
"default": "error"
},
"reportTypedDictNotRequiredAccess": {
"$id": "#/properties/reportTypedDictNotRequiredAccess",
"$ref": "#/definitions/diagnostic",