PY-61853 PEP 695 Type Parameter Syntax: Parsing

GitOrigin-RevId: 286b53bb4e69cf1deb58dc75f41652e6a12a3af3
This commit is contained in:
Daniil Kalinin
2023-08-16 17:43:14 +02:00
committed by intellij-monorepo-bot
parent 3ed8555cf1
commit 8b217ed53a
56 changed files with 813 additions and 12 deletions

View File

@@ -44,6 +44,7 @@ public final class PyTokenTypes {
public static final PyElementType IS_KEYWORD = new PyElementType("IS_KEYWORD");
public static final PyElementType LAMBDA_KEYWORD = new PyElementType("LAMBDA_KEYWORD");
public static final PyElementType MATCH_KEYWORD = new PyElementType("MATCH_KEYWORD");
public static final PyElementType TYPE_KEYWORD = new PyElementType("TYPE_KEYWORD");
public static final PyElementType NOT_KEYWORD = new PyElementType("NOT_KEYWORD");
public static final PyElementType OR_KEYWORD = new PyElementType("OR_KEYWORD");
public static final PyElementType PASS_KEYWORD = new PyElementType("PASS_KEYWORD");

View File

@@ -39,7 +39,8 @@ import java.util.Map;
* Represents a class declaration in source.
*/
public interface PyClass extends PsiNameIdentifierOwner, PyCompoundStatement, PyDocStringOwner, StubBasedPsiElement<PyClassStub>,
ScopeOwner, PyDecoratable, PyTypedElement, PyQualifiedNameOwner, PyStatementListContainer, PyWithAncestors {
ScopeOwner, PyDecoratable, PyTypedElement, PyQualifiedNameOwner, PyStatementListContainer, PyWithAncestors,
PyTypeParameterListOwner {
PyClass[] EMPTY_ARRAY = new PyClass[0];
ArrayFactory<PyClass> ARRAY_FACTORY = count -> count == 0 ? EMPTY_ARRAY : new PyClass[count];
@@ -74,7 +75,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyCompoundStatement, Py
* @see #getSuperClassTypes(TypeEvalContext) for the full list of super classes.
* @see #getAncestorTypes(TypeEvalContext) for the full list of ancestors.
*/
PyClass @NotNull [] getSuperClasses(@Nullable TypeEvalContext context);
PyClass @NotNull [] getSuperClasses(@Nullable TypeEvalContext context);
/**
* Returns a PSI element for the super classes list.
@@ -160,8 +161,8 @@ public interface PyClass extends PsiNameIdentifierOwner, PyCompoundStatement, Py
/**
* Finds a property with the specified name in the class or one of its ancestors.
*
* @param name of the property
* @param context type eval (null to use loose context, but you better provide one)
* @param name of the property
* @param context type eval (null to use loose context, but you better provide one)
* @return descriptor of property accessors, or null if such property does not exist.
*/
@Nullable
@@ -203,7 +204,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyCompoundStatement, Py
*
* @param context context to use for this process
* @return list of attrs.
*
* <p>
* TODO: Replace it and {@link #getClassAttributes()} with a single getClassAttributes(@NotNull TypeEvalContext context, boolean inherited)
*/
@NotNull
@@ -256,7 +257,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyCompoundStatement, Py
/**
* @return True iff this and parent are the same or parent is one of our superclasses.
*/
boolean isSubclass(PyClass parent, @Nullable TypeEvalContext context);
boolean isSubclass(PyClass parent, @Nullable TypeEvalContext context);
boolean isSubclass(@NotNull String superClassQName, @Nullable TypeEvalContext context);

View File

@@ -385,4 +385,16 @@ public class PyElementVisitor extends PsiElementVisitor {
public void visitPyCaseClause(@NotNull PyCaseClause node) {
visitPyElement(node);
}
public void visitPyTypeAliasStatement(@NotNull PyTypeAliasStatement node) {
visitPyStatement(node);
}
public void visitPyTypeParameter(@NotNull PyTypeParameter node) {
visitPyElement(node);
}
public void visitPyTypeParameterList(@NotNull PyTypeParameterList node) {
visitPyElement(node);
}
}

View File

@@ -3,7 +3,6 @@ package com.jetbrains.python.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiNameIdentifierOwner;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.StubBasedPsiElement;
import com.intellij.util.ArrayFactory;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
@@ -20,7 +19,7 @@ import java.util.List;
*/
public interface PyFunction extends StubBasedPsiElement<PyFunctionStub>, PsiNameIdentifierOwner, PyCompoundStatement,
PyDecoratable, PyCallable, PyStatementListContainer, PyPossibleClassMember,
ScopeOwner, PyDocStringOwner, PyTypeCommentOwner, PyAnnotationOwner {
ScopeOwner, PyDocStringOwner, PyTypeCommentOwner, PyAnnotationOwner, PyTypeParameterListOwner {
PyFunction[] EMPTY_ARRAY = new PyFunction[0];
ArrayFactory<PyFunction> ARRAY_FACTORY = count -> count == 0 ? EMPTY_ARRAY : new PyFunction[count];

View File

@@ -0,0 +1,14 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.psi;
import com.intellij.psi.PsiNameIdentifierOwner;
import org.jetbrains.annotations.Nullable;
/**
* Represents Type Alias Statement added in <a href="https://peps.python.org/pep-0695/">PEP 695</a>
*/
public interface PyTypeAliasStatement extends PyStatement, PsiNameIdentifierOwner, PyTypeParameterListOwner, PyTypedElement {
@Nullable
PyExpression getTypeExpression();
}

View File

@@ -0,0 +1,19 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiNameIdentifierOwner;
import org.jetbrains.annotations.Nullable;
/**
* Represents a Type Parameter that can be a part of {@link PyTypeParameterList}<br>
* For more information see <a href="https://peps.python.org/pep-0695/">PEP 695</a>
*/
public interface PyTypeParameter extends PyElement, PsiNameIdentifierOwner, PyTypedElement {
@Nullable
PyExpression getBoundExpression();
@Nullable
ASTNode getNameNode();
}

View File

@@ -0,0 +1,18 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.psi;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Represents a list of generic Type Parameters.<br>
* e.g. {@code def foo[T, U](x: T | U): ...}<br>
* where {@code [T, U]} is the list of generic Type Parameters.<br>
* For more information see <a href="https://peps.python.org/pep-0695/">PEP 695</a>
*/
public interface PyTypeParameterList extends PyElement {
@NotNull
List<PyTypeParameter> getTypeParameters();
}

View File

@@ -0,0 +1,17 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.psi;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nullable;
/**
* PSI element that can contain {@link PyTypeParameterList}
* which was added in <a href="https://peps.python.org/pep-0695/">PEP 695</a>
*/
public interface PyTypeParameterListOwner extends PsiElement {
@Nullable
default PyTypeParameterList getTypeParameterList() {
return null;
}
}

View File

@@ -210,6 +210,7 @@ PARSE.expected.number=Number expected
PARSE.expected.case.clause=Case clause expected
PARSE.expected.pattern=Pattern expected
PARSE.expected.name.or.wildcard=Name or '_' expected
PARSE.expected.type.parameter=Type parameter expected
statement.expected.found.0=Statement expected, found {0}
unexpected.indent=Unexpected indent

View File

@@ -55,6 +55,8 @@ public interface PyElementTypes {
PyElementType PRINT_TARGET = new PyElementType("PRINT_TARGET", node -> new PyPrintTargetImpl(node));
PyElementType DECORATOR = new PyElementType("DECORATOR", node -> new PyDecoratorImpl(node));
PyElementType TYPE_PARAMETER = new PyElementType("TYPE_PARAMETER", node -> new PyTypeParameterImpl(node));
PyElementType TYPE_PARAMETER_LIST = new PyElementType("TYPE_PARAMETER_LIST", node -> new PyTypeParameterListImpl(node));
// Statements
PyElementType EXPRESSION_STATEMENT = new PyElementType("EXPRESSION_STATEMENT", node -> new PyExpressionStatementImpl(node));
@@ -171,4 +173,5 @@ public interface PyElementTypes {
PyElementType KEYWORD_PATTERN = new PyElementType("KEYWORD_PATTERN", node -> new PyKeywordPatternImpl(node));
PyElementType OR_PATTERN = new PyElementType("OR_PATTERN", node -> new PyOrPatternImpl(node));
PyElementType AS_PATTERN = new PyElementType("AS_PATTERN", node -> new PyAsPatternImpl(node));
PyElementType TYPE_ALIAS_STATEMENT = new PyElementType("TYPE_ALIAS_STATEMENT", node -> new PyTypeAliasStatementImpl(node));
}

View File

@@ -29,7 +29,8 @@ public class PythonTokenSetContributor extends PythonDialectsTokenSetContributor
ASSERT_STATEMENT, BREAK_STATEMENT, CONTINUE_STATEMENT, DEL_STATEMENT, EXEC_STATEMENT, FOR_STATEMENT,
FROM_IMPORT_STATEMENT, GLOBAL_STATEMENT, IMPORT_STATEMENT, IF_STATEMENT, PASS_STATEMENT,
PRINT_STATEMENT, RAISE_STATEMENT, RETURN_STATEMENT, TRY_EXCEPT_STATEMENT, WITH_STATEMENT,
WHILE_STATEMENT, NONLOCAL_STATEMENT, CLASS_DECLARATION, FUNCTION_DECLARATION, MATCH_STATEMENT, CASE_CLAUSE);
WHILE_STATEMENT, NONLOCAL_STATEMENT, CLASS_DECLARATION, FUNCTION_DECLARATION, MATCH_STATEMENT, CASE_CLAUSE,
TYPE_ALIAS_STATEMENT);
}
@NotNull
@@ -59,7 +60,7 @@ public class PythonTokenSetContributor extends PythonDialectsTokenSetContributor
RAISE_KEYWORD, RETURN_KEYWORD, TRY_KEYWORD, WITH_KEYWORD, WHILE_KEYWORD,
YIELD_KEYWORD,
MATCH_KEYWORD, CASE_KEYWORD,
NONE_KEYWORD, TRUE_KEYWORD, FALSE_KEYWORD, NONLOCAL_KEYWORD, DEBUG_KEYWORD, ASYNC_KEYWORD, AWAIT_KEYWORD);
NONE_KEYWORD, TRUE_KEYWORD, FALSE_KEYWORD, NONLOCAL_KEYWORD, DEBUG_KEYWORD, ASYNC_KEYWORD, AWAIT_KEYWORD, TYPE_KEYWORD);
}
@NotNull

View File

@@ -44,7 +44,8 @@ public class FunctionParsing extends Parsing {
protected void parseFunctionInnards(@NotNull SyntaxTreeBuilder.Marker functionMarker, boolean async) {
myBuilder.advanceLexer();
parseIdentifierOrSkip(PyTokenTypes.LPAR);
parseIdentifierOrSkip(PyTokenTypes.LPAR, PyTokenTypes.LBRACKET);
myContext.getStatementParser().parseTypeParameterList();
parseParameterList();
parseReturnTypeAnnotation();
checkMatches(PyTokenTypes.COLON, message("PARSE.expected.colon"));

View File

@@ -127,6 +127,11 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
parseAsyncStatement(false);
return;
}
if (atToken(PyTokenTypes.IDENTIFIER, TOK_TYPE)) {
if (parseTypeAliasStatement()) {
return;
}
}
if (atToken(PyTokenTypes.IDENTIFIER, TOK_ASYNC)) {
if (parseAsyncStatement(true)) {
@@ -142,6 +147,24 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
parseSimpleStatement();
}
private boolean parseTypeAliasStatement() {
assert atToken(PyTokenTypes.IDENTIFIER, TOK_TYPE);
SyntaxTreeBuilder.Marker mark = myBuilder.mark();
myBuilder.remapCurrentToken(PyTokenTypes.TYPE_KEYWORD);
nextToken();
if (!atToken(PyTokenTypes.IDENTIFIER)) {
mark.rollbackTo();
myBuilder.remapCurrentToken(PyTokenTypes.IDENTIFIER);
return false;
}
parseIdentifierOrSkip(PyTokenTypes.LBRACKET, PyTokenTypes.EQ);
parseTypeParameterList();
checkMatches(PyTokenTypes.EQ, PyPsiBundle.message("PARSE.eq.expected"));
myContext.getExpressionParser().parseExpression();
mark.done(PyElementTypes.TYPE_ALIAS_STATEMENT);
return true;
}
private boolean parseMatchStatement() {
assert atToken(PyTokenTypes.IDENTIFIER, TOK_MATCH);
SyntaxTreeBuilder.Marker mark = myBuilder.mark();
@@ -913,7 +936,8 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
public void parseClassDeclaration(SyntaxTreeBuilder.Marker classMarker) {
assertCurrentToken(PyTokenTypes.CLASS_KEYWORD);
myBuilder.advanceLexer();
parseIdentifierOrSkip(PyTokenTypes.LPAR, PyTokenTypes.COLON);
parseIdentifierOrSkip(PyTokenTypes.LPAR, PyTokenTypes.LBRACKET, PyTokenTypes.COLON);
parseTypeParameterList();
if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
getExpressionParser().parseArgumentList();
}
@@ -1017,6 +1041,52 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
}
}
public void parseTypeParameterList() {
if (atToken(PyTokenTypes.LBRACKET)) {
final SyntaxTreeBuilder.Marker typeParamList = myBuilder.mark();
nextToken();
do {
if (!parseTypeParameter()) {
myBuilder.error(PyPsiBundle.message("PARSE.expected.type.parameter"));
}
if (atToken(PyTokenTypes.COMMA)) {
nextToken();
}
else if (!atToken(PyTokenTypes.RBRACKET)) {
break;
}
}
while (!atToken(PyTokenTypes.RBRACKET));
checkMatches(PyTokenTypes.RBRACKET, PyPsiBundle.message("PARSE.expected.symbols", ",", "]"));
typeParamList.done(PyElementTypes.TYPE_PARAMETER_LIST);
}
}
private boolean parseTypeParameter() {
if (isIdentifier(myBuilder) || atToken(PyTokenTypes.MULT) || atToken(PyTokenTypes.EXP)) {
SyntaxTreeBuilder.Marker typeParamMarker = myBuilder.mark();
if (atAnyOfTokens(PyTokenTypes.MULT, PyTokenTypes.EXP)) {
nextToken();
}
if (!parseIdentifierOrSkip(PyTokenTypes.RBRACKET, PyTokenTypes.COMMA, PyTokenTypes.COLON)) {
typeParamMarker.drop();
return false;
}
if (matchToken(PyTokenTypes.COLON)) {
if (!myContext.getExpressionParser().parseSingleExpression(false)) {
myBuilder.error(PyPsiBundle.message("PARSE.expected.expression"));
}
}
typeParamMarker.done(PyElementTypes.TYPE_PARAMETER);
return true;
}
return false;
}
@Override
public IElementType filter(final IElementType source, final int start, final int end, final CharSequence text) {
return filter(source, start, end, text, true);

View File

@@ -143,6 +143,12 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
return statementList;
}
@Override
@Nullable
public PyTypeParameterList getTypeParameterList() {
return childToPsi(PyElementTypes.TYPE_PARAMETER_LIST);
}
@Override
public PyArgumentList getSuperClassExpressionList() {
final PyArgumentList argList = PsiTreeUtil.getChildOfType(this, PyArgumentList.class);

View File

@@ -64,6 +64,12 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
super(stub, nodeType);
}
@Override
@Nullable
public PyTypeParameterList getTypeParameterList() {
return childToPsi(PyElementTypes.TYPE_PARAMETER_LIST);
}
private class CachedStructuredDocStringProvider implements CachedValueProvider<StructuredDocString> {
@Override
public @Nullable Result<StructuredDocString> compute() {

View File

@@ -0,0 +1,73 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PyTypeAliasStatementImpl extends PyElementImpl implements PyTypeAliasStatement {
public PyTypeAliasStatementImpl(ASTNode astNode) {
super(astNode);
}
@Override
protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyTypeAliasStatement(this);
}
@Override
@Nullable
public PyExpression getTypeExpression() {
PsiElement child = getLastChild();
while (child != null && !(child instanceof PyExpression)) {
if (child instanceof PsiErrorElement) return null;
child = child.getPrevSibling();
}
return (PyExpression)child;
}
@Override
@Nullable
public PyTypeParameterList getTypeParameterList() {
return childToPsi(PyElementTypes.TYPE_PARAMETER_LIST);
}
@Override
@Nullable
public PsiElement getNameIdentifier() {
ASTNode nameNode = getNode().findChildByType(PyTokenTypes.IDENTIFIER);
return nameNode != null ? nameNode.getPsi() : null;
}
@Override
public String getName() {
PsiElement identifier = getNameIdentifier();
return identifier != null ? identifier.getText() : null;
}
@Override
public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
PsiElement identifier = getNameIdentifier();
if (identifier != null) {
ASTNode newName = PyUtil.createNewName(this, name);
ASTNode nameNode = identifier.getNode();
getNode().replaceChild(nameNode, newName);
}
return this;
}
@Override
@Nullable
public PyType getType(@NotNull TypeEvalContext context, TypeEvalContext.@NotNull Key key) {
// TODO
return null;
}
}

View File

@@ -0,0 +1,69 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyTypeParameter;
import com.jetbrains.python.psi.PyUtil;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PyTypeParameterImpl extends PyElementImpl implements PyTypeParameter {
public PyTypeParameterImpl(ASTNode astNode) {
super(astNode);
}
@Override
protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyTypeParameter(this);
}
@Override
@Nullable
public String getName() {
ASTNode nameNode = getNameNode();
return nameNode != null ? nameNode.getText() : null;
}
@Override
@Nullable
public PyExpression getBoundExpression() {
return PsiTreeUtil.getChildOfType(this, PyExpression.class);
}
@Override
@Nullable
public ASTNode getNameNode() {
return getNode().findChildByType(PyTokenTypes.IDENTIFIER);
}
@Override
@Nullable
public PsiElement getNameIdentifier() {
ASTNode nameNode = getNameNode();
return nameNode != null ? nameNode.getPsi() : null;
}
@Override
public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
ASTNode oldNameElement = getNameNode();
if (oldNameElement != null) {
ASTNode nameElement = PyUtil.createNewName(this, name);
getNode().replaceChild(oldNameElement, nameElement);
}
return this;
}
@Override
@Nullable
public PyType getType(@NotNull TypeEvalContext context, TypeEvalContext.@NotNull Key key) {
return null;
}
}

