Skip to content

Commit

Permalink
Added code to conform with the new checks mandated in the typing spec…
Browse files Browse the repository at this point in the history
… for `@final` and `@override` applied to an overload. This addresses #9747.
  • Loading branch information
erictraut committed Jan 25, 2025
1 parent 9f71e14 commit b1971d5
Show file tree
Hide file tree
Showing 19 changed files with 120 additions and 67 deletions.
68 changes: 39 additions & 29 deletions packages/pyright-internal/src/analyzer/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6212,7 +6212,7 @@ export class Checker extends ParseTreeWalker {
const overloads = OverloadedType.getOverloads(typeOfSymbol);
const implementation = OverloadedType.getImplementation(typeOfSymbol);

this._validateOverloadFinalConsistency(overloads, implementation);
this._validateOverloadFinalOverride(overloads, implementation);

this._validateOverloadAbstractConsistency(overloads, implementation);
});
Expand Down Expand Up @@ -6264,42 +6264,52 @@ export class Checker extends ParseTreeWalker {
});
}

private _validateOverloadFinalConsistency(overloads: FunctionType[], implementation: Type | undefined) {
// If there's an implementation, it will determine whether the
// function is @final.
if (implementation && isFunction(implementation)) {
// If one or more of the overloads is marked @final but the
// implementation is not, report an error.
if (!FunctionType.isFinal(implementation)) {
overloads.forEach((overload) => {
if (FunctionType.isFinal(overload) && overload.shared.declaration?.node) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportInconsistentOverload,
LocMessage.overloadFinalInconsistencyImpl().format({
name: overload.shared.name,
}),
getNameNodeForDeclaration(overload.shared.declaration) ?? overload.shared.declaration.node
);
}
});
}
return;
}

if (overloads.length > 0 && !FunctionType.isFinal(overloads[0])) {
overloads.slice(1).forEach((overload, index) => {
private _validateOverloadFinalOverride(overloads: FunctionType[], implementation: Type | undefined) {
// If there's an implementation, the overloads are not allowed to be marked final or override.
if (implementation) {
overloads.forEach((overload) => {
if (FunctionType.isFinal(overload) && overload.shared.declaration?.node) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportInconsistentOverload,
LocMessage.overloadFinalInconsistencyNoImpl().format({
name: overload.shared.name,
index: index + 2,
}),
LocMessage.overloadFinalImpl(),
getNameNodeForDeclaration(overload.shared.declaration) ?? overload.shared.declaration.node
);
}

if (FunctionType.isOverridden(overload) && overload.shared.declaration?.node) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportInconsistentOverload,
LocMessage.overloadOverrideImpl(),
getNameNodeForDeclaration(overload.shared.declaration) ?? overload.shared.declaration.node
);
}
});

return;
}

// If there's not an implementation, only the first overload can be marked final.
if (overloads.length === 0) {
return;
}

overloads.slice(1).forEach((overload, index) => {
if (FunctionType.isFinal(overload) && overload.shared.declaration?.node) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportInconsistentOverload,
LocMessage.overloadFinalNoImpl(),
getNameNodeForDeclaration(overload.shared.declaration) ?? overload.shared.declaration.node
);
}

if (FunctionType.isOverridden(overload) && overload.shared.declaration?.node) {
this._evaluator.addDiagnostic(
DiagnosticRule.reportInconsistentOverload,
LocMessage.overloadOverrideNoImpl(),
getNameNodeForDeclaration(overload.shared.declaration) ?? overload.shared.declaration.node
);
}
});
}

