mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-05 12:27:30 +03:00
Fixed bug that results in false positive reportInconsistentOverload
and reportNoOverloadImplementation
errors when an overloaded decorator is applied to a non-overloaded function or method. This addresses #8246. (#8249)
This commit is contained in:
parent
1e1e912380
commit
f104e7bb30
@ -3092,106 +3092,123 @@ export class Checker extends ParseTreeWalker {
|
|||||||
|
|
||||||
private _reportInvalidOverload(name: string, symbol: Symbol) {
|
private _reportInvalidOverload(name: string, symbol: Symbol) {
|
||||||
const typedDecls = symbol.getTypedDeclarations();
|
const typedDecls = symbol.getTypedDeclarations();
|
||||||
if (typedDecls.length >= 1) {
|
if (typedDecls.length === 0) {
|
||||||
const primaryDecl = typedDecls[0];
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (primaryDecl.type === DeclarationType.Function) {
|
const primaryDecl = typedDecls[0];
|
||||||
const type = this._evaluator.getEffectiveTypeOfSymbol(symbol);
|
|
||||||
const overloadedFunctions = isOverloadedFunction(type)
|
|
||||||
? OverloadedFunctionType.getOverloads(type)
|
|
||||||
: isFunction(type) && FunctionType.isOverloaded(type)
|
|
||||||
? [type]
|
|
||||||
: [];
|
|
||||||
|
|
||||||
if (overloadedFunctions.length === 1) {
|
if (primaryDecl.type !== DeclarationType.Function) {
|
||||||
// There should never be a single overload.
|
return;
|
||||||
this._evaluator.addDiagnostic(
|
}
|
||||||
DiagnosticRule.reportInconsistentOverload,
|
|
||||||
LocMessage.singleOverload().format({ name }),
|
|
||||||
primaryDecl.node.name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the file is not a stub and this is the first overload,
|
const type = this._evaluator.getEffectiveTypeOfSymbol(symbol);
|
||||||
// verify that there is an implementation.
|
const overloadedFunctions = isOverloadedFunction(type)
|
||||||
if (!this._fileInfo.isStubFile && overloadedFunctions.length > 0) {
|
? OverloadedFunctionType.getOverloads(type)
|
||||||
let implementationFunction: FunctionType | undefined;
|
: isFunction(type) && FunctionType.isOverloaded(type)
|
||||||
let exemptMissingImplementation = false;
|
? [type]
|
||||||
|
: [];
|
||||||
|
|
||||||
if (isOverloadedFunction(type)) {
|
// If the implementation has no name, it was synthesized probably by a
|
||||||
implementationFunction = OverloadedFunctionType.getImplementation(type);
|
// decorator that used a callable with a ParamSpec that captured the
|
||||||
|
// overloaded signature. We'll exempt it from this check.
|
||||||
|
if (isOverloadedFunction(type)) {
|
||||||
|
const overloads = OverloadedFunctionType.getOverloads(type);
|
||||||
|
if (overloads.length > 0 && overloads[0].details.name === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (isFunction(type)) {
|
||||||
|
if (type.details.name === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the implementation has no name, it was synthesized probably by a
|
if (overloadedFunctions.length === 1) {
|
||||||
// decorator that used a callable with a ParamSpec that captured the
|
// There should never be a single overload.
|
||||||
// overloaded signature. We'll exempt it from this check.
|
this._evaluator.addDiagnostic(
|
||||||
const overloads = OverloadedFunctionType.getOverloads(type);
|
DiagnosticRule.reportInconsistentOverload,
|
||||||
if (overloads.length > 0 && overloads[0].details.name === '') {
|
LocMessage.singleOverload().format({ name }),
|
||||||
exemptMissingImplementation = true;
|
primaryDecl.node.name
|
||||||
}
|
);
|
||||||
} else if (isFunction(type) && !FunctionType.isOverloaded(type)) {
|
}
|
||||||
implementationFunction = type;
|
|
||||||
|
// If the file is not a stub and this is the first overload,
|
||||||
|
// verify that there is an implementation.
|
||||||
|
if (this._fileInfo.isStubFile || overloadedFunctions.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let implementationFunction: FunctionType | undefined;
|
||||||
|
|
||||||
|
if (isOverloadedFunction(type)) {
|
||||||
|
implementationFunction = OverloadedFunctionType.getImplementation(type);
|
||||||
|
} else if (isFunction(type) && !FunctionType.isOverloaded(type)) {
|
||||||
|
implementationFunction = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!implementationFunction) {
|
||||||
|
const containingClassNode = ParseTreeUtils.getEnclosingClassOrFunction(primaryDecl.node);
|
||||||
|
if (containingClassNode && containingClassNode.nodeType === ParseNodeType.Class) {
|
||||||
|
const classType = this._evaluator.getTypeOfClass(containingClassNode);
|
||||||
|
if (classType) {
|
||||||
|
if (ClassType.isProtocolClass(classType.classType)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!implementationFunction) {
|
if (ClassType.supportsAbstractMethods(classType.classType)) {
|
||||||
const containingClassNode = ParseTreeUtils.getEnclosingClassOrFunction(primaryDecl.node);
|
if (
|
||||||
if (containingClassNode && containingClassNode.nodeType === ParseNodeType.Class) {
|
isOverloadedFunction(type) &&
|
||||||
const classType = this._evaluator.getTypeOfClass(containingClassNode);
|
OverloadedFunctionType.getOverloads(type).every((overload) =>
|
||||||
if (classType) {
|
FunctionType.isAbstractMethod(overload)
|
||||||
if (ClassType.isProtocolClass(classType.classType)) {
|
)
|
||||||
exemptMissingImplementation = true;
|
) {
|
||||||
} else if (ClassType.supportsAbstractMethods(classType.classType)) {
|
return;
|
||||||
if (
|
|
||||||
isOverloadedFunction(type) &&
|
|
||||||
OverloadedFunctionType.getOverloads(type).every((overload) =>
|
|
||||||
FunctionType.isAbstractMethod(overload)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
exemptMissingImplementation = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a method within a protocol class, don't require that
|
|
||||||
// there is an implementation.
|
|
||||||
if (!exemptMissingImplementation) {
|
|
||||||
this._evaluator.addDiagnostic(
|
|
||||||
DiagnosticRule.reportNoOverloadImplementation,
|
|
||||||
LocMessage.overloadWithoutImplementation().format({
|
|
||||||
name: primaryDecl.node.name.value,
|
|
||||||
}),
|
|
||||||
primaryDecl.node.name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (isOverloadedFunction(type)) {
|
|
||||||
// Verify that all overload signatures are assignable to implementation signature.
|
|
||||||
OverloadedFunctionType.getOverloads(type).forEach((overload, index) => {
|
|
||||||
const diag = new DiagnosticAddendum();
|
|
||||||
if (!this._isLegalOverloadImplementation(overload, implementationFunction!, diag)) {
|
|
||||||
if (implementationFunction!.details.declaration) {
|
|
||||||
const diagnostic = this._evaluator.addDiagnostic(
|
|
||||||
DiagnosticRule.reportInconsistentOverload,
|
|
||||||
LocMessage.overloadImplementationMismatch().format({
|
|
||||||
name,
|
|
||||||
index: index + 1,
|
|
||||||
}) + diag.getString(),
|
|
||||||
implementationFunction!.details.declaration.node.name
|
|
||||||
);
|
|
||||||
|
|
||||||
if (diagnostic && overload.details.declaration) {
|
|
||||||
diagnostic.addRelatedInfo(
|
|
||||||
LocAddendum.overloadSignature(),
|
|
||||||
overload.details.declaration?.uri ?? primaryDecl.uri,
|
|
||||||
overload.details.declaration?.range ?? primaryDecl.range
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a method within a protocol class, don't require that
|
||||||
|
// there is an implementation.
|
||||||
|
this._evaluator.addDiagnostic(
|
||||||
|
DiagnosticRule.reportNoOverloadImplementation,
|
||||||
|
LocMessage.overloadWithoutImplementation().format({
|
||||||
|
name: primaryDecl.node.name.value,
|
||||||
|
}),
|
||||||
|
primaryDecl.node.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isOverloadedFunction(type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that all overload signatures are assignable to implementation signature.
|
||||||
|
OverloadedFunctionType.getOverloads(type).forEach((overload, index) => {
|
||||||
|
const diag = new DiagnosticAddendum();
|
||||||
|
if (!this._isLegalOverloadImplementation(overload, implementationFunction!, diag)) {
|
||||||
|
if (implementationFunction!.details.declaration) {
|
||||||
|
const diagnostic = this._evaluator.addDiagnostic(
|
||||||
|
DiagnosticRule.reportInconsistentOverload,
|
||||||
|
LocMessage.overloadImplementationMismatch().format({
|
||||||
|
name,
|
||||||
|
index: index + 1,
|
||||||
|
}) + diag.getString(),
|
||||||
|
implementationFunction!.details.declaration.node.name
|
||||||
|
);
|
||||||
|
|
||||||
|
if (diagnostic && overload.details.declaration) {
|
||||||
|
diagnostic.addRelatedInfo(
|
||||||
|
LocAddendum.overloadSignature(),
|
||||||
|
overload.details.declaration?.uri ?? primaryDecl.uri,
|
||||||
|
overload.details.declaration?.range ?? primaryDecl.range
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _reportMultipleFinalDeclarations(name: string, symbol: Symbol, scopeType: ScopeType) {
|
private _reportMultipleFinalDeclarations(name: string, symbol: Symbol, scopeType: ScopeType) {
|
||||||
|
@ -43,7 +43,7 @@ class ClassB(Protocol):
|
|||||||
|
|
||||||
|
|
||||||
def deco1(
|
def deco1(
|
||||||
_origin: Callable[P, T]
|
_origin: Callable[P, T],
|
||||||
) -> Callable[[Callable[..., Any]], Callable[P, T]]: ...
|
) -> Callable[[Callable[..., Any]], Callable[P, T]]: ...
|
||||||
|
|
||||||
|
|
||||||
@ -60,3 +60,21 @@ def func5(v: int | str) -> int | str: ...
|
|||||||
|
|
||||||
@deco1(func5)
|
@deco1(func5)
|
||||||
def func6(*args: Any, **kwargs: Any) -> Any: ...
|
def func6(*args: Any, **kwargs: Any) -> Any: ...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def deco2() -> Callable[[Callable[P, T]], Callable[P, T | None]]: ...
|
||||||
|
@overload
|
||||||
|
def deco2(
|
||||||
|
x: Callable[[], T],
|
||||||
|
) -> Callable[[Callable[P, T]], Callable[P, T]]: ...
|
||||||
|
|
||||||
|
|
||||||
|
def deco2(
|
||||||
|
x: Callable[[], T | None] = lambda: None,
|
||||||
|
) -> Callable[[Callable[P, T]], Callable[P, T | None]]: ...
|
||||||
|
|
||||||
|
|
||||||
|
@deco2(x=dict)
|
||||||
|
def func7() -> dict[str, str]:
|
||||||
|
return {}
|
||||||
|
Loading…
Reference in New Issue
Block a user