View File

@@ -0,0 +1,28 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.PyTypeParameter;
import com.jetbrains.python.psi.PyTypeParameterList;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class PyTypeParameterListImpl extends PyElementImpl implements PyTypeParameterList {
public PyTypeParameterListImpl(ASTNode astNode) {
super(astNode);
}
@Override
protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyTypeParameterList(this);
}
@Override
@NotNull
public List<PyTypeParameter> getTypeParameters() {
return findChildrenByType(PyElementTypes.TYPE_PARAMETER);
}
}

View File

@@ -33,6 +33,7 @@ public class StarAnnotator extends PyAnnotator {
if (!node.isAssignmentTarget() &&
!allowedUnpacking(node) &&
!(parent instanceof PyParameterTypeList) &&
!(parent instanceof PyTypeParameter) &&
!(parent instanceof PyAnnotation && isVariadicArg(parent.getParent()))) {
getHolder().newAnnotation(HighlightSeverity.ERROR, PyPsiBundle.message("ANN.can.t.use.starred.expression.here")).create();
}

View File

@@ -0,0 +1 @@
type MyType[] = int | str

View File

@@ -0,0 +1,21 @@
PyFile:TypeAliasStatementRecoveryWithEmptyTypeParameterList.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PsiErrorElement:Type parameter expected
<empty list>
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyBinaryExpression
PyReferenceExpression: int
PsiElement(Py:IDENTIFIER)('int')
PsiWhiteSpace(' ')
PsiElement(Py:OR)('|')
PsiWhiteSpace(' ')
PyReferenceExpression: str
PsiElement(Py:IDENTIFIER)('str')

View File

@@ -0,0 +1 @@
type MyType =

View File

@@ -0,0 +1,9 @@
PyFile:TypeAliasStatementRecoveryWithEqSignButNoAssignedType.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiErrorElement:Expression expected
<empty list>

View File

@@ -0,0 +1 @@
type MyType

View File

@@ -0,0 +1,7 @@
PyFile:TypeAliasStatementRecoveryWithNoAssignedType.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PsiErrorElement:'=' expected
<empty list>

View File

@@ -0,0 +1 @@
type MyType[T: str] = List[T]

View File

@@ -0,0 +1,24 @@
PyFile:TypeAliasStatementWithBoundedTypeParameter.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyReferenceExpression: str
PsiElement(Py:IDENTIFIER)('str')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: List
PsiElement(Py:IDENTIFIER)('List')
PsiElement(Py:LBRACKET)('[')
PyReferenceExpression: T
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1 @@
type MyType[T: str, ] = list[T]

View File

@@ -0,0 +1,26 @@
PyFile:TypeAliasStatementWithBoundedTypeParameterAndDanglingComma.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyReferenceExpression: str
PsiElement(Py:IDENTIFIER)('str')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: list
PsiElement(Py:IDENTIFIER)('list')
PsiElement(Py:LBRACKET)('[')
PyReferenceExpression: T
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1 @@
type MyTypeWithParamSpec[**P] = Callable[P, int]

View File

@@ -0,0 +1,26 @@
PyFile:TypeAliasStatementWithParamSpec.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyTypeWithParamSpec')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:EXP)('**')
PsiElement(Py:IDENTIFIER)('P')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: Callable
PsiElement(Py:IDENTIFIER)('Callable')
PsiElement(Py:LBRACKET)('[')
PyTupleExpression
PyReferenceExpression: P
PsiElement(Py:IDENTIFIER)('P')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyReferenceExpression: int
PsiElement(Py:IDENTIFIER)('int')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1 @@
type MyType[T, ] = list[T]

View File

@@ -0,0 +1,22 @@
PyFile:TypeAliasStatementWithTypeParameterAndDanglingComma.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: list
PsiElement(Py:IDENTIFIER)('list')
PsiElement(Py:LBRACKET)('[')
PyReferenceExpression: T
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1 @@
type MyType[T: (int | str)] = list[T]

View File

@@ -0,0 +1,33 @@
PyFile:TypeAliasStatementWithTypeParameterBoundedWithExpression.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyBinaryExpression
PyReferenceExpression: int
PsiElement(Py:IDENTIFIER)('int')
PsiWhiteSpace(' ')
PsiElement(Py:OR)('|')
PsiWhiteSpace(' ')
PyReferenceExpression: str
PsiElement(Py:IDENTIFIER)('str')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: list
PsiElement(Py:IDENTIFIER)('list')
PsiElement(Py:LBRACKET)('[')
PyReferenceExpression: T
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1 @@
type MyType[T, U] = Union[T, U]

View File

@@ -0,0 +1,29 @@
PyFile:TypeAliasStatementWithTypeParameterList.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('U')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: Union
PsiElement(Py:IDENTIFIER)('Union')
PsiElement(Py:LBRACKET)('[')
PyTupleExpression
PyReferenceExpression: T
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyReferenceExpression: U
PsiElement(Py:IDENTIFIER)('U')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1 @@
type MyTypeWithTypeVarTuple[*Ts] = Tuple[*Ts]

View File

@@ -0,0 +1,23 @@
PyFile:TypeAliasStatementWithTypeVarTuple.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyTypeWithTypeVarTuple')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:MULT)('*')
PsiElement(Py:IDENTIFIER)('Ts')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: Tuple
PsiElement(Py:IDENTIFIER)('Tuple')
PsiElement(Py:LBRACKET)('[')
PyStarExpression
PsiElement(Py:MULT)('*')
PyReferenceExpression: Ts
PsiElement(Py:IDENTIFIER)('Ts')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1 @@
type MyType = str

View File

@@ -0,0 +1,10 @@
PyFile:TypeAliasStatementWithoutTypeParameterList.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyReferenceExpression: str
PsiElement(Py:IDENTIFIER)('str')

View File

@@ -0,0 +1 @@
type = 3

View File

@@ -0,0 +1,9 @@
PyFile:TypeKeywordAsIdentifier.py
PyAssignmentStatement
PyTargetExpression: type
PsiElement(Py:IDENTIFIER)('type')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('3')

View File

@@ -0,0 +1 @@
type MyType[T: ] = list[T]

View File

@@ -0,0 +1,24 @@
PyFile:TypeParameterInTypeAliasStatementRecoveryIncompleteBound.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:COLON)(':')
PsiErrorElement:Expression expected
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: list
PsiElement(Py:IDENTIFIER)('list')
PsiElement(Py:LBRACKET)('[')
PyReferenceExpression: T
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1 @@
class Clazz[T]: pass

