mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 07:55:56 +03:00
Added provisional support for draft PEP 702 (marking deprecations using the type system).
This commit is contained in:
parent
761fabc9c3
commit
53e8cd4145
@ -35,6 +35,7 @@ Pyright supports [configuration files](/docs/configuration.md) that provide gran
|
||||
* [PEP 695](https://www.python.org/dev/peps/pep-0695/) (draft) Type parameter syntax
|
||||
* [PEP 696](https://www.python.org/dev/peps/pep-0696/) (draft) Type defaults for TypeVarLikes
|
||||
* [PEP 698](https://www.python.org/dev/peps/pep-0698/) (draft) Override decorator for static typing
|
||||
* [PEP 702](https://www.python.org/dev/peps/pep-0702/) (draft) Marking deprecations
|
||||
* Type inference for function return values, instance variables, class variables, and globals
|
||||
* Type guards that understand conditional code flow constructs like if/else statements
|
||||
|
||||
|
@ -112,6 +112,8 @@ The following settings control pyright’s diagnostic output (warnings or errors
|
||||
|
||||
<a name="reportConstantRedefinition"></a> **reportConstantRedefinition** [boolean or string, optional]: Generate or suppress diagnostics for attempts to redefine variables whose names are all-caps with underscores and numerals. The default value for this setting is 'none'.
|
||||
|
||||
<a name="reportDeprecated"></a> **reportDeprecated** [boolean or string, optional]: Generate or suppress diagnostics for use of a class or function that has been marked as deprecated. The default value for this setting is 'none'.
|
||||
|
||||
<a name="reportIncompatibleMethodOverride"></a> **reportIncompatibleMethodOverride** [boolean or string, optional]: Generate or suppress diagnostics for methods that override a method of the same name in a base class in an incompatible manner (wrong number of parameters, incompatible parameter types, or incompatible return type). The default value for this setting is 'none'.
|
||||
|
||||
<a name="reportIncompatibleVariableOverride"></a> **reportIncompatibleVariableOverride** [boolean or string, optional]: Generate or suppress diagnostics for class variable declarations that override a symbol of the same name in a base class with a type that is incompatible with the base class symbol type. The default value for this setting is 'none'.
|
||||
@ -321,6 +323,7 @@ The following table lists the default severity levels for each diagnostic rule w
|
||||
| reportUnboundVariable | "none" | "error" | "error" |
|
||||
| reportUnusedCoroutine | "none" | "error" | "error" |
|
||||
| reportConstantRedefinition | "none" | "none" | "error" |
|
||||
| reportDeprecated | "none" | "none" | "error" |
|
||||
| reportDuplicateImport | "none" | "none" | "error" |
|
||||
| reportFunctionMemberAccess | "none" | "none" | "error" |
|
||||
| reportImportCycles | "none" | "none" | "error" |
|
||||
|
@ -1374,9 +1374,9 @@ export class Checker extends ParseTreeWalker {
|
||||
this._reportUnboundName(node);
|
||||
}
|
||||
|
||||
// Report the use of a deprecated symbol. For now, this functionality
|
||||
// is disabled. We'll leave it in place for the future.
|
||||
// this._reportDeprecatedUse(node);
|
||||
// Report the use of a deprecated symbol.
|
||||
const type = this._evaluator.getType(node);
|
||||
this._reportDeprecatedUse(node, type);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1392,7 +1392,9 @@ export class Checker extends ParseTreeWalker {
|
||||
}
|
||||
|
||||
override visitMemberAccess(node: MemberAccessNode) {
|
||||
this._evaluator.getType(node);
|
||||
const type = this._evaluator.getType(node);
|
||||
this._reportDeprecatedUse(node.memberName, type);
|
||||
|
||||
this._conditionallyReportPrivateUsage(node.memberName);
|
||||
|
||||
// Walk the leftExpression but not the memberName.
|
||||
@ -1475,6 +1477,9 @@ export class Checker extends ParseTreeWalker {
|
||||
break;
|
||||
}
|
||||
|
||||
const type = this._evaluator.getType(node.alias ?? node.name);
|
||||
this._reportDeprecatedUse(node.name, type);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3562,31 +3567,109 @@ export class Checker extends ParseTreeWalker {
|
||||
return false;
|
||||
}
|
||||
|
||||
private _reportDeprecatedUse(node: NameNode) {
|
||||
const deprecatedForm = deprecatedAliases.get(node.value) ?? deprecatedSpecialForms.get(node.value);
|
||||
|
||||
if (!deprecatedForm) {
|
||||
return;
|
||||
}
|
||||
|
||||
const type = this._evaluator.getType(node);
|
||||
|
||||
private _reportDeprecatedUse(node: NameNode, type: Type | undefined) {
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isInstantiableClass(type) || type.details.fullName !== deprecatedForm.fullName) {
|
||||
return;
|
||||
let errorMessage: string | undefined;
|
||||
let deprecatedMessage: string | undefined;
|
||||
|
||||
doForEachSubtype(type, (subtype) => {
|
||||
if (isClass(subtype)) {
|
||||
if (
|
||||
!subtype.includeSubclasses &&
|
||||
subtype.details.deprecatedMessage !== undefined &&
|
||||
node.value === subtype.details.name
|
||||
) {
|
||||
deprecatedMessage = subtype.details.deprecatedMessage;
|
||||
errorMessage = Localizer.Diagnostic.deprecatedClass();
|
||||
}
|
||||
} else if (isFunction(subtype)) {
|
||||
if (subtype.details.deprecatedMessage !== undefined && node.value === subtype.details.name) {
|
||||
deprecatedMessage = subtype.details.deprecatedMessage;
|
||||
errorMessage = Localizer.Diagnostic.deprecatedFunction();
|
||||
}
|
||||
} else if (isOverloadedFunction(subtype)) {
|
||||
// Determine if the node is part of a call expression. If so,
|
||||
// we can determine which overload(s) were used to satisfy
|
||||
// the call expression and determine whether any of them
|
||||
// are deprecated.
|
||||
let callTypeResult: TypeResult | undefined;
|
||||
if (node.parent?.nodeType === ParseNodeType.Call && node.parent.leftExpression === node) {
|
||||
callTypeResult = this._evaluator.getTypeResult(node.parent);
|
||||
} else if (
|
||||
node.parent?.nodeType === ParseNodeType.MemberAccess &&
|
||||
node.parent.memberName === node &&
|
||||
node.parent.parent?.nodeType === ParseNodeType.Call &&
|
||||
node.parent.parent.leftExpression === node.parent
|
||||
) {
|
||||
callTypeResult = this._evaluator.getTypeResult(node.parent.parent);
|
||||
}
|
||||
|
||||
if (
|
||||
callTypeResult &&
|
||||
callTypeResult.overloadsUsedForCall &&
|
||||
callTypeResult.overloadsUsedForCall.length > 0
|
||||
) {
|
||||
callTypeResult.overloadsUsedForCall.forEach((overload) => {
|
||||
if (overload.details.deprecatedMessage !== undefined && node.value === overload.details.name) {
|
||||
deprecatedMessage = overload.details.deprecatedMessage;
|
||||
errorMessage = Localizer.Diagnostic.deprecatedFunction();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (errorMessage) {
|
||||
const diag = new DiagnosticAddendum();
|
||||
if (deprecatedMessage) {
|
||||
diag.addMessage(deprecatedMessage);
|
||||
}
|
||||
|
||||
if (this._fileInfo.diagnosticRuleSet.reportDeprecated === 'none') {
|
||||
this._evaluator.addDeprecated(errorMessage + diag.getString(), node);
|
||||
} else {
|
||||
this._evaluator.addDiagnostic(
|
||||
this._fileInfo.diagnosticRuleSet.reportDeprecated,
|
||||
DiagnosticRule.reportDeprecated,
|
||||
errorMessage + diag.getString(),
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._fileInfo.executionEnvironment.pythonVersion >= deprecatedForm.version) {
|
||||
this._evaluator.addDeprecated(
|
||||
Localizer.Diagnostic.deprecatedType().format({
|
||||
version: versionToString(deprecatedForm.version),
|
||||
replacement: deprecatedForm.replacementText,
|
||||
}),
|
||||
node
|
||||
);
|
||||
// We'll leave this disabled for now because this would be too noisy for most
|
||||
// code bases. We may want to add it at some future date.
|
||||
if (0) {
|
||||
const deprecatedForm = deprecatedAliases.get(node.value) ?? deprecatedSpecialForms.get(node.value);
|
||||
|
||||
if (deprecatedForm) {
|
||||
if (isInstantiableClass(type) && type.details.fullName === deprecatedForm.fullName) {
|
||||
if (this._fileInfo.executionEnvironment.pythonVersion >= deprecatedForm.version) {
|
||||
if (this._fileInfo.diagnosticRuleSet.reportDeprecated === 'none') {
|
||||
this._evaluator.addDeprecated(
|
||||
Localizer.Diagnostic.deprecatedType().format({
|
||||
version: versionToString(deprecatedForm.version),
|
||||
replacement: deprecatedForm.replacementText,
|
||||
}),
|
||||
node
|
||||
);
|
||||
} else {
|
||||
this._evaluator.addDiagnostic(
|
||||
this._fileInfo.diagnosticRuleSet.reportDeprecated,
|
||||
DiagnosticRule.reportDeprecated,
|
||||
Localizer.Diagnostic.deprecatedType().format({
|
||||
version: versionToString(deprecatedForm.version),
|
||||
replacement: deprecatedForm.replacementText,
|
||||
}),
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7287,6 +7287,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
|
||||
if (callResult.argumentErrors) {
|
||||
typeResult.typeErrors = true;
|
||||
} else {
|
||||
typeResult.overloadsUsedForCall = callResult.overloadsUsedForCall;
|
||||
}
|
||||
|
||||
if (callResult.isTypeIncomplete) {
|
||||
@ -7698,6 +7700,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
typeVarContext: TypeVarContext;
|
||||
}[] = [];
|
||||
let isTypeIncomplete = false;
|
||||
const overloadsUsedForCall: FunctionType[] = [];
|
||||
|
||||
for (let expandedTypesIndex = 0; expandedTypesIndex < expandedArgTypes.length; expandedTypesIndex++) {
|
||||
let matchedOverload: FunctionType | undefined;
|
||||
@ -7751,6 +7754,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
}
|
||||
|
||||
if (!callResult.argumentErrors && callResult.returnType) {
|
||||
overloadsUsedForCall.push(overload);
|
||||
|
||||
matchedOverload = overload;
|
||||
matchedOverloads.push({
|
||||
overload: matchedOverload,
|
||||
@ -7804,7 +7809,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
}
|
||||
|
||||
if (!matchedOverload) {
|
||||
return { argumentErrors: true, isTypeIncomplete };
|
||||
return { argumentErrors: true, isTypeIncomplete, overloadsUsedForCall };
|
||||
}
|
||||
}
|
||||
|
||||
@ -7839,6 +7844,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
returnType: combineTypes(returnTypes),
|
||||
isTypeIncomplete,
|
||||
specializedInitSelfType: finalCallResult.specializedInitSelfType,
|
||||
overloadsUsedForCall,
|
||||
};
|
||||
}
|
||||
|
||||
@ -7952,7 +7958,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
);
|
||||
}
|
||||
|
||||
return { argumentErrors: true, isTypeIncomplete: false };
|
||||
return { argumentErrors: true, isTypeIncomplete: false, overloadsUsedForCall: [] };
|
||||
}
|
||||
|
||||
// Create a helper lambda that evaluates the overload that matches
|
||||
@ -8049,7 +8055,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
return { ...result, argumentErrors: true };
|
||||
}
|
||||
|
||||
return { argumentErrors: true, isTypeIncomplete: false };
|
||||
return { argumentErrors: true, isTypeIncomplete: false, overloadsUsedForCall: [] };
|
||||
}
|
||||
|
||||
// Replaces each item in the expandedArgTypes with n items where n is
|
||||
@ -8124,6 +8130,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
let reportedErrors = false;
|
||||
let isTypeIncomplete = false;
|
||||
let usedMetaclassCallMethod = false;
|
||||
const overloadsUsedForCall: FunctionType[] = [];
|
||||
|
||||
// Create a helper function that determines whether we should skip argument
|
||||
// validation for either __init__ or __new__. This is required for certain
|
||||
@ -8166,6 +8173,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
if (expectedCallResult.isTypeIncomplete) {
|
||||
isTypeIncomplete = true;
|
||||
}
|
||||
|
||||
overloadsUsedForCall.push(...expectedCallResult.overloadsUsedForCall);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8202,6 +8211,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
if (callResult.isTypeIncomplete) {
|
||||
isTypeIncomplete = true;
|
||||
}
|
||||
|
||||
overloadsUsedForCall.push(...callResult.overloadsUsedForCall);
|
||||
} else {
|
||||
reportedErrors = true;
|
||||
}
|
||||
@ -8425,7 +8436,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
}
|
||||
}
|
||||
|
||||
const result: CallResult = { argumentErrors: reportedErrors, returnType, isTypeIncomplete };
|
||||
const result: CallResult = {
|
||||
argumentErrors: reportedErrors,
|
||||
returnType,
|
||||
isTypeIncomplete,
|
||||
overloadsUsedForCall,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -8443,6 +8459,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
): CallResult | undefined {
|
||||
let isTypeIncomplete = false;
|
||||
let argumentErrors = false;
|
||||
const overloadsUsedForCall: FunctionType[] = [];
|
||||
|
||||
const returnType = mapSubtypes(expectedType, (expectedSubType) => {
|
||||
expectedSubType = transformPossibleRecursiveTypeAlias(expectedSubType);
|
||||
@ -8486,6 +8503,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
argumentErrors = true;
|
||||
}
|
||||
|
||||
overloadsUsedForCall.push(...callResult.overloadsUsedForCall);
|
||||
|
||||
return applyExpectedSubtypeForConstructor(type, expectedSubType, typeVarContext);
|
||||
}
|
||||
}
|
||||
@ -8497,7 +8516,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return { returnType, isTypeIncomplete, argumentErrors };
|
||||
return { returnType, isTypeIncomplete, argumentErrors, overloadsUsedForCall };
|
||||
}
|
||||
|
||||
function applyExpectedSubtypeForConstructor(
|
||||
@ -8584,9 +8603,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
let argumentErrors = false;
|
||||
let isTypeIncomplete = false;
|
||||
let specializedInitSelfType: Type | undefined;
|
||||
const overloadsUsedForCall: FunctionType[] = [];
|
||||
|
||||
if (recursionCount > maxTypeRecursionCount) {
|
||||
return { returnType: UnknownType.create(), argumentErrors: true };
|
||||
return { returnType: UnknownType.create(), argumentErrors: true, overloadsUsedForCall };
|
||||
}
|
||||
recursionCount++;
|
||||
|
||||
@ -8601,7 +8621,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
}),
|
||||
exprNode
|
||||
);
|
||||
return { returnType: UnknownType.create(), argumentErrors: true };
|
||||
return { returnType: UnknownType.create(), argumentErrors: true, overloadsUsedForCall };
|
||||
}
|
||||
|
||||
const returnType = mapSubtypesExpandTypeVars(
|
||||
@ -8675,6 +8695,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
isTypeIncomplete = true;
|
||||
}
|
||||
|
||||
overloadsUsedForCall.push(...functionResult.overloadsUsedForCall);
|
||||
|
||||
if (functionResult.argumentErrors) {
|
||||
argumentErrors = true;
|
||||
} else {
|
||||
@ -8747,6 +8769,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
expectedType
|
||||
);
|
||||
|
||||
overloadsUsedForCall.push(...functionResult.overloadsUsedForCall);
|
||||
|
||||
if (functionResult.isTypeIncomplete) {
|
||||
isTypeIncomplete = true;
|
||||
}
|
||||
@ -8953,6 +8977,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
expectedType
|
||||
);
|
||||
|
||||
overloadsUsedForCall.push(...constructorResult.overloadsUsedForCall);
|
||||
|
||||
if (constructorResult.argumentErrors) {
|
||||
argumentErrors = true;
|
||||
}
|
||||
@ -9023,6 +9049,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
expectedType,
|
||||
recursionCount
|
||||
);
|
||||
|
||||
overloadsUsedForCall.push(...functionResult.overloadsUsedForCall);
|
||||
|
||||
if (functionResult.argumentErrors) {
|
||||
argumentErrors = true;
|
||||
}
|
||||
@ -9082,6 +9111,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
recursionCount
|
||||
);
|
||||
|
||||
overloadsUsedForCall.push(...callResult.overloadsUsedForCall);
|
||||
|
||||
if (callResult.argumentErrors) {
|
||||
argumentErrors = true;
|
||||
}
|
||||
@ -9109,6 +9140,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
returnType: isNever(returnType) && !returnType.isNoReturn ? undefined : returnType,
|
||||
isTypeIncomplete,
|
||||
specializedInitSelfType,
|
||||
overloadsUsedForCall,
|
||||
};
|
||||
}
|
||||
|
||||
@ -10507,6 +10539,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
isTypeIncomplete,
|
||||
activeParam: matchResults.activeParam,
|
||||
specializedInitSelfType,
|
||||
overloadsUsedForCall: argumentErrors ? [] : [type],
|
||||
};
|
||||
}
|
||||
|
||||
@ -10551,6 +10584,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
return {
|
||||
argumentErrors: true,
|
||||
activeParam: matchResults.activeParam,
|
||||
overloadsUsedForCall: [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -16176,6 +16210,16 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isOverloadedFunction(decoratorCallType)) {
|
||||
if (
|
||||
decoratorCallType.overloads.length > 0 &&
|
||||
decoratorCallType.overloads[0].details.builtInName === 'deprecated'
|
||||
) {
|
||||
originalClassType.details.deprecatedMessage = getCustomDeprecationMessage(decoratorNode);
|
||||
return inputClassType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isOverloadedFunction(decoratorType)) {
|
||||
@ -16190,6 +16234,11 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
);
|
||||
return inputClassType;
|
||||
}
|
||||
|
||||
if (decoratorType.overloads.length > 0 && decoratorType.overloads[0].details.builtInName === 'deprecated') {
|
||||
originalClassType.details.deprecatedMessage = getCustomDeprecationMessage(decoratorNode);
|
||||
return inputClassType;
|
||||
}
|
||||
} else if (isFunction(decoratorType)) {
|
||||
if (decoratorType.details.builtInName === 'final') {
|
||||
originalClassType.details.flags |= ClassTypeFlags.Final;
|
||||
@ -16198,7 +16247,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
// behavior because its function definition results in a cyclical
|
||||
// dependency between builtins, typing and _typeshed stubs.
|
||||
return inputClassType;
|
||||
} else if (decoratorType.details.builtInName === 'runtime_checkable') {
|
||||
}
|
||||
|
||||
if (decoratorType.details.builtInName === 'runtime_checkable') {
|
||||
originalClassType.details.flags |= ClassTypeFlags.RuntimeCheckable;
|
||||
|
||||
// Don't call getTypeOfDecorator for runtime_checkable. It appears
|
||||
@ -16238,6 +16289,22 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
return getTypeOfDecorator(decoratorNode, inputClassType);
|
||||
}
|
||||
|
||||
// Given a @typing.deprecated decorator node, returns either '' or a custom
|
||||
// deprecation message if one is provided.
|
||||
function getCustomDeprecationMessage(decorator: DecoratorNode): string {
|
||||
if (
|
||||
decorator.expression.nodeType === ParseNodeType.Call &&
|
||||
decorator.expression.arguments.length > 0 &&
|
||||
decorator.expression.arguments[0].argumentCategory === ArgumentCategory.Simple &&
|
||||
decorator.expression.arguments[0].valueExpression.nodeType === ParseNodeType.StringList &&
|
||||
decorator.expression.arguments[0].valueExpression.strings.length === 1
|
||||
) {
|
||||
return decorator.expression.arguments[0].valueExpression.strings[0].value;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
// Runs any registered "callback hooks" that depend on the specified class type.
|
||||
// This allows us to complete any work that requires dependent classes to be
|
||||
// completed.
|
||||
@ -17203,6 +17270,16 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
return inputFunctionType;
|
||||
}
|
||||
}
|
||||
|
||||
if (isOverloadedFunction(decoratorCallType)) {
|
||||
if (
|
||||
decoratorCallType.overloads.length > 0 &&
|
||||
decoratorCallType.overloads[0].details.builtInName === 'deprecated'
|
||||
) {
|
||||
undecoratedType.details.deprecatedMessage = getCustomDeprecationMessage(decoratorNode);
|
||||
return inputFunctionType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let returnType = getTypeOfDecorator(decoratorNode, inputFunctionType);
|
||||
@ -17249,6 +17326,11 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isOverloadedFunction(decoratorType)) {
|
||||
if (decoratorType.overloads.length > 0 && decoratorType.overloads[0].details.builtInName === 'deprecated') {
|
||||
undecoratedType.details.deprecatedMessage = getCustomDeprecationMessage(decoratorNode);
|
||||
return inputFunctionType;
|
||||
}
|
||||
} else if (isInstantiableClass(decoratorType)) {
|
||||
if (ClassType.isBuiltIn(decoratorType)) {
|
||||
switch (decoratorType.details.name) {
|
||||
@ -18695,17 +18777,13 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
|
||||
// don't bother doing additional work.
|
||||
let cacheEntry = readTypeCacheEntry(subnode);
|
||||
if (cacheEntry && !cacheEntry.typeResult.isIncomplete) {
|
||||
return { type: cacheEntry.typeResult.type };
|
||||
return cacheEntry.typeResult;
|
||||
}
|
||||
|
||||
callback();
|
||||
cacheEntry = readTypeCacheEntry(subnode);
|
||||
if (cacheEntry) {
|
||||
return {
|
||||
type: cacheEntry.typeResult.type,
|
||||
isIncomplete: cacheEntry.typeResult.isIncomplete,
|
||||
expectedTypeDiagAddendum: cacheEntry.typeResult.expectedTypeDiagAddendum,
|
||||
};
|
||||
return cacheEntry.typeResult;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
@ -182,6 +182,9 @@ export interface TypeResult {
|
||||
// Is the type wrapped in a "Required" or "NotRequired" class?
|
||||
isRequired?: boolean;
|
||||
isNotRequired?: boolean;
|
||||
|
||||
// If a call expression, which overloads were used to satisfy it?
|
||||
overloadsUsedForCall?: FunctionType[];
|
||||
}
|
||||
|
||||
export interface TypeResultWithNode extends TypeResult {
|
||||
@ -319,6 +322,11 @@ export interface CallResult {
|
||||
// is used for overloaded constructors where the arguments to the
|
||||
// constructor influence the specialized type of the constructed object.
|
||||
specializedInitSelfType?: Type | undefined;
|
||||
|
||||
// The overload or overloads used to satisfy the call. There can
|
||||
// be multiple overloads in the case where the call type is a union
|
||||
// or we have used union expansion for arguments.
|
||||
overloadsUsedForCall: FunctionType[];
|
||||
}
|
||||
|
||||
export interface PrintTypeOptions {
|
||||
|
@ -497,6 +497,7 @@ interface ClassDetails {
|
||||
typeParameters: TypeVarType[];
|
||||
typeVarScopeId?: TypeVarScopeId | undefined;
|
||||
docString?: string | undefined;
|
||||
deprecatedMessage?: string | undefined;
|
||||
dataClassEntries?: DataClassEntry[] | undefined;
|
||||
dataClassBehaviors?: DataClassBehaviors | undefined;
|
||||
typedDictEntries?: Map<string, TypedDictEntry> | undefined;
|
||||
@ -1212,6 +1213,7 @@ interface FunctionDetails {
|
||||
constructorTypeVarScopeId?: TypeVarScopeId | undefined;
|
||||
builtInName?: string | undefined;
|
||||
docString?: string | undefined;
|
||||
deprecatedMessage?: string | undefined;
|
||||
|
||||
// Transforms to apply if this function is used
|
||||
// as a decorator.
|
||||
|
@ -187,6 +187,9 @@ export interface DiagnosticRuleSet {
|
||||
// Report attempts to redefine variables that are in all-caps.
|
||||
reportConstantRedefinition: DiagnosticLevel;
|
||||
|
||||
// Report use of deprecated classes or functions.
|
||||
reportDeprecated: DiagnosticLevel;
|
||||
|
||||
// Report usage of method override that is incompatible with
|
||||
// the base class method of the same name?
|
||||
reportIncompatibleMethodOverride: DiagnosticLevel;
|
||||
@ -361,6 +364,7 @@ export function getDiagLevelDiagnosticRules() {
|
||||
DiagnosticRule.reportTypeCommentUsage,
|
||||
DiagnosticRule.reportPrivateImportUsage,
|
||||
DiagnosticRule.reportConstantRedefinition,
|
||||
DiagnosticRule.reportDeprecated,
|
||||
DiagnosticRule.reportIncompatibleMethodOverride,
|
||||
DiagnosticRule.reportIncompatibleVariableOverride,
|
||||
DiagnosticRule.reportInconsistentConstructor,
|
||||
@ -445,6 +449,7 @@ export function getOffDiagnosticRuleSet(): DiagnosticRuleSet {
|
||||
reportTypeCommentUsage: 'none',
|
||||
reportPrivateImportUsage: 'none',
|
||||
reportConstantRedefinition: 'none',
|
||||
reportDeprecated: 'none',
|
||||
reportIncompatibleMethodOverride: 'none',
|
||||
reportIncompatibleVariableOverride: 'none',
|
||||
reportInconsistentConstructor: 'none',
|
||||
@ -525,6 +530,7 @@ export function getBasicDiagnosticRuleSet(): DiagnosticRuleSet {
|
||||
reportTypeCommentUsage: 'none',
|
||||
reportPrivateImportUsage: 'error',
|
||||
reportConstantRedefinition: 'none',
|
||||
reportDeprecated: 'none',
|
||||
reportIncompatibleMethodOverride: 'none',
|
||||
reportIncompatibleVariableOverride: 'none',
|
||||
reportInconsistentConstructor: 'none',
|
||||
@ -605,6 +611,7 @@ export function getStrictDiagnosticRuleSet(): DiagnosticRuleSet {
|
||||
reportTypeCommentUsage: 'error',
|
||||
reportPrivateImportUsage: 'error',
|
||||
reportConstantRedefinition: 'error',
|
||||
reportDeprecated: 'error',
|
||||
reportIncompatibleMethodOverride: 'error',
|
||||
reportIncompatibleVariableOverride: 'error',
|
||||
reportInconsistentConstructor: 'error',
|
||||
|
@ -46,6 +46,7 @@ export enum DiagnosticRule {
|
||||
reportTypeCommentUsage = 'reportTypeCommentUsage',
|
||||
reportPrivateImportUsage = 'reportPrivateImportUsage',
|
||||
reportConstantRedefinition = 'reportConstantRedefinition',
|
||||
reportDeprecated = 'reportDeprecated',
|
||||
reportIncompatibleMethodOverride = 'reportIncompatibleMethodOverride',
|
||||
reportIncompatibleVariableOverride = 'reportIncompatibleVariableOverride',
|
||||
reportInconsistentConstructor = 'reportInconsistentConstructor',
|
||||
|
@ -329,6 +329,8 @@ export namespace Localizer {
|
||||
export const declaredReturnTypeUnknown = () => getRawString('Diagnostic.declaredReturnTypeUnknown');
|
||||
export const defaultValueContainsCall = () => getRawString('Diagnostic.defaultValueContainsCall');
|
||||
export const defaultValueNotAllowed = () => getRawString('Diagnostic.defaultValueNotAllowed');
|
||||
export const deprecatedClass = () => getRawString('Diagnostic.deprecatedClass');
|
||||
export const deprecatedFunction = () => getRawString('Diagnostic.deprecatedFunction');
|
||||
export const deprecatedType = () =>
|
||||
new ParameterizedString<{ version: string; replacement: string }>(
|
||||
getRawString('Diagnostic.deprecatedType')
|
||||
|
@ -89,6 +89,8 @@
|
||||
"declaredReturnTypeUnknown": "Declared return type is unknown",
|
||||
"defaultValueContainsCall": "Function calls and mutable objects not allowed within parameter default value expression",
|
||||
"defaultValueNotAllowed": "Parameter with \"*\" or \"**\" cannot have default value",
|
||||
"deprecatedClass": "This class is deprecated",
|
||||
"deprecatedFunction": "This function is deprecated",
|
||||
"deprecatedType": "This type is deprecated as of Python {version}; use \"{replacement}\" instead",
|
||||
"delTargetExpr": "Expression cannot be deleted",
|
||||
"dictExpandIllegalInComprehension": "Dictionary expansion not allowed in comprehension",
|
||||
|
@ -492,3 +492,25 @@ test('RegionComments1', () => {
|
||||
// const analysisResults3 = TestUtils.typeAnalyzeSampleFiles(['deprecated1.py'], configOptions);
|
||||
// TestUtils.validateResults(analysisResults3, 0, 0, 0, 0, 13);
|
||||
// });
|
||||
|
||||
test('Deprecated2', () => {
|
||||
const configOptions = new ConfigOptions('.');
|
||||
|
||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated2.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 5);
|
||||
|
||||
configOptions.diagnosticRuleSet.reportDeprecated = 'error';
|
||||
const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['deprecated2.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults2, 5);
|
||||
});
|
||||
|
||||
test('Deprecated3', () => {
|
||||
const configOptions = new ConfigOptions('.');
|
||||
|
||||
const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['deprecated3.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults1, 0, 0, 0, undefined, undefined, 5);
|
||||
|
||||
configOptions.diagnosticRuleSet.reportDeprecated = 'error';
|
||||
const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['deprecated3.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults2, 5);
|
||||
});
|
||||
|
71
packages/pyright-internal/src/tests/samples/deprecated2.py
Normal file
71
packages/pyright-internal/src/tests/samples/deprecated2.py
Normal file
@ -0,0 +1,71 @@
|
||||
# This sample tests the @typing.deprecated decorator introduced in PEP 702.
|
||||
|
||||
from typing_extensions import deprecated, overload
|
||||
|
||||
|
||||
@deprecated("Use ClassB instead")
|
||||
class ClassA:
|
||||
...
|
||||
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
ClassA()
|
||||
|
||||
|
||||
class ClassC:
|
||||
@deprecated("Don't temp me")
|
||||
def method1(self) -> None:
|
||||
...
|
||||
|
||||
@overload
|
||||
@deprecated("Int is no longer supported")
|
||||
def method2(self, a: int) -> None:
|
||||
...
|
||||
|
||||
@overload
|
||||
def method2(self, a: None = None) -> None:
|
||||
...
|
||||
|
||||
def method2(self, a: int | None = None) -> None:
|
||||
...
|
||||
|
||||
|
||||
c1 = ClassC()
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
c1.method1()
|
||||
|
||||
c1.method2()
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
c1.method2(2)
|
||||
|
||||
|
||||
@deprecated("Test")
|
||||
def func1() -> None:
|
||||
...
|
||||
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
func1()
|
||||
|
||||
|
||||
@overload
|
||||
def func2(a: str) -> None:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
@deprecated("int no longer supported")
|
||||
def func2(a: int) -> int:
|
||||
...
|
||||
|
||||
|
||||
def func2(a: str | int) -> int | None:
|
||||
...
|
||||
|
||||
|
||||
func2("hi")
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
func2(3)
|
25
packages/pyright-internal/src/tests/samples/deprecated3.py
Normal file
25
packages/pyright-internal/src/tests/samples/deprecated3.py
Normal file
@ -0,0 +1,25 @@
|
||||
# This sample tests the @typing.deprecated decorator introduced in PEP 702.
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
from .deprecated2 import func1
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
from .deprecated2 import ClassA as A
|
||||
|
||||
from .deprecated2 import func2
|
||||
from .deprecated2 import ClassC as C
|
||||
|
||||
func2("hi")
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
func2(1)
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
c1 = C.method1
|
||||
|
||||
|
||||
c2 = C()
|
||||
c2.method2()
|
||||
|
||||
# This should generate an error if reportDeprecated is enabled.
|
||||
c2.method2(3)
|
@ -313,3 +313,9 @@ def override(__arg: _F) -> _F: ...
|
||||
|
||||
# Proposed extension to PEP 647
|
||||
StrictTypeGuard: _SpecialForm = ...
|
||||
|
||||
# Proposed PEP 702
|
||||
@overload
|
||||
def deprecated(__f: _F) -> _F: ...
|
||||
@overload
|
||||
def deprecated(__msg: str) -> Callable[[_F], _F]: ...
|
||||
|
@ -36,6 +36,7 @@ Pyright supports [configuration files](/docs/configuration.md) that provide gran
|
||||
* [PEP 695](https://www.python.org/dev/peps/pep-0695/) (draft) Type parameter syntax
|
||||
* [PEP 696](https://www.python.org/dev/peps/pep-0696/) (draft) Type defaults for TypeVarLikes
|
||||
* [PEP 698](https://www.python.org/dev/peps/pep-0698/) (draft) Override decorator for static typing
|
||||
* [PEP 702](https://www.python.org/dev/peps/pep-0702/) (draft) Marking deprecations
|
||||
* Type inference for function return values, instance variables, class variables, and globals
|
||||
* Type guards that understand conditional code flow constructs like if/else statements
|
||||
|
||||
|
@ -473,6 +473,17 @@
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"reportDeprecated": {
|
||||
"type": "string",
|
||||
"description": "Diagnostics for use of deprecated classes or functions.",
|
||||
"default": "none",
|
||||
"enum": [
|
||||
"none",
|
||||
"information",
|
||||
"warning",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"reportIncompatibleMethodOverride": {
|
||||
"type": "string",
|
||||
"description": "Diagnostics for methods that override a method of the same name in a base class in an incompatible manner (wrong number of parameters, incompatible parameter types, or incompatible return type).",
|
||||
|
@ -316,6 +316,12 @@
|
||||
"title": "Controls reporting of attempts to redefine variables that are in all-caps",
|
||||
"default": "none"
|
||||
},
|
||||
"reportDeprecated": {
|
||||
"$id": "#/properties/reportDeprecated",
|
||||
"$ref": "#/definitions/diagnostic",
|
||||
"title": "Controls reporting of use of deprecated class or function",
|
||||
"default": "none"
|
||||
},
|
||||
"reportIncompatibleMethodOverride": {
|
||||
"$id": "#/properties/reportIncompatibleMethodOverride",
|
||||
"$ref": "#/definitions/diagnostic",
|
||||
|
Loading…
Reference in New Issue
Block a user