// For a TypedDict class that derives from another TypedDict class
Expand Down
10 changes: 4 additions & 6 deletions packages/pyright-internal/src/localization/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -747,16 +747,14 @@ export namespace Localizer {
new ParameterizedString<{ name: string }>(getRawString('Diagnostic.overloadAbstractImplMismatch'));
export const overloadClassMethodInconsistent = () =>
new ParameterizedString<{ name: string }>(getRawString('Diagnostic.overloadClassMethodInconsistent'));
export const overloadFinalInconsistencyImpl = () =>
new ParameterizedString<{ name: string }>(getRawString('Diagnostic.overloadFinalInconsistencyImpl'));
export const overloadFinalInconsistencyNoImpl = () =>
new ParameterizedString<{ name: string; index: number }>(
getRawString('Diagnostic.overloadFinalInconsistencyNoImpl')
);
export const overloadFinalImpl = () => getRawString('Diagnostic.overloadFinalImpl');
export const overloadFinalNoImpl = () => getRawString('Diagnostic.overloadFinalNoImpl');
export const overloadImplementationMismatch = () =>
new ParameterizedString<{ name: string; index: number }>(
getRawString('Diagnostic.overloadImplementationMismatch')
);
export const overloadOverrideImpl = () => getRawString('Diagnostic.overloadOverrideImpl');
export const overloadOverrideNoImpl = () => getRawString('Diagnostic.overloadOverrideNoImpl');
export const overloadReturnTypeMismatch = () =>
new ParameterizedString<{ name: string; newIndex: number; prevIndex: number }>(
getRawString('Diagnostic.overloadReturnTypeMismatch')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,6 @@
"overloadAbstractImplMismatch": "Přetížení musí odpovídat abstraktnímu stavu implementace.",
"overloadAbstractMismatch": "Buď musí být všechna přetížení abstraktní, nebo naopak nesmí být žádné z nich abstraktní.",
"overloadClassMethodInconsistent": "Přetížení pro {name} používají @classmethod nekonzistentně.",
"overloadFinalInconsistencyImpl": "Přetížení pro „{name}“ je označené @final ale implementace není",
"overloadFinalInconsistencyNoImpl": "Přetížení {index} pro „{name}“ je označené @final ale přetížení 1 není",
"overloadImplementationMismatch": "Přetížená implementace není konzistentní se signaturou přetížení {index}",
"overloadReturnTypeMismatch": "Přetížení {prevIndex} pro {name} se překrývá s přetížením {newIndex} a vrací nekompatibilní typ",
"overloadStaticMethodInconsistent": "Přetížení pro {name} používají @staticmethod nekonzistentně.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,6 @@
"overloadAbstractImplMismatch": "Überladungen müssen dem abstrakten Status der Implementierung entsprechen.",
"overloadAbstractMismatch": "Überladungen müssen alle abstrakt sein oder nicht.",
"overloadClassMethodInconsistent": "Überladungen für \"{name}\" verwenden @classmethod inkonsistent",
"overloadFinalInconsistencyImpl": "Die Überladung für „{name}“ ist @final markiert, die Implementierung ist es jedoch nicht.",
"overloadFinalInconsistencyNoImpl": "Überladung {index} für „{name}“ ist als @final markiert, Überladung 1 ist es jedoch nicht.",
"overloadImplementationMismatch": "Die überladene Implementierung ist nicht konsistent mit der Signatur der Überladung {index}",
"overloadReturnTypeMismatch": "Überladung {prevIndex} für \"{name}\" überlappt {newIndex} und gibt einen inkompatiblen Typ zurück.",
"overloadStaticMethodInconsistent": "Überladungen für \"{name}\" verwenden @staticmethod inkonsistent",
Expand Down
16 changes: 12 additions & 4 deletions packages/pyright-internal/src/localization/package.nls.en-us.json
Original file line number Diff line number Diff line change
Expand Up @@ -908,15 +908,23 @@
"message": "Overloads for \"{name}\" use @classmethod inconsistently",
"comment": "{Locked='@classmethod'}"
},
"overloadFinalInconsistencyImpl": {
"message": "Overload for \"{name}\" is marked @final but implementation is not",
"overloadFinalImpl": {
"message": "@final decorator should be applied only to the implementation",
"comment": "{Locked='@final'}"
},
"overloadFinalInconsistencyNoImpl": {
"message": "Overload {index} for \"{name}\" is marked @final but overload 1 is not",
"overloadFinalNoImpl": {
"message": "Only the first overload should be marked @final",
"comment": "{Locked='@final'}"
},
"overloadImplementationMismatch": "Overloaded implementation is not consistent with signature of overload {index}",
"overloadOverrideImpl": {
"message": "@override decorator should be applied only to the implementation",
"comment": "{Locked='@override'}"
},
"overloadOverrideNoImpl": {
"message": "Only the first overload should be marked @override",
"comment": "{Locked='@override'}"
},
"overloadReturnTypeMismatch": "Overload {prevIndex} for \"{name}\" overlaps overload {newIndex} and returns an incompatible type",
"overloadStaticMethodInconsistent": {
"message": "Overloads for \"{name}\" use @staticmethod inconsistently",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,6 @@
"overloadAbstractImplMismatch": "Las sobrecargas deben coincidir con el estado abstracto de la implementación",
"overloadAbstractMismatch": "Todos los métodos sobrecargados deben ser abstractos o no",
"overloadClassMethodInconsistent": "Las sobrecargas de \"{name}\" usan @classmethod de forma incoherente",
"overloadFinalInconsistencyImpl": "La sobrecarga de \"{name}\" está marcada @final pero la implementación no",
"overloadFinalInconsistencyNoImpl": "La sobrecarga {index} para \"{name}\" está marcada @final pero la sobrecarga 1 no lo está.",
"overloadImplementationMismatch": "La implementación de la sobrecarga no es consistente con la firma de la sobrecarga {index}",
"overloadReturnTypeMismatch": "La sobrecarga {prevIndex} para \" {name}\" se superpone con la sobrecarga {newIndex} y devuelve un tipo incompatible",
"overloadStaticMethodInconsistent": "Las sobrecargas de \"{name}\" usan @staticmethod de forma incoherente",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,6 @@
"overloadAbstractImplMismatch": "Les surcharges doivent correspondre à l’état abstrait de l’implémentation",
"overloadAbstractMismatch": "Les surcharges doivent toutes être abstraites ou non",
"overloadClassMethodInconsistent": "Les surcharges pour « {name} » utilisent @classmethod de manière incohérente",
"overloadFinalInconsistencyImpl": "La surcharge pour « {name} » est marquée @final, mais l’implémentation ne l’est pas",
"overloadFinalInconsistencyNoImpl": "La surcharge {index} pour « {name} » est marquée @final mais la surcharge 1 n’est pas",
"overloadImplementationMismatch": "L’implémentation surchargée n’est pas cohérente avec la signature de la surcharge {index}",
"overloadReturnTypeMismatch": "La surcharge {prevIndex} pour « {name} » chevauche la surcharge {newIndex} et retourne un type incompatible",
"overloadStaticMethodInconsistent": "Les surcharges pour « {name} » utilisent @staticmethod de manière incohérente",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,6 @@
"overloadAbstractImplMismatch": "Gli overload devono corrispondere allo stato astratto dell'implementazione",
"overloadAbstractMismatch": "Gli overload devono essere tutti astratti o no",
"overloadClassMethodInconsistent": "Gli overload per \"{name}\" usano @classmethod in modo incoerente",
"overloadFinalInconsistencyImpl": "L'overload per “{name}” è contrassegnato @final ma l'implementazione non lo è",
"overloadFinalInconsistencyNoImpl": "L'overload {index} per “{name}” è contrassegnato @final ma l'overload 1 non lo è",
"overloadImplementationMismatch": "L'implementazione di overload non è coerente con la firma dell'overload {index}",
"overloadReturnTypeMismatch": "L'overload {prevIndex} per \"{name}\" si sovrappone all'overload {newIndex} e restituisce un tipo incompatibile",
"overloadStaticMethodInconsistent": "Gli overload per \"{name}\" usano @staticmethod in modo incoerente",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,6 @@
"overloadAbstractImplMismatch": "オーバーロードは実装の抽象状態と一致する必要があります",
"overloadAbstractMismatch": "オーバーロードはすべて抽象であるか抽象でない必要があります",
"overloadClassMethodInconsistent": "\"{name}\" のオーバーロードでは、@classmethod を不整合に使用します",
"overloadFinalInconsistencyImpl": "\"{name}\" のオーバーロードは @final としてマークされていますが、実装は @final としてマークされていません",
"overloadFinalInconsistencyNoImpl": "\"{name}\" のオーバーロード {index} は @final としてマークされていますが、オーバーロード 1 は @final としてマークされていません",
"overloadImplementationMismatch": "オーバーロードされた実装がオーバーロード {index} のシグネチャと一致しません",
"overloadReturnTypeMismatch": "\"{name}\" のオーバーロード {prevIndex} はオーバーロード {newIndex} と重複し、互換性のない型を返します",
"overloadStaticMethodInconsistent": "\"{name}\" のオーバーロードでは、@staticmethod を不整合に使用します",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,6 @@
"overloadAbstractImplMismatch": "오버로드는 구현의 추상 상태와 일치해야 합니다.",
"overloadAbstractMismatch": "오버로드는 모두 추상이거나 아니어야 합니다",
"overloadClassMethodInconsistent": "\"{name}\"의 오버로드가 @classmethod를 일관되지 않게 사용합니다.",
"overloadFinalInconsistencyImpl": "\"{name}\"에 대한 오버로드가 @final로 표시되었지만 구현은 아닙니다.",
"overloadFinalInconsistencyNoImpl": "\"{name}\"에 대한 오버로드 {index}는 @final로 표시되지만 오버로드 1은 표시되지 않습니다.",
"overloadImplementationMismatch": "오버로드된 구현이 오버로드 {index}의 시그니처와 일치하지 않습니다.",
"overloadReturnTypeMismatch": "\"{name}\"에 대한 {prevIndex} 오버로드가 오버로드 {newIndex}과(와) 겹치고 호환되지 않는 형식을 반환합니다.",
"overloadStaticMethodInconsistent": "\"{name}\"의 오버로드가 @staticmethod를 일관되지 않게 사용합니다.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,6 @@
"overloadAbstractImplMismatch": "Przeciążenia muszą być zgodne ze stanem abstrakcyjnym implementacji",
"overloadAbstractMismatch": "Przeciążenia muszą być abstrakcyjne lub nieabstrakcyjne",
"overloadClassMethodInconsistent": "Przeciążenia dla nazwy „{name}” używają metody @classmethod niekonsekwentnie",
"overloadFinalInconsistencyImpl": "Przeciążenie elementu „{name}” jest oznaczone @final, ale implementacja nie jest",
"overloadFinalInconsistencyNoImpl": "Przeciążenie {index} dla elementu „{name}” jest oznaczone @final, ale przeciążenie 1 nie jest",
"overloadImplementationMismatch": "Przeciążone wdrożenie jest niespójne z sygnaturą przeciążenia {index}",
"overloadReturnTypeMismatch": "Przeciążenie {prevIndex} dla nazwy „{name}” nakłada się na przeciążenie {newIndex} i zwraca niezgodny typ",
"overloadStaticMethodInconsistent": "Przeciążenia dla nazwy „{name}” używają metody @staticmethod niekonsekwentnie",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,6 @@
"overloadAbstractImplMismatch": "As sobrecargas devem corresponder ao status abstrato da implementação",
"overloadAbstractMismatch": "As sobrecargas devem ser abstratas ou não",
"overloadClassMethodInconsistent": "Sobrecargas para \"{name}\" usam @classmethod inconsistentemente",
"overloadFinalInconsistencyImpl": "A sobrecarga para \"{name}\" está marcada como @final mas a implementação não está",
"overloadFinalInconsistencyNoImpl": "A sobrecarga {index} para \"{name}\" está marcada como @final mas a sobrecarga 1 não está",
"overloadImplementationMismatch": "A implementação sobrecarregada não é consistente com a assinatura da sobrecarga {index}",
"overloadReturnTypeMismatch": "A sobrecarga {prevIndex} para \"{name}\" sobrepõe a sobrecarga {newIndex} e retorna um tipo incompatível",
"overloadStaticMethodInconsistent": "Sobrecargas para \"{name}\" usam @staticmethod inconsistentemente",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,6 @@
"overloadAbstractImplMismatch": "[IgMzu][นั้Øvërløæðs mµst mætçh æþstræçt stætµs øf ïmplëmëñtætïøñẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्นั้ढूँ]",
"overloadAbstractMismatch": "[54DCM][นั้Øvërløæðs mµst æll þë æþstræçt ør ñøtẤğ倪İЂҰक्र्तिृまẤนั้ढूँ]",
"overloadClassMethodInconsistent": "[8y6vM][นั้Øvërløæðs før \"{ñæmë}\" µsë @classmethod ïñçøñsïstëñtlÿẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्นั้ढूँ]",
"overloadFinalInconsistencyImpl": "[0hpZY][นั้Øvërløæð før \"{ñæmë}\" ïs mærkëð @final þµt ïmplëmëñtætïøñ ïs ñøtẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्र्तिृนั้ढूँ]",
"overloadFinalInconsistencyNoImpl": "[Z6TSL][นั้Øvërløæð {ïñðëx} før \"{ñæmë}\" ïs mærkëð @final þµt øvërløæð 1 ïs ñøtẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्र्तिृนั้ढूँ]",
"overloadImplementationMismatch": "[dXlXE][นั้Øvërløæðëð ïmplëmëñtætïøñ ïs ñøt çøñsïstëñt wïth sïgñætµrë øf øvërløæð {ïñðëx}Ấğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्र्तिृまẤğ倪นั้ढूँ]",
"overloadReturnTypeMismatch": "[6BN74][นั้Øvërløæð {prëvÏñðëx} før \"{ñæmë}\" øvërlæps øvërløæð {ñëwÏñðëx} æñð rëtµrñs æñ ïñçømpætïþlë tÿpëẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्र्นั้ढूँ]",
"overloadStaticMethodInconsistent": "[PKQvM][นั้Øvërløæðs før \"{ñæmë}\" µsë @staticmethod ïñçøñsïstëñtlÿẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्นั้ढूँ]",
Expand Down
Loading

0 comments on commit b1971d5

Please sign in to comment.