From 6ac1a7eebf0731da1af94ce18734d1bdd80880f1 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sun, 21 Jan 2024 01:47:43 -0800 Subject: [PATCH] Added new diagnostic rule `reportCallIssue` that covers issues relating to call expressions and arguments. This partially addresses #6973. (#7076) --- docs/configuration.md | 3 ++ .../src/analyzer/constructorTransform.ts | 8 +-- .../src/analyzer/dataClasses.ts | 2 +- .../src/analyzer/namedTuples.ts | 4 +- .../src/analyzer/typeEvaluator.ts | 50 +++++++++---------- .../src/analyzer/typedDicts.ts | 6 +-- .../src/common/configOptions.ts | 8 +++ .../src/common/diagnosticRules.ts | 1 + packages/vscode-pyright/package.json | 16 ++++++ .../schemas/pyrightconfig.schema.json | 6 +++ 10 files changed, 69 insertions(+), 35 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 8e806a6e7..8d9c8813f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -96,6 +96,8 @@ The following settings control pyright’s diagnostic output (warnings or errors **reportAttributeAccessIssue** [boolean or string, optional]: Generate or suppress diagnostics related to attribute accesses. The default value for this setting is `"error"`. + **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"`. + **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"`. **reportIndexIssue** [boolean or string, optional]: Generate or suppress diagnostics related to index operations and expressions. The default value for this setting is `"error"`. @@ -348,6 +350,7 @@ The following table lists the default severity levels for each diagnostic rule w | reportAbstractUsage | "none" | "error" | "error" | "error" | | reportAssertTypeFailure | "none" | "error" | "error" | "error" | | reportAttributeAccessIssue | "none" | "error" | "error" | "error" | +| reportCallIssue | "none" | "error" | "error" | "error" | | reportGeneralTypeIssues | "none" | "error" | "error" | "error" | | reportInconsistentOverload | "none" | "error" | "error" | "error" | | reportIndexIssue | "none" | "error" | "error" | "error" | diff --git a/packages/pyright-internal/src/analyzer/constructorTransform.ts b/packages/pyright-internal/src/analyzer/constructorTransform.ts index e34a9e5b7..aef4cf4c7 100644 --- a/packages/pyright-internal/src/analyzer/constructorTransform.ts +++ b/packages/pyright-internal/src/analyzer/constructorTransform.ts @@ -170,7 +170,7 @@ function applyPartialTransform( if (applicableOverloads.length === 0) { if (sawArgErrors) { evaluator.addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.noOverload().format({ name: origFunctionType.overloads[0].details.name, }), @@ -275,7 +275,7 @@ function applyPartialTransformToFunction( if (!reportedPositionalError) { if (errorNode) { evaluator.addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, paramListDetails.positionParamCount === 1 ? LocMessage.argPositionalExpectedOne() : LocMessage.argPositionalExpectedCount().format({ @@ -331,7 +331,7 @@ function applyPartialTransformToFunction( if (paramListDetails.kwargsIndex === undefined) { if (errorNode) { evaluator.addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.paramNameMissing().format({ name: arg.name.value }), arg.name ); @@ -374,7 +374,7 @@ function applyPartialTransformToFunction( if (paramMap.has(paramName)) { if (errorNode) { evaluator.addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.paramAlreadyAssigned().format({ name: arg.name.value }), arg.name ); diff --git a/packages/pyright-internal/src/analyzer/dataClasses.ts b/packages/pyright-internal/src/analyzer/dataClasses.ts index 02ad5e406..a5d3d7e46 100644 --- a/packages/pyright-internal/src/analyzer/dataClasses.ts +++ b/packages/pyright-internal/src/analyzer/dataClasses.ts @@ -1020,7 +1020,7 @@ export function validateDataClassTransformDecorator( node.arguments.forEach((arg) => { if (!arg.name || arg.argumentCategory !== ArgumentCategory.Simple) { evaluator.addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.dataClassTransformPositionalParam(), arg ); diff --git a/packages/pyright-internal/src/analyzer/namedTuples.ts b/packages/pyright-internal/src/analyzer/namedTuples.ts index 225f93a2e..56204dac0 100644 --- a/packages/pyright-internal/src/analyzer/namedTuples.ts +++ b/packages/pyright-internal/src/analyzer/namedTuples.ts @@ -82,7 +82,7 @@ export function createNamedTupleType( } if (argList.length === 0) { - evaluator.addDiagnostic(DiagnosticRule.reportGeneralTypeIssues, LocMessage.namedTupleFirstArg(), errorNode); + evaluator.addDiagnostic(DiagnosticRule.reportCallIssue, LocMessage.namedTupleFirstArg(), errorNode); } else { const nameArg = argList[0]; if (nameArg.argumentCategory !== ArgumentCategory.Simple) { @@ -162,7 +162,7 @@ export function createNamedTupleType( const entryTypes: Type[] = []; if (argList.length < 2) { - evaluator.addDiagnostic(DiagnosticRule.reportGeneralTypeIssues, LocMessage.namedTupleSecondArg(), errorNode); + evaluator.addDiagnostic(DiagnosticRule.reportCallIssue, LocMessage.namedTupleSecondArg(), errorNode); addGenericGetAttribute = true; } else { const entriesArg = argList[1]; diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index 15bfe4708..a85b8f9ee 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -8898,7 +8898,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions diagAddendum.addMessage(LocAddendum.argumentTypes().format({ types: argTypes.join(', ') })); addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.noOverload().format({ name: functionName }) + diagAddendum.getString(), errorNode ); @@ -8925,7 +8925,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (emitNoOverloadFoundError) { const functionName = bestMatch.overload.details.name || ''; const diagnostic = addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.noOverload().format({ name: functionName }), errorNode ); @@ -9116,7 +9116,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (callTypeResult.type.specialForm) { const exprNode = errorNode.nodeType === ParseNodeType.Call ? errorNode.leftExpression : errorNode; addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.objectNotCallable().format({ type: printType(callTypeResult.type, { expandTypeAlias: true }), }), @@ -9284,7 +9284,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions } case TypeCategory.Module: { - addDiagnostic(DiagnosticRule.reportGeneralTypeIssues, LocMessage.moduleNotCallable(), errorNode); + addDiagnostic(DiagnosticRule.reportCallIssue, LocMessage.moduleNotCallable(), errorNode); return { argumentErrors: true }; } @@ -9304,7 +9304,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions ): CallResult { if (TypeBase.isInstantiable(expandedCallType)) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.callableNotInstantiable().format({ type: printType(expandedCallType), }), @@ -9544,7 +9544,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions inferenceContext: InferenceContext | undefined ): CallResult { if (expandedCallType.literalValue !== undefined) { - addDiagnostic(DiagnosticRule.reportGeneralTypeIssues, LocMessage.literalNotCallable(), errorNode); + addDiagnostic(DiagnosticRule.reportCallIssue, LocMessage.literalNotCallable(), errorNode); return { returnType: UnknownType.create(), argumentErrors: true }; } @@ -9556,7 +9556,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (isInstantiableMetaclass(expandedCallType)) { if (expandedCallType.typeArguments && expandedCallType.isTypeArgumentExplicit) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.objectNotCallable().format({ type: printType(expandedCallType), }), @@ -9666,7 +9666,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (ClassType.isSpecialFormClass(expandedCallType)) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.typeNotIntantiable().format({ type: className }), errorNode ); @@ -9837,7 +9837,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (!callMethodType) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.objectNotCallable().format({ type: printType(expandedCallType), }), @@ -10102,7 +10102,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (argIndex < positionalOnlyLimitIndex && argList[argIndex].name) { const nameNode = argList[argIndex].name; if (nameNode) { - addDiagnostic(DiagnosticRule.reportGeneralTypeIssues, LocMessage.argPositional(), nameNode); + addDiagnostic(DiagnosticRule.reportCallIssue, LocMessage.argPositional(), nameNode); reportedArgError = true; } } @@ -10142,7 +10142,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (tooManyPositionals) { if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, positionParamLimitIndex === 1 ? LocMessage.argPositionalExpectedOne() : LocMessage.argPositionalExpectedCount().format({ @@ -10187,7 +10187,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions ) { if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, positionParamLimitIndex === 1 ? LocMessage.argPositionalExpectedOne() : LocMessage.argPositionalExpectedCount().format({ @@ -10266,7 +10266,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (isParamVariadic && !isArgCompatibleWithVariadic) { if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.unpackedArgWithVariadicParam(), argList[argIndex].valueExpression || errorNode ); @@ -10340,7 +10340,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { // Have we run out of arguments and still have parameters left to fill? addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, remainingArgCount === 1 ? LocMessage.argMorePositionalExpectedOne() : LocMessage.argMorePositionalExpectedCount().format({ @@ -10441,7 +10441,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (argsRemainingCount > 0) { if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, argsRemainingCount === 1 ? LocMessage.argMorePositionalExpectedOne() : LocMessage.argMorePositionalExpectedCount().format({ @@ -10536,7 +10536,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (!diag.isEmpty()) { if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.unpackedTypedDictArgument() + diag.getString(), argList[argIndex].valueExpression || errorNode ); @@ -10615,7 +10615,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (!isValidMappingType) { if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.unpackedDictArgumentNotMapping(), argList[argIndex].valueExpression || errorNode ); @@ -10640,7 +10640,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (paramEntry.argsReceived > 0) { if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.paramAlreadyAssigned().format({ name: paramNameValue }), paramName ); @@ -10692,7 +10692,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions } else { if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.paramNameMissing().format({ name: paramName.value }), paramName ); @@ -10705,7 +10705,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions } else { if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, positionParamLimitIndex === 1 ? LocMessage.argPositionalExpectedOne() : LocMessage.argPositionalExpectedCount().format({ @@ -10795,7 +10795,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions const missingParamNames = unassignedParams.map((p) => `"${p}"`).join(', '); if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, unassignedParams.length === 1 ? LocMessage.argMissingForParam().format({ name: missingParamNames }) : LocMessage.argMissingForParams().format({ names: missingParamNames }), @@ -10882,7 +10882,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions ) { if (!isDiagnosticSuppressedForNode(errorNode) && !isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.typeVarTupleMustBeUnpacked(), argParam.argument.valueExpression ?? errorNode ); @@ -11365,7 +11365,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (!sawParamSpecArgs || !sawParamSpecKwargs) { if (!isTypeIncomplete) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.paramSpecArgsMissing().format({ type: printType(type.details.paramSpec) }), errorNode ); @@ -11760,7 +11760,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (argumentErrors) { addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.paramSpecArgsMissing().format({ type: printType(functionType.details.paramSpec), }), @@ -12646,7 +12646,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions let className = ''; if (argList.length !== 2) { - addDiagnostic(DiagnosticRule.reportGeneralTypeIssues, LocMessage.newTypeParamCount(), errorNode); + addDiagnostic(DiagnosticRule.reportCallIssue, LocMessage.newTypeParamCount(), errorNode); return undefined; } diff --git a/packages/pyright-internal/src/analyzer/typedDicts.ts b/packages/pyright-internal/src/analyzer/typedDicts.ts index b84b0d928..a9cbb25e8 100644 --- a/packages/pyright-internal/src/analyzer/typedDicts.ts +++ b/packages/pyright-internal/src/analyzer/typedDicts.ts @@ -79,7 +79,7 @@ export function createTypedDictType( // Point2D = TypedDict('Point2D', x=int, y=int, label=str) let className: string | undefined; if (argList.length === 0) { - evaluator.addDiagnostic(DiagnosticRule.reportGeneralTypeIssues, LocMessage.typedDictFirstArg(), errorNode); + evaluator.addDiagnostic(DiagnosticRule.reportCallIssue, LocMessage.typedDictFirstArg(), errorNode); } else { const nameArg = argList[0]; if ( @@ -119,7 +119,7 @@ export function createTypedDictType( let usingDictSyntax = false; if (argList.length < 2) { - evaluator.addDiagnostic(DiagnosticRule.reportGeneralTypeIssues, LocMessage.typedDictSecondArgDict(), errorNode); + evaluator.addDiagnostic(DiagnosticRule.reportCallIssue, LocMessage.typedDictSecondArgDict(), errorNode); } else { const entriesArg = argList[1]; @@ -200,7 +200,7 @@ export function createTypedDictType( } } else { evaluator.addDiagnostic( - DiagnosticRule.reportGeneralTypeIssues, + DiagnosticRule.reportCallIssue, LocMessage.typedDictExtraArgs(), arg.valueExpression || errorNode ); diff --git a/packages/pyright-internal/src/common/configOptions.ts b/packages/pyright-internal/src/common/configOptions.ts index 9056e4e22..2ef9adbf2 100644 --- a/packages/pyright-internal/src/common/configOptions.ts +++ b/packages/pyright-internal/src/common/configOptions.ts @@ -168,6 +168,9 @@ export interface DiagnosticRuleSet { // Report issues related to attribute access expressions? reportAttributeAccessIssue: DiagnosticLevel; + // Report issues related to call expressions? + reportCallIssue: DiagnosticLevel; + // Report inconsistencies with function overload signatures? reportInconsistentOverload: DiagnosticLevel; @@ -411,6 +414,7 @@ export function getDiagLevelDiagnosticRules() { DiagnosticRule.reportAbstractUsage, DiagnosticRule.reportAssertTypeFailure, DiagnosticRule.reportAttributeAccessIssue, + DiagnosticRule.reportCallIssue, DiagnosticRule.reportInconsistentOverload, DiagnosticRule.reportIndexIssue, DiagnosticRule.reportInvalidTypeArguments, @@ -512,6 +516,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet { reportAbstractUsage: 'none', reportAssertTypeFailure: 'none', reportAttributeAccessIssue: 'none', + reportCallIssue: 'none', reportInconsistentOverload: 'none', reportIndexIssue: 'none', reportInvalidTypeArguments: 'none', @@ -609,6 +614,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet { reportAbstractUsage: 'error', reportAssertTypeFailure: 'error', reportAttributeAccessIssue: 'error', + reportCallIssue: 'error', reportInconsistentOverload: 'error', reportIndexIssue: 'error', reportInvalidTypeArguments: 'error', @@ -706,6 +712,7 @@ export function getStandardDiagnosticRuleSet(): DiagnosticRuleSet { reportAbstractUsage: 'error', reportAssertTypeFailure: 'error', reportAttributeAccessIssue: 'error', + reportCallIssue: 'error', reportInconsistentOverload: 'error', reportIndexIssue: 'error', reportInvalidTypeArguments: 'error', @@ -803,6 +810,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet { reportAbstractUsage: 'error', reportAssertTypeFailure: 'error', reportAttributeAccessIssue: 'error', + reportCallIssue: 'error', reportInconsistentOverload: 'error', reportIndexIssue: 'error', reportInvalidTypeArguments: 'error', diff --git a/packages/pyright-internal/src/common/diagnosticRules.ts b/packages/pyright-internal/src/common/diagnosticRules.ts index c6fd9598a..ab669fb5f 100644 --- a/packages/pyright-internal/src/common/diagnosticRules.ts +++ b/packages/pyright-internal/src/common/diagnosticRules.ts @@ -38,6 +38,7 @@ export enum DiagnosticRule { reportAbstractUsage = 'reportAbstractUsage', reportAssertTypeFailure = 'reportAssertTypeFailure', reportAttributeAccessIssue = 'reportAttributeAccessIssue', + reportCallIssue = 'reportCallIssue', reportInconsistentOverload = 'reportInconsistentOverload', reportIndexIssue = 'reportIndexIssue', reportInvalidTypeArguments = 'reportInvalidTypeArguments', diff --git a/packages/vscode-pyright/package.json b/packages/vscode-pyright/package.json index b101789b6..3e35d42e2 100644 --- a/packages/vscode-pyright/package.json +++ b/packages/vscode-pyright/package.json @@ -463,6 +463,22 @@ false ] }, + "reportCallIssue": { + "type": [ + "string", + "boolean" + ], + "description": "Diagnostics for issues involving call expressions and arguments.", + "default": "error", + "enum": [ + "none", + "information", + "warning", + "error", + true, + false + ] + }, "reportInconsistentOverload": { "type": [ "string", diff --git a/packages/vscode-pyright/schemas/pyrightconfig.schema.json b/packages/vscode-pyright/schemas/pyrightconfig.schema.json index caeb550e2..ed989bb7a 100644 --- a/packages/vscode-pyright/schemas/pyrightconfig.schema.json +++ b/packages/vscode-pyright/schemas/pyrightconfig.schema.json @@ -269,6 +269,12 @@ "title": "Controls reporting of issues related to attribute accesses", "default": "error" }, + "reportCallIssue": { + "$id": "#/properties/reportCallIssue", + "$ref": "#/definitions/diagnostic", + "title": "Controls reporting of issues related to call expressions and arguments", + "default": "error" + }, "reportInconsistentOverload": { "$id": "#/properties/reportInconsistentOverload", "$ref": "#/definitions/diagnostic",