mirror of
https://github.com/microsoft/pyright.git
synced 2024-07-07 04:26:31 +03:00
Added a check for class pattern matches where the number of positional patterns exceeds the number of positional fields supported by the class. This addresses #6019.
This commit is contained in:
parent
aa67d94adb
commit
0e95e48650
|
@ -1669,16 +1669,9 @@ export function validateClassPattern(evaluator: TypeEvaluator, pattern: PatternC
|
|||
} else {
|
||||
const isBuiltIn = isClassSpecialCaseForClassPattern(exprType);
|
||||
|
||||
// If it's a special-case builtin class, only one positional argument is allowed.
|
||||
// If it's a special-case builtin class, only positional arguments are allowed.
|
||||
if (isBuiltIn) {
|
||||
if (pattern.arguments.length > 1) {
|
||||
evaluator.addDiagnostic(
|
||||
getFileInfo(pattern).diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.classPatternBuiltInArgCount(),
|
||||
pattern.arguments[1]
|
||||
);
|
||||
} else if (pattern.arguments.length === 1 && pattern.arguments[0].name) {
|
||||
if (pattern.arguments.length === 1 && pattern.arguments[0].name) {
|
||||
evaluator.addDiagnostic(
|
||||
getFileInfo(pattern).diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
|
@ -1687,6 +1680,36 @@ export function validateClassPattern(evaluator: TypeEvaluator, pattern: PatternC
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Emits an error if the supplied number of positional patterns is less than
|
||||
// expected for the given subject type.
|
||||
let positionalPatternCount = pattern.arguments.findIndex((arg) => arg.name !== undefined);
|
||||
if (positionalPatternCount < 0) {
|
||||
positionalPatternCount = pattern.arguments.length;
|
||||
}
|
||||
|
||||
let expectedPatternCount = 1;
|
||||
if (!isBuiltIn) {
|
||||
let positionalArgNames: string[] = [];
|
||||
if (pattern.arguments.some((arg) => !arg.name)) {
|
||||
positionalArgNames = getPositionalMatchArgNames(evaluator, exprType);
|
||||
}
|
||||
|
||||
expectedPatternCount = positionalArgNames.length;
|
||||
}
|
||||
|
||||
if (positionalPatternCount > expectedPatternCount) {
|
||||
evaluator.addDiagnostic(
|
||||
getFileInfo(pattern).diagnosticRuleSet.reportGeneralTypeIssues,
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
Localizer.Diagnostic.classPatternPositionalArgCount().format({
|
||||
type: exprType.details.name,
|
||||
expected: expectedPatternCount,
|
||||
received: positionalPatternCount,
|
||||
}),
|
||||
pattern.arguments[expectedPatternCount]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -281,9 +281,12 @@ export namespace Localizer {
|
|||
export const classMethodClsParam = () => getRawString('Diagnostic.classMethodClsParam');
|
||||
export const classNotRuntimeSubscriptable = () =>
|
||||
new ParameterizedString<{ name: string }>(getRawString('Diagnostic.classNotRuntimeSubscriptable'));
|
||||
export const classPatternBuiltInArgCount = () => getRawString('Diagnostic.classPatternBuiltInArgCount');
|
||||
export const classPatternBuiltInArgPositional = () =>
|
||||
getRawString('Diagnostic.classPatternBuiltInArgPositional');
|
||||
export const classPatternPositionalArgCount = () =>
|
||||
new ParameterizedString<{ type: string; expected: number; received: number }>(
|
||||
getRawString('Diagnostic.classPatternPositionalArgCount')
|
||||
);
|
||||
export const classPatternTypeAlias = () =>
|
||||
new ParameterizedString<{ type: string }>(getRawString('Diagnostic.classPatternTypeAlias'));
|
||||
export const classTypeParametersIllegal = () => getRawString('Diagnostic.classTypeParametersIllegal');
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "Přepsání __class_getitem__ by mělo mít parametr cls",
|
||||
"classMethodClsParam": "Metody třídy by měly mít parametr „cls“",
|
||||
"classNotRuntimeSubscriptable": "Dolní index pro třídu {name} vygeneruje výjimku modulu runtime; anotaci typu uzavřete do uvozovek",
|
||||
"classPatternBuiltInArgCount": "Vzor třídy přijímá maximálně 1 poziční dílčí vzor",
|
||||
"classPatternBuiltInArgPositional": "Vzor třídy přijímá pouze poziční dílčí vzor",
|
||||
"classPatternTypeAlias": "Typ „{type}“ nelze použít ve vzorci třídy, protože se jedná o specializovaný alias typu",
|
||||
"classTypeParametersIllegal": "Syntaxe parametru typu třídy vyžaduje Python 312 nebo novější",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "__class_getitem__ Außerkraftsetzung sollte einen \"cls\"-Parameter annehmen.",
|
||||
"classMethodClsParam": "Klassenmethoden sollten einen \"cls\"-Parameter verwenden.",
|
||||
"classNotRuntimeSubscriptable": "Durch das Tiefstellungsskript für die Klasse \"{name}\" wird eine Laufzeitausnahme generiert; schließen Sie die Typanmerkung in Anführungszeichen ein",
|
||||
"classPatternBuiltInArgCount": "Das Klassenmuster akzeptiert höchstens 1 positionsbezogenes Untermuster.",
|
||||
"classPatternBuiltInArgPositional": "Das Klassenmuster akzeptiert nur positionsbezogenes Untermuster.",
|
||||
"classPatternTypeAlias": "\"{type}\" kann nicht in einem Klassenmuster verwendet werden, da es sich um einen spezialisierten Typalias handelt.",
|
||||
"classTypeParametersIllegal": "Die Syntax des Klassentypparameters erfordert Python 3.12 oder höher.",
|
||||
|
|
|
@ -52,8 +52,8 @@
|
|||
"classGetItemClsParam": "__class_getitem__ override should take a \"cls\" parameter",
|
||||
"classMethodClsParam": "Class methods should take a \"cls\" parameter",
|
||||
"classNotRuntimeSubscriptable": "Subscript for class \"{name}\" will generate runtime exception; enclose type annotation in quotes",
|
||||
"classPatternBuiltInArgCount": "Class pattern accepts at most 1 positional sub-pattern",
|
||||
"classPatternBuiltInArgPositional": "Class pattern accepts only positional sub-pattern",
|
||||
"classPatternPositionalArgCount": "Too many positional patterns for class \"{type}\"; expected {expected} but received {received}",
|
||||
"classPatternTypeAlias": "\"{type}\" cannot be used in a class pattern because it is a specialized type alias",
|
||||
"classTypeParametersIllegal": "Class type parameter syntax requires Python 3.12 or newer",
|
||||
"classVarNotAllowed": "\"ClassVar\" is not allowed in this context",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "__class_getitem__ debe tomar un parámetro \"cls\"",
|
||||
"classMethodClsParam": "Los métodos de clase deben tomar un parámetro \"cls\"",
|
||||
"classNotRuntimeSubscriptable": "El subíndice para la clase \"{name}\" generará una excepción en tiempo de ejecución; encierre la anotación de tipo entre comillas",
|
||||
"classPatternBuiltInArgCount": "El patrón de clase acepta como máximo 1 subpatrón posicional",
|
||||
"classPatternBuiltInArgPositional": "El patrón de clase solo acepta subpatrones posicionales",
|
||||
"classPatternTypeAlias": "\"{type}\" no se puede usar en un patrón de clase porque es un alias de tipo especializado",
|
||||
"classTypeParametersIllegal": "La sintaxis de los parámetros de tipo de clase requiere Python 3.12 o posterior.",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "__class_getitem__ remplacement doit prendre un paramètre « cls »",
|
||||
"classMethodClsParam": "Les méthodes de classe doivent prendre un paramètre \"cls\"",
|
||||
"classNotRuntimeSubscriptable": "L’indice de la classe « {name} » génère une exception d’exécution ; placer l’annotation de type entre guillemets",
|
||||
"classPatternBuiltInArgCount": "Le modèle de classe accepte au maximum 1 sous-modèle positionnel",
|
||||
"classPatternBuiltInArgPositional": "Le modèle de classe accepte uniquement le sous-modèle positionnel",
|
||||
"classPatternTypeAlias": "\"{type}\" ne peut pas être utilisé dans un modèle de classe car il s'agit d'un alias de type spécialisé",
|
||||
"classTypeParametersIllegal": "La syntaxe du paramètre de type de classe nécessite Python 3.12 ou version ultérieure",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "__class_getitem__ override deve accettare un parametro \"cls\"",
|
||||
"classMethodClsParam": "I metodi di classe devono accettare un parametro \"cls\"",
|
||||
"classNotRuntimeSubscriptable": "Il pedice per la classe \"{name}\" genererà un'eccezione di runtime; racchiudere l'annotazione di tipo tra virgolette",
|
||||
"classPatternBuiltInArgCount": "Il modello di classe accetta al massimo 1 sotto pattern posizionale",
|
||||
"classPatternBuiltInArgPositional": "Il modello di classe accetta solo un sotto pattern posizionale",
|
||||
"classPatternTypeAlias": "\"{type}\" non può essere usato in uno schema di classe, perché è un alias di tipo specializzato",
|
||||
"classTypeParametersIllegal": "La sintassi del parametro del tipo di classe richiede Python 3.12 o versione successiva",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "__class_getitem__ override は \"cls\" パラメーターを受け取る必要があります",
|
||||
"classMethodClsParam": "クラス メソッドは \"cls\" パラメーターを受け取る必要があります",
|
||||
"classNotRuntimeSubscriptable": "クラス \"{name}\" の添字はランタイム例外を生成します。型の注釈を引用符で囲む",
|
||||
"classPatternBuiltInArgCount": "クラス パターンは、最大 1 つの位置指定サブパターンを受け入れます",
|
||||
"classPatternBuiltInArgPositional": "クラス パターンは位置指定サブパターンのみを受け入れます",
|
||||
"classPatternTypeAlias": "\"{type}\" は特殊な型エイリアスであるため、クラス パターンでは使用できません",
|
||||
"classTypeParametersIllegal": "クラス型パラメーターの構文には Python 3.12 以降が必要です",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "__class_getitem__ 재정의는 \"cls\" 매개 변수를 사용해야 합니다.",
|
||||
"classMethodClsParam": "클래스 메서드는 ‘cls’ 매개 변수를 사용해야 합니다.",
|
||||
"classNotRuntimeSubscriptable": "클래스 \"{name}\"에 대한 첨자는 런타임 예외를 생성합니다. 따옴표로 형식 주석 묶기",
|
||||
"classPatternBuiltInArgCount": "클래스 패턴은 최대 1개의 위치 하위 패턴을 허용합니다.",
|
||||
"classPatternBuiltInArgPositional": "클래스 패턴은 위치 하위 패턴만 허용합니다.",
|
||||
"classPatternTypeAlias": "‘{type}’은(는) 특수 형식 별칭이므로 클래스 패턴에서 사용할 수 없습니다.",
|
||||
"classTypeParametersIllegal": "클래스 형식 매개 변수 구문에는 Python 3.12 이상이 필요합니다.",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "Przesłonięcie __class_getitem__ powinno przyjmować parametr „cls”.",
|
||||
"classMethodClsParam": "Metody klasy powinny przyjmować parametr „cls”",
|
||||
"classNotRuntimeSubscriptable": "Indeks dolny dla klasy „{name}” wygeneruje wyjątek czasu uruchamiania; umieść adnotację typu w cudzysłowie",
|
||||
"classPatternBuiltInArgCount": "Wzorzec klasy akceptuje co najwyżej 1 podwzorzec pozycyjny",
|
||||
"classPatternBuiltInArgPositional": "Wzorzec klasy akceptuje tylko podwzorzec pozycyjny",
|
||||
"classPatternTypeAlias": "„{type}” nie może być używany we wzorcu klasy, ponieważ jest to alias typu specjalnego",
|
||||
"classTypeParametersIllegal": "Składnia parametru typu klasy wymaga języka Python w wersji 3.12 lub nowszej",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "A substituição__class_getitem__ deve usar um parâmetro \"cls\"",
|
||||
"classMethodClsParam": "Os métodos de classe devem usar um parâmetro \"cls\"",
|
||||
"classNotRuntimeSubscriptable": "O subscrito para a classe \"{name}\" gerará uma exceção de runtime. Coloque a anotação de tipo entre aspas",
|
||||
"classPatternBuiltInArgCount": "O padrão de classe aceita no máximo 1 sub-padrão posicional",
|
||||
"classPatternBuiltInArgPositional": "O padrão de classe aceita apenas sub-padrão posicional",
|
||||
"classPatternTypeAlias": "\"{type}\" não pode ser usado em um padrão de classe porque é um alias de tipo especializado",
|
||||
"classTypeParametersIllegal": "A sintaxe do parâmetro de tipo de classe requer o Python 3.12 ou mais recente",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "[A2iHF][นั้__çlæss_gëtïtëm__ øvërrïðë shøµlð tækë æ \"çls\" pæræmëtërẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्นั้ढूँ]",
|
||||
"classMethodClsParam": "[aWMN3][นั้Çlæss mëthøðs shøµlð tækë æ \"çls\" pæræmëtërẤğ倪İЂҰक्र्तिृまẤğ倪นั้ढूँ]",
|
||||
"classNotRuntimeSubscriptable": "[O9BL6][นั้§µþsçrïpt før çlæss \"{ñæmë}\" wïll gëñërætë rµñtïmë ëxçëptïøñ; ëñçløsë tÿpë æññøtætïøñ ïñ qµøtësẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्र्นั้ढूँ]",
|
||||
"classPatternBuiltInArgCount": "[moI4V][นั้Çlæss pættërñ æççëpts æt møst 1 pøsïtïøñæl sµþ-pættërñẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्นั้ढूँ]",
|
||||
"classPatternBuiltInArgPositional": "[DOfs5][นั้Çlæss pættërñ æççëpts øñlÿ pøsïtïøñæl sµþ-pættërñẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰนั้ढूँ]",
|
||||
"classPatternTypeAlias": "[AxDtv][นั้\"{tÿpë}\" çæññøt þë µsëð ïñ æ çlæss pættërñ þëçæµsë ït ïs æ spëçïælïzëð tÿpë ælïæsẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्र्तिृまẤğ倪İนั้ढूँ]",
|
||||
"classTypeParametersIllegal": "[GybXD][นั้Çlæss tÿpë pæræmëtër sÿñtæx rëqµïrës Pÿthøñ 3.12 ør ñëwërẤğ倪İЂҰक्र्तिृまẤğ倪İЂҰक्र्นั้ढूँ]",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "Переопределение метода __class_getitem__ должно принимать параметр \"cls\"",
|
||||
"classMethodClsParam": "Методы класса должны принимать параметр cls",
|
||||
"classNotRuntimeSubscriptable": "Операция взятия подстроки для класса \"{name}\" создаст исключение среды выполнения; заключите заметку типа в кавычки",
|
||||
"classPatternBuiltInArgCount": "Шаблон класса принимает не более одного позиционного вложенного шаблона",
|
||||
"classPatternBuiltInArgPositional": "Шаблон класса принимает только позиционный вложенный шаблон",
|
||||
"classPatternTypeAlias": "\"{type}\" нельзя использовать в шаблоне класса, поскольку это псевдоним специализированного типа",
|
||||
"classTypeParametersIllegal": "Синтаксис параметра типа класса может использоваться в Python версии не ниже 3.12.",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "__class_getitem__ geçersiz kılması bir \"cls\" parametresi almalı",
|
||||
"classMethodClsParam": "Sınıf metotları bir \"cls\" parametresi almalıdır",
|
||||
"classNotRuntimeSubscriptable": "\"{name}\" sınıfına ait alt simge çalışma zamanı özel durumunu oluşturur; tür ek açıklamalarını tırnak içine alın",
|
||||
"classPatternBuiltInArgCount": "Sınıf deseni en fazla 1 konumsal alt deseni kabul eder",
|
||||
"classPatternBuiltInArgPositional": "Sınıf deseni yalnızca konumsal alt desen kabul eder",
|
||||
"classPatternTypeAlias": "\"{type}\" özel bir tür diğer adı olduğundan sınıf deseninde kullanılamaz",
|
||||
"classTypeParametersIllegal": "Sınıf türü parametresi söz dizimi için Python 3.12 veya daha yeni bir sürümü gerekiyor",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "__class_getitem__替代应采用“cls”参数",
|
||||
"classMethodClsParam": "类方法应采用“cls”参数",
|
||||
"classNotRuntimeSubscriptable": "类“{name}”的下标将生成运行时异常;将类型批注括在引号中",
|
||||
"classPatternBuiltInArgCount": "类模式最多接受 1 个位置子模式",
|
||||
"classPatternBuiltInArgPositional": "类模式仅接受位置子模式",
|
||||
"classPatternTypeAlias": "无法在类模式中使用“{type}”,因为它是专用类型别名",
|
||||
"classTypeParametersIllegal": "类类型参数语法需要 Python 3.12 或更高版本",
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
"classGetItemClsParam": "__class_getitem__ 覆寫應接受 \"cls\" 參數",
|
||||
"classMethodClsParam": "類別方法應採用 \"cls\" 參數",
|
||||
"classNotRuntimeSubscriptable": "類別 \"{name}\" 的下標會產生執行階段例外; 以引號括住類型註釋",
|
||||
"classPatternBuiltInArgCount": "類別模式最多接受 1 個位置子模式",
|
||||
"classPatternBuiltInArgPositional": "類別模式僅接受位置子模式",
|
||||
"classPatternTypeAlias": "無法在類別模式中使用 \"{type}\",因為它是特殊的型別別名",
|
||||
"classTypeParametersIllegal": "類別類型參數語法需要 Python 3.12 或更新版本",
|
||||
|
|
63
packages/pyright-internal/src/tests/samples/matchClass5.py
Normal file
63
packages/pyright-internal/src/tests/samples/matchClass5.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
# This sample tests the detection of too many positional patterns.
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class A:
|
||||
a: int
|
||||
|
||||
|
||||
class B:
|
||||
a: int
|
||||
b: int
|
||||
|
||||
__match_args__ = ("a", "b")
|
||||
|
||||
|
||||
class C(B):
|
||||
...
|
||||
|
||||
class D(int): ...
|
||||
|
||||
def func1(subj: A | B):
|
||||
match subj:
|
||||
# This should generate an error because A accepts only
|
||||
# one positional pattern.
|
||||
case A(1, 2):
|
||||
pass
|
||||
|
||||
case A(1):
|
||||
pass
|
||||
|
||||
case A():
|
||||
pass
|
||||
|
||||
case B(1, 2):
|
||||
pass
|
||||
|
||||
# This should generate an error because B accepts only
|
||||
# two positional patterns.
|
||||
case B(1, 2, 3):
|
||||
pass
|
||||
|
||||
# This should generate an error because B accepts only
|
||||
# two positional patterns.
|
||||
case C(1, 2, 3):
|
||||
pass
|
||||
|
||||
case D(1):
|
||||
pass
|
||||
|
||||
# This should generate an error because D accepts only
|
||||
# one positional pattern.
|
||||
case D(1, 2):
|
||||
pass
|
||||
|
||||
case int(1):
|
||||
pass
|
||||
|
||||
# This should generate an error because int accepts only
|
||||
# one positional pattern.
|
||||
case int(1, 2):
|
||||
pass
|
|
@ -1248,6 +1248,14 @@ test('MatchClass4', () => {
|
|||
TestUtils.validateResults(analysisResults, 0);
|
||||
});
|
||||
|
||||
test('MatchClass5', () => {
|
||||
const configOptions = new ConfigOptions('.');
|
||||
|
||||
configOptions.defaultPythonVersion = PythonVersion.V3_10;
|
||||
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['matchClass5.py'], configOptions);
|
||||
TestUtils.validateResults(analysisResults, 5);
|
||||
});
|
||||
|
||||
test('MatchValue1', () => {
|
||||
const configOptions = new ConfigOptions('.');
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user