View File

@@ -0,0 +1,17 @@
PyFile:TypeParameterListInClassDeclaration.py
PyClass: Clazz
PsiElement(Py:CLASS_KEYWORD)('class')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('Clazz')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')
PyArgumentList
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')

View File

@@ -0,0 +1 @@
def foo[T](a): pass

View File

@@ -0,0 +1,20 @@
PyFile:TypeParameterListInFunctionDeclaration.py
PyFunction('foo')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('foo')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')
PyParameterList
PsiElement(Py:LPAR)('(')
PyNamedParameter('a')
PsiElement(Py:IDENTIFIER)('a')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')

View File

@@ -0,0 +1,24 @@
PyFile:TypeParameterListInFunctionDeclarationRecoveryNotClosedRightBracket.py
PyFunction('foo')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('foo')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiErrorElement:',' or ']' expected
<empty list>
PyParameterList
PsiElement(Py:LPAR)('(')
PyNamedParameter('a')
PsiElement(Py:IDENTIFIER)('a')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyExpressionStatement
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')

View File

@@ -0,0 +1 @@
type MyType[T = list[T]

View File

@@ -0,0 +1,21 @@
PyFile:TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracket.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiErrorElement:',' or ']' expected
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: list
PsiElement(Py:IDENTIFIER)('list')
PsiElement(Py:LBRACKET)('[')
PyReferenceExpression: T
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1 @@
type MyType[T,,] = list[T]

View File

@@ -0,0 +1,24 @@
PyFile:TypeParameterListInTypeAliasStatementRecoveryUnexpectedSymbolAfterComma.py
PyTypeAliasStatement
PsiElement(Py:TYPE_KEYWORD)('type')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('MyType')
PyTypeParameterList
PsiElement(Py:LBRACKET)('[')
PyTypeParameter
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:COMMA)(',')
PsiErrorElement:Type parameter expected
<empty list>
PsiElement(Py:COMMA)(',')
PsiElement(Py:RBRACKET)(']')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: list
PsiElement(Py:IDENTIFIER)('list')
PsiElement(Py:LBRACKET)('[')
PyReferenceExpression: T
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')

