mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
PY-75759 Implement inspections for allowed type variable default values, scoping and type vars following TypeVarTuple
GitOrigin-RevId: 455cdff457a24523fde47947c1ee5d3bd830d07d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f05119cead
commit
a7d5a2deea
@@ -1136,6 +1136,12 @@ INSP.type.hints.illegal.literal.parameter='Literal' may be parameterized with li
|
||||
INSP.type.hints.parameters.to.generic.must.all.be.type.variables=Parameters to 'Generic[...]' must all be type variables
|
||||
INSP.type.hints.parameters.to.generic.must.all.be.unique=Parameters to 'Generic[...]' must all be unique
|
||||
INSP.type.hints.non.default.type.vars.cannot.follow.defaults=Non-default TypeVars cannot follow ones with defaults
|
||||
INSP.type.hints.default.type.var.cannot.follow.type.var.tuple=TypeVar with a default value cannot follow TypeVarTuple
|
||||
INSP.type.hints.default.type.refers.to.type.var.out.of.scope=Type parameter has a default type that refers to one or more type variables that are out of scope
|
||||
INSP.type.hints.default.type.of.type.var.tuple.must.be.unpacked=Default type of TypeVarTuple must be unpacked
|
||||
INSP.type.hints.default.type.of.param.spec.must.be.param.spec.or.list.of.types=Default type of ParamSpec must be a ParamSpec type or a list of types
|
||||
INSP.type.hints.default.type.must.be.type.expression=Default type must be a type expression
|
||||
INSP.type.hints.default.type.is.out.of.scope=Default type of type parameter ''{0}'' refers to a type variable that is out of scope
|
||||
INSP.type.hints.illegal.callable.format='Callable' must be used as 'Callable[[arg, ...], result]'
|
||||
INSP.type.hints.illegal.first.parameter='Callable' first parameter must be a parameter expression
|
||||
INSP.type.hints.parameters.to.generic.types.must.be.types=Parameters to generic types must be types
|
||||
|
||||
@@ -121,8 +121,8 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<
|
||||
|
||||
public static final Set<String> TYPE_DICT_QUALIFIERS = Set.of(REQUIRED, REQUIRED_EXT, NOT_REQUIRED, NOT_REQUIRED_EXT, READONLY, READONLY_EXT);
|
||||
|
||||
private static final String UNPACK = "typing.Unpack";
|
||||
private static final String UNPACK_EXT = "typing_extensions.Unpack";
|
||||
public static final String UNPACK = "typing.Unpack";
|
||||
public static final String UNPACK_EXT = "typing_extensions.Unpack";
|
||||
|
||||
public static final String SELF = "typing.Self";
|
||||
public static final String SELF_EXT = "typing_extensions.Self";
|
||||
|
||||
@@ -9,10 +9,12 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.jetbrains.python.PyPsiBundle
|
||||
import com.jetbrains.python.ast.PyAstTypeParameter
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider
|
||||
import com.jetbrains.python.psi.*
|
||||
import com.jetbrains.python.psi.types.PyClassLikeType
|
||||
import com.jetbrains.python.psi.types.PyTypeParameterType
|
||||
import com.jetbrains.python.psi.types.PyTypeVarType
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
|
||||
@@ -46,15 +48,22 @@ class PyNewStyleGenericSyntaxInspection : PyInspection() {
|
||||
override fun visitPyTypeParameterList(node: PyTypeParameterList) {
|
||||
val typeParameters = node.typeParameters
|
||||
var lastIsDefault = false
|
||||
var lastIsTypeVarTuple = false
|
||||
for (typeParameter in typeParameters) {
|
||||
if (typeParameter.defaultExpressionText != null) {
|
||||
lastIsDefault = true
|
||||
if (lastIsTypeVarTuple && typeParameter.kind == PyAstTypeParameter.Kind.TypeVar) {
|
||||
registerProblem(typeParameter,
|
||||
PyPsiBundle.message("INSP.type.hints.default.type.var.cannot.follow.type.var.tuple"),
|
||||
ProblemHighlightType.GENERIC_ERROR)
|
||||
}
|
||||
}
|
||||
else if (lastIsDefault) {
|
||||
registerProblem(typeParameter,
|
||||
PyPsiBundle.message("INSP.type.hints.non.default.type.vars.cannot.follow.defaults"),
|
||||
ProblemHighlightType.GENERIC_ERROR)
|
||||
}
|
||||
lastIsTypeVarTuple = typeParameter.kind == PyAstTypeParameter.Kind.TypeVarTuple
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +136,7 @@ class PyNewStyleGenericSyntaxInspection : PyInspection() {
|
||||
PsiTreeUtil.findChildrenOfAnyType(element, false, PyReferenceExpression::class.java)
|
||||
.associateWith { Ref.deref(PyTypingTypeProvider.getType(it, myTypeEvalContext)) }
|
||||
// if declarationElement a.k.a target expression is null then it most likely resolves to type parameter
|
||||
.filter { (_, v) -> v is PyTypeVarType
|
||||
.filter { (_, v) -> v is PyTypeParameterType
|
||||
&& v.declarationElement is PyTargetExpression
|
||||
&& ScopeUtil.getScopeOwner(v.declarationElement) !is PyTypeAliasStatement }
|
||||
.forEach { (k, _) ->
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.intellij.modcommand.ModPsiUpdater
|
||||
import com.intellij.modcommand.PsiUpdateModCommandQuickFix
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
@@ -22,6 +23,7 @@ import com.jetbrains.python.PyNames
|
||||
import com.jetbrains.python.PyPsiBundle
|
||||
import com.jetbrains.python.PyTokenTypes
|
||||
import com.jetbrains.python.ast.PyAstFunction
|
||||
import com.jetbrains.python.ast.PyAstTypeParameter
|
||||
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache
|
||||
import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner
|
||||
@@ -111,6 +113,16 @@ class PyTypeHintsInspection : PyInspection() {
|
||||
checkParameterizedBuiltins(node)
|
||||
}
|
||||
|
||||
override fun visitPyTypeParameter(typeParameter: PyTypeParameter) {
|
||||
val defaultExpression = typeParameter.defaultExpression
|
||||
if (defaultExpression == null) return
|
||||
when(typeParameter.kind) {
|
||||
PyAstTypeParameter.Kind.TypeVar -> checkTypeVarDefaultType(defaultExpression, typeParameter)
|
||||
PyAstTypeParameter.Kind.ParamSpec -> checkParamSpecDefaultValue(defaultExpression, typeParameter)
|
||||
PyAstTypeParameter.Kind.TypeVarTuple -> checkTypeVarTupleDefaultValue(defaultExpression, typeParameter)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkParameterizedBuiltins(node: PySubscriptionExpression) {
|
||||
if (LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON39)) return
|
||||
|
||||
@@ -327,6 +339,10 @@ class PyTypeHintsInspection : PyInspection() {
|
||||
PyPsiBundle.message("INSP.type.hints.paramspec.expects.string.literal.as.first.argument"),
|
||||
PyPsiBundle.message("INSP.type.hints.argument.to.paramspec.must.be.string.equal.to.variable.name"))
|
||||
}
|
||||
|
||||
if (name == "default" && argument != null) {
|
||||
checkParamSpecDefaultValue(argument, typeParameter = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,6 +353,9 @@ class PyTypeHintsInspection : PyInspection() {
|
||||
PyPsiBundle.message("INSP.type.hints.typevar.tuple.expects.string.literal.as.first.argument"),
|
||||
PyPsiBundle.message("INSP.type.hints.argument.to.typevar.tuple.must.be.string.equal.to.variable.name"))
|
||||
}
|
||||
if (name == "default" && argument != null) {
|
||||
checkTypeVarTupleDefaultValue(argument, typeParameter = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,6 +371,7 @@ class PyTypeHintsInspection : PyInspection() {
|
||||
var covariant = false
|
||||
var contravariant = false
|
||||
var bound: PyExpression? = null
|
||||
var default: PyExpression? = null
|
||||
val constraints = mutableListOf<PyExpression?>()
|
||||
|
||||
processMatchedArgument(call) { name, argument ->
|
||||
@@ -363,6 +383,7 @@ class PyTypeHintsInspection : PyInspection() {
|
||||
"covariant" -> covariant = PyEvaluator.evaluateAsBoolean(argument, false)
|
||||
"contravariant" -> contravariant = PyEvaluator.evaluateAsBoolean(argument, false)
|
||||
"bound" -> bound = argument
|
||||
"default" -> default = argument
|
||||
"constraints" -> constraints.add(argument)
|
||||
}
|
||||
}
|
||||
@@ -382,6 +403,10 @@ class PyTypeHintsInspection : PyInspection() {
|
||||
ProblemHighlightType.GENERIC_ERROR)
|
||||
}
|
||||
|
||||
default?.let { checkIsNotLiteral(it) }
|
||||
|
||||
// TODO match bounds and constraints
|
||||
|
||||
constraints.asSequence().plus(bound).forEach {
|
||||
if (it != null) {
|
||||
val type = PyTypingTypeProvider.getType(it, myTypeEvalContext)?.get()
|
||||
@@ -398,6 +423,60 @@ class PyTypeHintsInspection : PyInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkTypeVarDefaultType(defaultExpression: PyExpression, typeParameter: PyTypeParameter?) {
|
||||
checkIsNotLiteral(defaultExpression)
|
||||
checkDefaultIsInScope(defaultExpression, typeParameter)
|
||||
}
|
||||
|
||||
private fun checkIsNotLiteral(expression: PyExpression) {
|
||||
if (PyTypingTypeProvider.getType(expression, myTypeEvalContext) == null) {
|
||||
registerProblem(expression, PyPsiBundle.message("INSP.type.hints.default.type.must.be.type.expression"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkTypeVarTupleDefaultValue(defaultExpression: PyExpression, typeParameter: PyTypeParameter?) {
|
||||
if ((typeParameter != null && defaultExpression is PyStarExpression) || defaultExpression is PySubscriptionExpression) {
|
||||
val type = Ref.deref(PyTypingTypeProvider.getType(defaultExpression, myTypeEvalContext))
|
||||
if (type is PyUnpackedTupleType || type is PyTypeVarTupleType) {
|
||||
checkDefaultIsInScope(defaultExpression, typeParameter)
|
||||
return
|
||||
}
|
||||
}
|
||||
registerProblem(defaultExpression, PyPsiBundle.message("INSP.type.hints.default.type.of.type.var.tuple.must.be.unpacked"))
|
||||
}
|
||||
|
||||
private fun checkParamSpecDefaultValue(defaultExpression: PyExpression, typeParameter: PyTypeParameter?) {
|
||||
if (defaultExpression is PyNoneLiteralExpression && defaultExpression.isEllipsis) return
|
||||
if (defaultExpression is PyListLiteralExpression) {
|
||||
defaultExpression.elements.forEach {
|
||||
checkIsNotLiteral(it)
|
||||
checkDefaultIsInScope(it, typeParameter)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (defaultExpression is PyReferenceExpression) {
|
||||
val defaultType = Ref.deref(PyTypingTypeProvider.getType(defaultExpression, myTypeEvalContext))
|
||||
if (defaultType !is PyParamSpecType) {
|
||||
registerProblem(defaultExpression, PyPsiBundle.message("INSP.type.hints.default.type.of.param.spec.must.be.param.spec.or.list.of.types"))
|
||||
}
|
||||
checkDefaultIsInScope(defaultExpression, typeParameter)
|
||||
return
|
||||
}
|
||||
registerProblem(defaultExpression, PyPsiBundle.message("INSP.type.hints.default.type.of.param.spec.must.be.param.spec.or.list.of.types"))
|
||||
}
|
||||
|
||||
private fun checkDefaultIsInScope(expression: PyExpression?, parent: PsiElement?) {
|
||||
if (parent is PyTypeParameter && expression != null) {
|
||||
val defaultOutOfScope =
|
||||
PyTypeChecker.collectGenerics(Ref.deref(PyTypingTypeProvider.getType(expression, myTypeEvalContext)), myTypeEvalContext)
|
||||
.allTypeParameters
|
||||
.any { typeParam -> typeParam.declarationElement !is PyTypeParameter }
|
||||
if (defaultOutOfScope) {
|
||||
registerProblem(expression, PyPsiBundle.message("INSP.type.hints.default.type.is.out.of.scope", parent.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkNameIsTheSameAsTarget(argument: PyExpression?, target: PyExpression?,
|
||||
@InspectionMessage notStringLiteralMessage: String,
|
||||
@InspectionMessage notEqualMessage: String) {
|
||||
@@ -793,35 +872,57 @@ class PyTypeHintsInspection : PyInspection() {
|
||||
val parameters = (index as? PyTupleExpression)?.elements ?: arrayOf(index)
|
||||
val genericParameters = mutableSetOf<PsiElement>()
|
||||
var lastIsDefault = false
|
||||
var lastIsTypeVarTuple = false
|
||||
val processedGenerics = mutableSetOf<PyType>()
|
||||
|
||||
parameters.forEach {
|
||||
if (it !is PyReferenceExpression && it !is PyStarExpression && it !is PySubscriptionExpression) {
|
||||
registerProblem(it, PyPsiBundle.message("INSP.type.hints.parameters.to.generic.must.all.be.type.variables"),
|
||||
ProblemHighlightType.GENERIC_ERROR)
|
||||
}
|
||||
else {
|
||||
val type = myTypeEvalContext.getType(it)
|
||||
val expression = if (it is PyStarExpression) it.expression else it
|
||||
|
||||
if (type != null) {
|
||||
if (type is PyTypeVarType || isParamSpecOrConcatenate(it, myTypeEvalContext)) {
|
||||
if (it is PyReferenceExpression && !genericParameters.addAll(multiFollowAssignmentsChain(it))) {
|
||||
registerProblem(it, PyPsiBundle.message("INSP.type.hints.parameters.to.generic.must.all.be.unique"),
|
||||
if (expression != null) {
|
||||
val type = myTypeEvalContext.getType(expression)
|
||||
|
||||
if (type != null) {
|
||||
if (type is PyTypeVarType || isParamSpecOrConcatenate(it, myTypeEvalContext) || type is PyTypeVarTupleType) {
|
||||
if (it is PyReferenceExpression && !genericParameters.addAll(multiFollowAssignmentsChain(it))) {
|
||||
registerProblem(it, PyPsiBundle.message("INSP.type.hints.parameters.to.generic.must.all.be.unique"),
|
||||
ProblemHighlightType.GENERIC_ERROR)
|
||||
}
|
||||
}
|
||||
else {
|
||||
registerProblem(it, PyPsiBundle.message("INSP.type.hints.parameters.to.generic.must.all.be.type.variables"),
|
||||
ProblemHighlightType.GENERIC_ERROR)
|
||||
}
|
||||
}
|
||||
else {
|
||||
registerProblem(it, PyPsiBundle.message("INSP.type.hints.parameters.to.generic.must.all.be.type.variables"),
|
||||
ProblemHighlightType.GENERIC_ERROR)
|
||||
}
|
||||
|
||||
if (type is PyTypeParameterType) {
|
||||
val typeVarDeclaration = type.declarationElement
|
||||
if (hasDefault(typeVarDeclaration)) {
|
||||
lastIsDefault = true
|
||||
} else if (lastIsDefault) {
|
||||
registerProblem(it,
|
||||
PyPsiBundle.message("INSP.type.hints.non.default.type.vars.cannot.follow.defaults"),
|
||||
ProblemHighlightType.GENERIC_ERROR)
|
||||
if (type is PyTypeParameterType) {
|
||||
val typeVarDeclaration = type.declarationElement
|
||||
val defaultType = type.defaultType
|
||||
if (hasDefault(typeVarDeclaration)) {
|
||||
lastIsDefault = true
|
||||
if (lastIsTypeVarTuple && type is PyTypeVarType) {
|
||||
registerProblem(it,
|
||||
PyPsiBundle.message("INSP.type.hints.default.type.var.cannot.follow.type.var.tuple"),
|
||||
ProblemHighlightType.GENERIC_ERROR)
|
||||
}
|
||||
val genericTypes = PyTypeChecker.collectGenerics(Ref.deref(defaultType), myTypeEvalContext)
|
||||
val defaultOutOfScope = genericTypes.allTypeParameters.firstOrNull { typeVar -> typeVar !in processedGenerics }
|
||||
if (defaultOutOfScope != null) {
|
||||
registerProblem(it,
|
||||
PyPsiBundle.message("INSP.type.hints.default.type.refers.to.type.var.out.of.scope", defaultOutOfScope.name))
|
||||
}
|
||||
}
|
||||
else if (lastIsDefault) {
|
||||
registerProblem(it,
|
||||
PyPsiBundle.message("INSP.type.hints.non.default.type.vars.cannot.follow.defaults"),
|
||||
ProblemHighlightType.GENERIC_ERROR)
|
||||
}
|
||||
processedGenerics.add(type)
|
||||
}
|
||||
lastIsTypeVarTuple = type is PyTypeVarTupleType
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -839,6 +940,9 @@ class PyTypeHintsInspection : PyInspection() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (declarationElement is PyTypeParameter) {
|
||||
return declarationElement.defaultExpressionText != null
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -141,6 +141,17 @@ public class PyNewStyleGenericSyntaxInspectionTest extends PyInspectionTestCase
|
||||
"""));
|
||||
}
|
||||
|
||||
// PY-75759
|
||||
public void testTypeVarCannotFollowTypeVarTuple() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON312,
|
||||
() -> doTestByText("""
|
||||
class ClassA[*Ts, <error descr="TypeVar with a default value cannot follow TypeVarTuple">T = int</error>]: ...
|
||||
class ClassB[*Ts = *tuple[int], <error descr="TypeVar with a default value cannot follow TypeVarTuple">T = int</error>]: ...
|
||||
class ClassC[*Ts, **P = [float, bool]]: ...
|
||||
class ClassD[*Ts, **P]: ...
|
||||
"""));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected @NotNull Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
@@ -1630,6 +1630,158 @@ public class PyTypeHintsInspectionTest extends PyInspectionTestCase {
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-75759
|
||||
public void testTypeVarDefaultsScoping() {
|
||||
doTestByText("""
|
||||
from typing import TypeVar, Generic
|
||||
|
||||
S1 = TypeVar("S1")
|
||||
S2 = TypeVar("S2", default=S1)
|
||||
StepT = TypeVar("StepT", default=int | None)
|
||||
StartT = TypeVar("StartT", default="StopT")
|
||||
StopT = TypeVar("StopT", default=int)
|
||||
|
||||
class slice(Generic[<warning descr="Type parameter has a default type that refers to one or more type variables that are out of scope">StartT</warning>, StopT, StepT]): ...
|
||||
class slice2(Generic[StopT, StartT, StepT]): ...
|
||||
|
||||
class Foo3(Generic[S1]):
|
||||
class Bar2(Generic[<warning descr="Type parameter has a default type that refers to one or more type variables that are out of scope">S2</warning>]): ...
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-75759
|
||||
public void testTypeVarAllowedDefaultValues() {
|
||||
doTestByText("""
|
||||
from typing import TypeVar, Generic
|
||||
|
||||
T = TypeVar("T", default=<warning descr="Default type must be a type expression">3</warning>)
|
||||
T1 = TypeVar("T1", default=<warning descr="Default type must be a type expression">True</warning>)
|
||||
T3 = TypeVar("T3", default="NormalT")
|
||||
NormalT = TypeVar("NormalT")
|
||||
T4 = TypeVar("T4", default=NormalT)
|
||||
T5 = TypeVar("T5", default=list)
|
||||
class Clazz: ...
|
||||
T6 = TypeVar("T6", default=Clazz)
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-75759
|
||||
public void testNewStyleTypeVarAllowedDefaultValues() {
|
||||
doTestByText("""
|
||||
from typing import TypeVar, Generic, ParamSpec, TypeVarTuple
|
||||
T1 = TypeVar("T1")
|
||||
Ts1 = TypeVarTuple("Ts1")
|
||||
P1 = ParamSpec("P1")
|
||||
|
||||
class Clazz[T = int]: ...
|
||||
class Clazz[T = dict[int, str]]: ...
|
||||
class Clazz[T, T1 = T]: ...
|
||||
class Clazz[T = <warning descr="Default type must be a type expression">1</warning>]: ...
|
||||
class Clazz[T = <warning descr="Default type must be a type expression">True</warning>]: ...
|
||||
class Clazz[T = <warning descr="Default type of type parameter 'T' refers to a type variable that is out of scope">T1</warning>]: ...
|
||||
class Clazz[T = <warning descr="Default type of type parameter 'T' refers to a type variable that is out of scope">Ts1</warning>]: ...
|
||||
class Clazz[T = <warning descr="Default type of type parameter 'T' refers to a type variable that is out of scope">P1</warning>]: ...
|
||||
class Clazz[T = <warning descr="Default type of type parameter 'T' refers to a type variable that is out of scope">list[T1]</warning>]: ...
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-75759
|
||||
public void testParamSpecAllowedDefaultValues() {
|
||||
doTestByText("""
|
||||
from typing import ParamSpec, TypeVar
|
||||
T = TypeVar(<warning descr="The argument to 'TypeVar()' must be a string equal to the variable name to which it is assigned">"T1"</warning>)
|
||||
P = ParamSpec(<warning descr="The argument to 'ParamSpec()' must be a string equal to the variable name to which it is assigned">"P1"</warning>)
|
||||
|
||||
P1 = ParamSpec("P1", default=[])
|
||||
P2 = ParamSpec("P2", default=[int, str, None, int | None])
|
||||
P3 = ParamSpec("P3", default=[int, T])
|
||||
P4 = ParamSpec("P4", default=[int])
|
||||
P5 = ParamSpec("P5", default=...)
|
||||
P6 = ParamSpec("P6", default=<warning descr="Default type of ParamSpec must be a ParamSpec type or a list of types">int</warning>)
|
||||
P7 = ParamSpec("P7", default=<warning descr="Default type of ParamSpec must be a ParamSpec type or a list of types">3</warning>)
|
||||
P8 = ParamSpec("P8", default=<warning descr="Default type of ParamSpec must be a ParamSpec type or a list of types">(1, int)</warning>)
|
||||
P9 = ParamSpec("P9", default=P)
|
||||
P10 = ParamSpec("P10", default=[<warning descr="Default type must be a type expression">1</warning>, <warning descr="Default type must be a type expression">2</warning>])
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-75759
|
||||
public void testNewStyleParamSpecAllowedDefaultValues() {
|
||||
doTestByText("""
|
||||
from typing import TypeVar, Generic, ParamSpec, TypeVarTuple
|
||||
T1 = TypeVar("T1")
|
||||
Ts1 = TypeVarTuple("Ts1")
|
||||
P1 = ParamSpec("P1")
|
||||
|
||||
class Clazz[**P = []]: ...
|
||||
class Clazz[**P = [int]]: ...
|
||||
class Clazz[**P = [int, str]]: ...
|
||||
class Clazz[**P = [int, <warning descr="Default type must be a type expression">3</warning>]]: ...
|
||||
class Clazz[**P = [int, <warning descr="Default type must be a type expression">True</warning>]]: ...
|
||||
class Clazz[**P = <warning descr="Default type of ParamSpec must be a ParamSpec type or a list of types">True</warning>]: ...
|
||||
class Clazz[**P = <warning descr="Default type of ParamSpec must be a ParamSpec type or a list of types"><warning descr="Default type of type parameter 'P' refers to a type variable that is out of scope">T1</warning></warning>]: ...
|
||||
class Clazz[**P = [<warning descr="Default type of type parameter 'P' refers to a type variable that is out of scope">T1</warning>]]: ...
|
||||
class Clazz[**P = <warning descr="Default type of type parameter 'P' refers to a type variable that is out of scope">P1</warning>]: ...
|
||||
class Clazz[**P = <warning descr="Default type of ParamSpec must be a ParamSpec type or a list of types"><warning descr="Default type of type parameter 'P' refers to a type variable that is out of scope">Ts1</warning></warning>]: ...
|
||||
class Clazz[**P = [int, <warning descr="Default type of type parameter 'P' refers to a type variable that is out of scope">list[T1]</warning>]]: ...
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-75759
|
||||
public void testTypeVarTupleAllowedDefaultValues() {
|
||||
doTestByText("""
|
||||
from typing import TypeVarTuple, Unpack, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
Ts0 = TypeVarTuple("Ts0")
|
||||
Ts1 = TypeVarTuple("Ts1", default=Unpack[tuple[int]])
|
||||
Ts2 = TypeVarTuple("Ts2", default=<warning descr="Default type of TypeVarTuple must be unpacked">tuple[int]</warning>)
|
||||
Ts3 = TypeVarTuple("Ts3", default=<warning descr="Default type of TypeVarTuple must be unpacked">int</warning>)
|
||||
Ts4 = TypeVarTuple("Ts4", default=Unpack[Ts0])
|
||||
Ts5 = TypeVarTuple("Ts5", default=<warning descr="Default type of TypeVarTuple must be unpacked">Ts0</warning>)
|
||||
Ts6 = TypeVarTuple("Ts6", default=Unpack[tuple[int, ...]])
|
||||
Ts7 = TypeVarTuple("Ts7", default=Unpack[tuple[T, T]])
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-75759
|
||||
public void testNewStyleTypeVarTupleAllowedDefaultValues() {
|
||||
doTestByText("""
|
||||
from typing import TypeVar, Generic, ParamSpec, TypeVarTuple, Unpack
|
||||
T1 = TypeVar("T1")
|
||||
Ts1 = TypeVarTuple("Ts1")
|
||||
P1 = ParamSpec("P1")
|
||||
|
||||
class Clazz[*Ts = <warning descr="Default type of type parameter 'Ts' refers to a type variable that is out of scope">Unpack[tuple[int, T1]]</warning>]: ...
|
||||
class Clazz[*Ts = <warning descr="Default type of TypeVarTuple must be unpacked">1</warning>]: ...
|
||||
class Clazz[*Ts = <warning descr="Default type of TypeVarTuple must be unpacked">True</warning>]: ...
|
||||
class Clazz[*Ts = <warning descr="Default type of TypeVarTuple must be unpacked">tuple[int]</warning>]: ...
|
||||
class Clazz[*Ts = *tuple[int]]: ...
|
||||
class Clazz[*Ts = Unpack[tuple[int]]]: ...
|
||||
class Clazz[*Ts = <warning descr="Default type of TypeVarTuple must be unpacked">T1</warning>]: ...
|
||||
class Clazz[*Ts = <warning descr="Default type of TypeVarTuple must be unpacked">Ts1</warning>]: ...
|
||||
class Clazz[*Ts = <warning descr="Default type of TypeVarTuple must be unpacked">P1</warning>]: ...
|
||||
class Clazz[*Ts = Unpack[tuple[int, ...]]]: ...
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-75759
|
||||
public void testTypeVarCannotFollowTypeVarTuple() {
|
||||
doTestByText("""
|
||||
from typing import TypeVar, Generic, ParamSpec, TypeVarTuple, Unpack
|
||||
T = TypeVar("T", default = int)
|
||||
Ts = TypeVarTuple("Ts")
|
||||
TsDef = TypeVarTuple("TsDef", default = Unpack[tuple[int, int]])
|
||||
P = ParamSpec("P", default = [str, bool])
|
||||
|
||||
class Clazz(Generic[Ts, <error descr="TypeVar with a default value cannot follow TypeVarTuple">T</error>]): ...
|
||||
class Clazz1(Generic[TsDef, <error descr="TypeVar with a default value cannot follow TypeVarTuple">T</error>]): ...
|
||||
class Clazz2(Generic[TsDef, P]): ...
|
||||
class Clazz3(Generic[Ts, P]): ...
|
||||
""");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user