View File

@@ -1230,6 +1230,80 @@ public class PythonParsingTest extends ParsingTestCase {
doTest(LanguageLevel.getLatest());
}
public void testTypeAliasStatementWithoutTypeParameterList() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeAliasStatementWithTypeParameterList() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeAliasStatementWithTypeVarTuple() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeAliasStatementWithParamSpec() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeAliasStatementWithBoundedTypeParameter() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeAliasStatementWithTypeParameterBoundedWithExpression() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeAliasStatementWithTypeParameterAndDanglingComma() {
// Valid case
doTest(LanguageLevel.PYTHON312);
}
public void testTypeAliasStatementWithBoundedTypeParameterAndDanglingComma() {
// Valid case
doTest(LanguageLevel.PYTHON312);
}
public void testTypeAliasStatementRecoveryWithEmptyTypeParameterList() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeAliasStatementRecoveryWithNoAssignedType() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeAliasStatementRecoveryWithEqSignButNoAssignedType() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeParameterInTypeAliasStatementRecoveryIncompleteBound() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracket() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeParameterListInTypeAliasStatementRecoveryUnexpectedSymbolAfterComma() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeParameterListInFunctionDeclaration() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeParameterListInFunctionDeclarationRecoveryNotClosedRightBracket() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeParameterListInClassDeclaration() {
doTest(LanguageLevel.PYTHON312);
}
public void testTypeKeywordAsIdentifier() {
doTest(LanguageLevel.PYTHON312);
}
public void doTest() {
doTest(LanguageLevel.PYTHON26);
}