mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-34617 Support version check
GitOrigin-RevId: 3318ff79cdcc5ba0ce5e4feb65abad5ad0f4acfa
This commit is contained in:
committed by
intellij-monorepo-bot
parent
16a7fb4b3e
commit
93b9066edf
@@ -11,17 +11,18 @@ if sys.version_info < (3, 10):
|
||||
from _collections_abc import *
|
||||
else:
|
||||
from collections.abc import (
|
||||
Callable,
|
||||
ItemsView,
|
||||
Iterable,
|
||||
Iterator,
|
||||
KeysView,
|
||||
Mapping,
|
||||
MutableMapping,
|
||||
MutableSequence,
|
||||
Reversible,
|
||||
Sequence,
|
||||
ValuesView,
|
||||
Callable as Callable,
|
||||
Hashable as Hashable,
|
||||
ItemsView as ItemsView,
|
||||
Iterable as Iterable,
|
||||
Iterator as Iterator,
|
||||
KeysView as KeysView,
|
||||
Mapping as Mapping,
|
||||
MutableMapping as MutableMapping,
|
||||
MutableSequence as MutableSequence,
|
||||
Reversible as Reversible,
|
||||
Sequence as Sequence,
|
||||
ValuesView as ValuesView,
|
||||
)
|
||||
|
||||
__all__ = ["ChainMap", "Counter", "OrderedDict", "UserDict", "UserList", "UserString", "defaultdict", "deque", "namedtuple"]
|
||||
|
||||
@@ -381,6 +381,7 @@ else:
|
||||
def __ror__(self, other: Any) -> _SpecialForm: ...
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
from collections.abc import Buffer as Buffer
|
||||
from types import get_original_bases as get_original_bases
|
||||
from typing import TypeAliasType as TypeAliasType, override as override
|
||||
else:
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
package com.jetbrains.python.ast;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayFactory;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
@@ -11,9 +14,9 @@ import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.PythonDialectsTokenSetProvider;
|
||||
import com.jetbrains.python.ast.impl.PyUtilCore;
|
||||
import com.jetbrains.python.ast.controlFlow.AstScopeOwner;
|
||||
import com.jetbrains.python.ast.docstring.DocStringUtilCore;
|
||||
import com.jetbrains.python.ast.impl.PyUtilCore;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -28,7 +31,7 @@ import java.util.Optional;
|
||||
@ApiStatus.Experimental
|
||||
public interface PyAstFunction extends PsiNameIdentifierOwner, PyAstCompoundStatement,
|
||||
PyAstDecoratable, PyAstCallable, PyAstStatementListContainer, PyAstPossibleClassMember,
|
||||
AstScopeOwner, PyAstDocStringOwner, PyAstTypeCommentOwner, PyAstAnnotationOwner, PyAstTypeParameterListOwner{
|
||||
AstScopeOwner, PyAstDocStringOwner, PyAstTypeCommentOwner, PyAstAnnotationOwner, PyAstTypeParameterListOwner {
|
||||
|
||||
PyAstFunction[] EMPTY_ARRAY = new PyAstFunction[0];
|
||||
ArrayFactory<PyAstFunction> ARRAY_FACTORY = count -> count == 0 ? EMPTY_ARRAY : new PyAstFunction[count];
|
||||
@@ -231,7 +234,7 @@ public interface PyAstFunction extends PsiNameIdentifierOwner, PyAstCompoundStat
|
||||
@Override
|
||||
@Nullable
|
||||
default PyAstClass getContainingClass() {
|
||||
final PsiElement parent = PsiTreeUtil.getParentOfType(this, StubBasedPsiElement.class);
|
||||
final PsiElement parent = PsiTreeUtil.getParentOfType(this, AstScopeOwner.class);
|
||||
if (parent instanceof PyAstClass) {
|
||||
return (PyAstClass)parent;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -80,43 +79,6 @@ public final class PyPsiUtilsCore {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@NotNull
|
||||
public static <T extends PyAstElement> List<T> collectChildren(@NotNull PyAstFile pyFile, @NotNull Class<T> elementType) {
|
||||
final List<T> result = new ArrayList<>();
|
||||
pyFile.acceptChildren(new TopLevelVisitor() {
|
||||
@Override
|
||||
protected void checkAddElement(PsiElement node) {
|
||||
if (elementType.isInstance(node)) {
|
||||
result.add(elementType.cast(node));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyStatement(@NotNull PyAstStatement node) {
|
||||
if (PyAstStatement.class.isAssignableFrom(elementType) && !(node instanceof PyAstCompoundStatement)) {
|
||||
checkAddElement(node);
|
||||
return;
|
||||
}
|
||||
super.visitPyStatement(node);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@NotNull
|
||||
public static List<PsiElement> collectAllChildren(PsiElement e) {
|
||||
final List<PsiElement> result = new ArrayList<>();
|
||||
e.acceptChildren(new TopLevelVisitor() {
|
||||
@Override
|
||||
protected void checkAddElement(PsiElement node) {
|
||||
result.add(node);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first non-whitespace sibling following the given element but within its line boundaries.
|
||||
*/
|
||||
@@ -135,26 +97,6 @@ public final class PyPsiUtilsCore {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static abstract class TopLevelVisitor extends PyAstRecursiveElementVisitor {
|
||||
@Override
|
||||
public void visitPyElement(final @NotNull PyAstElement node) {
|
||||
super.visitPyElement(node);
|
||||
checkAddElement(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyClass(final @NotNull PyAstClass node) {
|
||||
checkAddElement(node); // do not recurse into functions
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyFunction(final @NotNull PyAstFunction node) {
|
||||
checkAddElement(node); // do not recurse into classes
|
||||
}
|
||||
|
||||
protected abstract void checkAddElement(PsiElement node);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String strValue(@Nullable PyAstExpression expression) {
|
||||
return expression instanceof PyAstStringLiteralExpression ? ((PyAstStringLiteralExpression)expression).getStringValue() : null;
|
||||
@@ -198,4 +140,12 @@ public final class PyPsiUtilsCore {
|
||||
}
|
||||
PsiUtilCore.ensureValid(element);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PyAstExpression flattenParens(@Nullable PyAstExpression expr) {
|
||||
while (expr instanceof PyAstParenthesizedExpression) {
|
||||
expr = ((PyAstParenthesizedExpression)expr).getContainedExpression();
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Assorted utility methods for Python code insight.
|
||||
*
|
||||
* These methods don't depend on the Python runtime.
|
||||
*
|
||||
* @see PyPsiUtilsCore for utilities used in Python PSI API
|
||||
@@ -99,11 +98,13 @@ public final class PyUtilCore {
|
||||
|
||||
public static boolean isTopLevel(@NotNull PsiElement element) {
|
||||
if (element instanceof StubBasedPsiElement) {
|
||||
final StubElement stub = ((StubBasedPsiElement<?>)element).getStub();
|
||||
final StubElement<?> stub = ((StubBasedPsiElement<?>)element).getStub();
|
||||
if (stub != null) {
|
||||
final StubElement parentStub = stub.getParentStub();
|
||||
if (parentStub != null) {
|
||||
return parentStub.getPsi() instanceof PsiFile;
|
||||
for (StubElement<?> parentStub = stub.getParentStub(); parentStub != null; parentStub = parentStub.getParentStub()) {
|
||||
PsiElement psi = parentStub.getPsi();
|
||||
if (!(psi instanceof PyAstIfPart || psi instanceof PyAstElsePart)) {
|
||||
return psi instanceof PsiFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1918,4 +1918,80 @@ public abstract class PyCommonResolveTest extends PyCommonResolveTestCase {
|
||||
PyFunction containingMethod = assertInstanceOf(ScopeUtil.getScopeOwner(classAttr), PyFunction.class);
|
||||
assertEquals("next", containingMethod.getName());
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testFileAttributeMatchingVersionCheck() {
|
||||
myFixture.copyDirectoryToProject("resolve/FileAttributeUnderVersionCheck", "");
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON310, () -> {
|
||||
myFixture.configureByText(
|
||||
PythonFileType.INSTANCE,
|
||||
"""
|
||||
import mod
|
||||
mod.foo
|
||||
<ref>"""
|
||||
);
|
||||
final PsiElement element = PyCommonResolveTestCase.findReferenceByMarker(myFixture.getFile()).resolve();
|
||||
assertResolveResult(element, PyTargetExpression.class, "foo", "mod.py");
|
||||
});
|
||||
assertFilesNotParsed();
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testFileAttributeNotMatchingVersionCheck() {
|
||||
myFixture.copyDirectoryToProject("resolve/FileAttributeUnderVersionCheck", "");
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON310, () -> {
|
||||
myFixture.configureByText(
|
||||
PythonFileType.INSTANCE,
|
||||
"""
|
||||
import mod
|
||||
mod.bar
|
||||
<ref>"""
|
||||
);
|
||||
final PsiElement element = PyCommonResolveTestCase.findReferenceByMarker(myFixture.getFile()).resolve();
|
||||
assertNull(element);
|
||||
});
|
||||
assertFilesNotParsed();
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testClassAttributeMatchingVersionCheck() {
|
||||
myFixture.copyDirectoryToProject("resolve/ClassAttributeUnderVersionCheck", "");
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON27, () -> {
|
||||
myFixture.configureByText(
|
||||
PythonFileType.INSTANCE,
|
||||
"""
|
||||
from mod import MyClass
|
||||
m = MyClass()
|
||||
m.buz()
|
||||
<ref>"""
|
||||
);
|
||||
final PsiElement element = PyCommonResolveTestCase.findReferenceByMarker(myFixture.getFile()).resolve();
|
||||
assertResolveResult(element, PyFunction.class, "buz", "mod.py");
|
||||
});
|
||||
assertFilesNotParsed();
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testClassAttributeNotMatchingVersionCheck() {
|
||||
myFixture.copyDirectoryToProject("resolve/ClassAttributeUnderVersionCheck", "");
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON27, () -> {
|
||||
myFixture.configureByText(
|
||||
PythonFileType.INSTANCE,
|
||||
"""
|
||||
from mod import MyClass
|
||||
m = MyClass()
|
||||
m.foo()
|
||||
<ref>"""
|
||||
);
|
||||
final PsiElement element = PyCommonResolveTestCase.findReferenceByMarker(myFixture.getFile()).resolve();
|
||||
assertNull(element);
|
||||
});
|
||||
assertFilesNotParsed();
|
||||
}
|
||||
|
||||
private void assertFilesNotParsed() {
|
||||
final PsiFile file = myFixture.getFile();
|
||||
assertProjectFilesNotParsed(file);
|
||||
assertSdkRootsNotParsed(file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2101,6 +2101,33 @@ public abstract class PythonCommonCompletionTest extends PythonCommonTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testVersionCheckAtFileLevel() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON27, () -> {
|
||||
List<String> suggested = doTestByFile();
|
||||
assertContainsElements(suggested, "attr0", "attr3", "f0", "f3", "MyClass0", "MyClass3");
|
||||
assertDoesntContain(suggested, "attr1", "attr2", "f1", "f2", "MyClass1", "MyClass2");
|
||||
});
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testVersionCheckAtClassLevel() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON25, () -> {
|
||||
List<String> suggested = doTestByFile();
|
||||
assertContainsElements(suggested, "attr0", "attr2", "f0", "f2", "MyClass0", "MyClass2");
|
||||
assertDoesntContain(suggested, "attr1", "attr3", "f1", "f3", "MyClass1", "MyClass3");
|
||||
});
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testVersionCheckInClassInsideMethod() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON310, () -> {
|
||||
List<String> suggested = doTestByFile();
|
||||
assertContainsElements(suggested, "f0", "f1");
|
||||
assertDoesntContain(suggested, "f2", "f3");
|
||||
});
|
||||
}
|
||||
|
||||
private void doTestHasattrContributor(String[] inList, String[] notInList) {
|
||||
doTestHasattrContributor("hasattrCompletion/" + getTestName(true) + ".py", inList, notInList);
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@ public interface PyElementTypes {
|
||||
TokenSet STATEMENT_LISTS = TokenSet.create(STATEMENT_LIST);
|
||||
|
||||
// Parts
|
||||
PyElementType IF_PART_IF = new PyElementType("IF_IF", Companion.getINSTANCE().getIfPartIfConstructor());
|
||||
PyElementType IF_PART_ELIF = new PyElementType("IF_ELIF", Companion.getINSTANCE().getIfPartElifConstructor());
|
||||
IElementType IF_PART_IF = Companion.getINSTANCE().getIfPartIf();
|
||||
IElementType IF_PART_ELIF = Companion.getINSTANCE().getIfPartElif();
|
||||
|
||||
PyElementType FOR_PART = new PyElementType("FOR_PART", Companion.getINSTANCE().getForPartConstructor());
|
||||
PyElementType WHILE_PART = new PyElementType("WHILE_PART", Companion.getINSTANCE().getWhilePartConstructor());
|
||||
@@ -109,7 +109,7 @@ public interface PyElementTypes {
|
||||
PyElementType TRY_PART = new PyElementType("TRY_PART", Companion.getINSTANCE().getTryPartConstructor());
|
||||
PyElementType FINALLY_PART = new PyElementType("FINALLY_PART", Companion.getINSTANCE().getFinallyPartConstructor());
|
||||
|
||||
PyElementType ELSE_PART = new PyElementType("ELSE_PART", Companion.getINSTANCE().getElsePartConstructor());
|
||||
IElementType ELSE_PART = Companion.getINSTANCE().getElsePart();
|
||||
|
||||
TokenSet PARTS = TokenSet.create(IF_PART_IF, IF_PART_ELIF, FOR_PART, WHILE_PART, TRY_PART, FINALLY_PART, ELSE_PART, EXCEPT_PART);
|
||||
TokenSet ELIFS = TokenSet.create(IF_PART_ELIF);
|
||||
|
||||
@@ -31,6 +31,9 @@ abstract class PyElementTypesFacade {
|
||||
abstract val typeParameter: IElementType
|
||||
abstract val typeParameterList: IElementType
|
||||
abstract val typeAliasStatement: IElementType
|
||||
abstract val ifPartIf: IElementType
|
||||
abstract val ifPartElif: IElementType
|
||||
abstract val elsePart: IElementType
|
||||
|
||||
// reparseable elements
|
||||
abstract val statementList: IElementType
|
||||
@@ -94,13 +97,10 @@ abstract class PyElementTypesFacade {
|
||||
abstract val setLiteralExpressionConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val setCompExpressionConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val dictCompExpressionConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val ifPartIfConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val ifPartElifConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val forPartConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val whilePartConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val tryPartConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val finallyPartConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val elsePartConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val fStringNodeConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val fStringFragmentConstructor: Function<in ASTNode, out PsiElement>
|
||||
abstract val fStringFragmentFormatPartConstructor: Function<in ASTNode, out PsiElement>
|
||||
|
||||
@@ -17,5 +17,6 @@
|
||||
<orderEntry type="module" module-name="intellij.python.parser" exported="" />
|
||||
<orderEntry type="module" module-name="intellij.python.ast" exported="" />
|
||||
<orderEntry type="module" module-name="intellij.python.syntax.core" />
|
||||
<orderEntry type="library" name="kotlin-stdlib" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import com.intellij.psi.StubBasedPsiElement;
|
||||
import com.jetbrains.python.ast.PyAstElsePart;
|
||||
import com.jetbrains.python.psi.stubs.PyElsePartStub;
|
||||
|
||||
/**
|
||||
* The 'else:' part of various compound statements.
|
||||
*/
|
||||
public interface PyElsePart extends PyAstElsePart, PyStatementPart {
|
||||
public interface PyElsePart extends PyAstElsePart, PyStatementPart, StubBasedPsiElement<PyElsePartStub> {
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2000-2024 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.StubBasedPsiElement;
|
||||
import com.jetbrains.python.ast.PyAstIfPartElif;
|
||||
import com.jetbrains.python.psi.stubs.PyIfPartElifStub;
|
||||
|
||||
public interface PyIfPartElif extends PyAstIfPartElif, PyIfPart {
|
||||
public interface PyIfPartElif extends PyAstIfPartElif, PyIfPart, StubBasedPsiElement<PyIfPartElifStub> {
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2000-2024 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.StubBasedPsiElement;
|
||||
import com.jetbrains.python.ast.PyAstIfPartIf;
|
||||
import com.jetbrains.python.psi.stubs.PyIfPartIfStub;
|
||||
|
||||
public interface PyIfPartIf extends PyAstIfPartIf, PyIfPart {
|
||||
public interface PyIfPartIf extends PyAstIfPartIf, PyIfPart, StubBasedPsiElement<PyIfPartIfStub> {
|
||||
}
|
||||
|
||||
@@ -16,10 +16,15 @@ import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.ast.impl.PyPsiUtilsCore;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.stubs.PyElsePartStub;
|
||||
import com.jetbrains.python.psi.stubs.PyIfPartElifStub;
|
||||
import com.jetbrains.python.psi.stubs.PyIfPartIfStub;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -408,40 +413,6 @@ public final class PyPsiUtils {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static <T extends PyElement> List<T> collectStubChildren(@NotNull PyFile pyFile,
|
||||
@Nullable StubElement<?> stub,
|
||||
@NotNull Class<T> elementType) {
|
||||
if (stub != null) {
|
||||
final List<T> result = new ArrayList<>();
|
||||
final List<StubElement<?>> children = stub.getChildrenStubs();
|
||||
for (StubElement<?> child : children) {
|
||||
PsiElement childPsi = child.getPsi();
|
||||
if (elementType.isInstance(childPsi)) {
|
||||
result.add(elementType.cast(childPsi));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return PyPsiUtilsCore.collectChildren(pyFile, elementType);
|
||||
}
|
||||
}
|
||||
|
||||
static List<PsiElement> collectAllStubChildren(PsiElement e, StubElement<?> stub) {
|
||||
if (stub != null) {
|
||||
final List<PsiElement> result = new ArrayList<>();
|
||||
final List<StubElement<?>> children = stub.getChildrenStubs();
|
||||
for (StubElement<?> child : children) {
|
||||
result.add(child.getPsi());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return PyPsiUtilsCore.collectAllChildren(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static int findArgumentIndex(PyCallExpression call, PsiElement argument) {
|
||||
final PyExpression[] args = call.getArguments();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
@@ -504,10 +475,7 @@ public final class PyPsiUtils {
|
||||
|
||||
@Nullable
|
||||
public static PyExpression flattenParens(@Nullable PyExpression expr) {
|
||||
while (expr instanceof PyParenthesizedExpression) {
|
||||
expr = ((PyParenthesizedExpression)expr).getContainedExpression();
|
||||
}
|
||||
return expr;
|
||||
return (PyExpression)PyPsiUtilsCore.flattenParens(expr);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -664,4 +632,59 @@ public final class PyPsiUtils {
|
||||
while (next != null);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://typing.readthedocs.io/en/latest/source/stubs.html#version-and-platform-checks">Version and Platform Checks</a>
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@Nullable
|
||||
public static StubElement<?> getParentStubSkippingVersionChecks(@Nullable StubElement<?> stub) {
|
||||
if (stub != null) {
|
||||
for (StubElement<?> e = stub.getParentStub(); e != null; e = e.getParentStub()) {
|
||||
boolean isVersionCheck = e instanceof PyIfPartIfStub || e instanceof PyIfPartElifStub || e instanceof PyElsePartStub;
|
||||
if (!isVersionCheck) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://typing.readthedocs.io/en/latest/source/stubs.html#version-and-platform-checks">Version and Platform Checks</a>
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public static boolean processChildrenStubs(@NotNull StubElement<?> stub,
|
||||
@NotNull LanguageLevel languageLevel,
|
||||
@NotNull Processor<? super StubElement<?>> processor) {
|
||||
boolean recurse = false;
|
||||
for (StubElement<?> child : stub.getChildrenStubs()) {
|
||||
if (child instanceof PyIfPartIfStub ifStub) {
|
||||
if (ifStub.getVersionCheck().matches(languageLevel)) {
|
||||
recurse = false;
|
||||
if (!processChildrenStubs(child, languageLevel, processor)) return false;
|
||||
}
|
||||
else {
|
||||
recurse = true;
|
||||
}
|
||||
}
|
||||
else if (child instanceof PyIfPartElifStub elifStub) {
|
||||
if (recurse && elifStub.getVersionCheck().matches(languageLevel)) {
|
||||
recurse = false;
|
||||
if (!processChildrenStubs(child, languageLevel, processor)) return false;
|
||||
}
|
||||
}
|
||||
else if (child instanceof PyElsePartStub) {
|
||||
if (recurse) {
|
||||
recurse = false;
|
||||
if (!processChildrenStubs(child, languageLevel, processor)) return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
recurse = false;
|
||||
if (!processor.process(child)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.psi.impl
|
||||
|
||||
import com.jetbrains.python.psi.LanguageLevel
|
||||
import com.jetbrains.python.psi.PyIfStatement
|
||||
import com.jetbrains.python.psi.PyRecursiveElementVisitor
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
* @see [Version and Platform Checks](https://typing.readthedocs.io/en/latest/source/stubs.html.version-and-platform-checks)
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
open class PyVersionAwareElementVisitor(private val languageLevel: LanguageLevel?) : PyRecursiveElementVisitor() {
|
||||
override fun visitPyIfStatement(node: PyIfStatement) {
|
||||
if (languageLevel == null) {
|
||||
super.visitPyIfStatement(node)
|
||||
return
|
||||
}
|
||||
val ifParts = sequenceOf(node.getIfPart()) + node.elifParts.asSequence()
|
||||
for (ifPart in ifParts) {
|
||||
val versionCheck = PyVersionCheck.fromCondition(ifPart)
|
||||
if (versionCheck == null) {
|
||||
super.visitPyIfStatement(node)
|
||||
return
|
||||
}
|
||||
if (versionCheck.matches(languageLevel)) {
|
||||
ifPart.statementList.accept(this)
|
||||
return
|
||||
}
|
||||
}
|
||||
node.elsePart?.statementList?.accept(this)
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.psi.impl
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.jetbrains.python.psi.LanguageLevel
|
||||
import com.jetbrains.python.psi.PyClass
|
||||
import com.jetbrains.python.psi.PyElement
|
||||
import com.jetbrains.python.psi.PyFunction
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
abstract class PyVersionAwareTopLevelElementVisitor(languageLevel: LanguageLevel) : PyVersionAwareElementVisitor(languageLevel) {
|
||||
override fun visitPyElement(node: PyElement) {
|
||||
super.visitPyElement(node)
|
||||
checkAddElement(node)
|
||||
}
|
||||
|
||||
override fun visitPyClass(node: PyClass) {
|
||||
checkAddElement(node) // do not recurse into classes
|
||||
}
|
||||
|
||||
override fun visitPyFunction(node: PyFunction) {
|
||||
checkAddElement(node) // do not recurse into functions
|
||||
}
|
||||
|
||||
protected abstract fun checkAddElement(node: PsiElement?)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.psi.impl
|
||||
|
||||
import com.intellij.openapi.util.Version
|
||||
import com.intellij.psi.util.QualifiedName
|
||||
import com.jetbrains.python.PyTokenTypes
|
||||
import com.jetbrains.python.ast.*
|
||||
import com.jetbrains.python.ast.impl.PyPsiUtilsCore
|
||||
import com.jetbrains.python.psi.LanguageLevel
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.math.BigInteger
|
||||
|
||||
@ApiStatus.Internal
|
||||
data class PyVersionCheck(val version: Version, val isLessThan: Boolean) {
|
||||
fun matches(languageLevel: LanguageLevel): Boolean {
|
||||
return isLessThan == version.compareTo(languageLevel.majorVersion, languageLevel.minorVersion) > 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Extracts the Python version comparison from {@code ifPart}'s condition if it's a version check as specified in
|
||||
* <a href="https://typing.readthedocs.io/en/latest/source/stubs.html#version-and-platform-checks">Version and Platform Checks</a> E.g.
|
||||
* <pre>{@code
|
||||
* if sys.version_info >= (3,):
|
||||
* ...
|
||||
* }</pre>
|
||||
* @return A {@link VersionCheck} instance if {@code ifPart} is a (valid) version check, or {@code null} otherwise.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun fromCondition(ifPart: PyAstIfPart): PyVersionCheck? {
|
||||
val binaryExpr = PyPsiUtilsCore.flattenParens(ifPart.condition)
|
||||
if (binaryExpr !is PyAstBinaryExpression) return null
|
||||
|
||||
val lhsRefExpr = PyPsiUtilsCore.flattenParens(binaryExpr.leftExpression)
|
||||
if (lhsRefExpr !is PyAstReferenceExpression) return null
|
||||
if (SYS_VERSION_INFO_QUALIFIED_NAME != lhsRefExpr.asQualifiedName()) return null
|
||||
|
||||
val versionTuple = PyPsiUtilsCore.flattenParens(binaryExpr.rightExpression)
|
||||
if (versionTuple !is PyAstTupleExpression<*>) return null
|
||||
val version = evaluateVersion(versionTuple)
|
||||
if (version == null) return null
|
||||
|
||||
val operator = binaryExpr.getOperator()
|
||||
if (operator !== PyTokenTypes.LT && operator !== PyTokenTypes.GE) return null
|
||||
return PyVersionCheck(version, operator === PyTokenTypes.LT)
|
||||
}
|
||||
|
||||
private val SYS_VERSION_INFO_QUALIFIED_NAME = QualifiedName.fromDottedString("sys.version_info")
|
||||
|
||||
private fun evaluateVersion(versionTuple: PyAstTupleExpression<*>): Version? {
|
||||
val elements = versionTuple.elements
|
||||
if (elements.size != 1 && elements.size != 2) {
|
||||
return null
|
||||
}
|
||||
|
||||
val major = evaluateNumber(elements[0])
|
||||
if (major == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (elements.size == 1) {
|
||||
return Version(major, 0, 0)
|
||||
}
|
||||
|
||||
val minor = evaluateNumber(elements[1])
|
||||
if (minor == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return Version(major, minor, 0)
|
||||
}
|
||||
|
||||
private fun evaluateNumber(expression: PyAstExpression?): Int? {
|
||||
if (expression !is PyAstNumericLiteralExpression) return null
|
||||
if (!expression.isIntegerLiteral) return null
|
||||
val value = expression.bigIntegerValue ?: return null
|
||||
val intValue = value.toInt()
|
||||
return if (BigInteger.valueOf(intValue.toLong()) == value) intValue else null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.psi.stubs;
|
||||
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.jetbrains.python.psi.PyElsePart;
|
||||
|
||||
public interface PyElsePartStub extends StubElement<PyElsePart> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.psi.stubs;
|
||||
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.jetbrains.python.psi.impl.PyVersionCheck;
|
||||
import com.jetbrains.python.psi.PyIfPartElif;
|
||||
|
||||
public interface PyIfPartElifStub extends StubElement<PyIfPartElif> {
|
||||
PyVersionCheck getVersionCheck();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.psi.stubs;
|
||||
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.jetbrains.python.psi.impl.PyVersionCheck;
|
||||
import com.jetbrains.python.psi.PyIfPartIf;
|
||||
|
||||
public interface PyIfPartIfStub extends StubElement<PyIfPartIf> {
|
||||
PyVersionCheck getVersionCheck();
|
||||
}
|
||||
@@ -4,6 +4,7 @@ package com.jetbrains.python
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.stubs.IStubElementType
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import com.intellij.psi.tree.IReparseableElementType
|
||||
import com.jetbrains.python.psi.impl.*
|
||||
import java.util.function.Function
|
||||
@@ -51,6 +52,12 @@ class PyElementTypesFacadeImpl : PyElementTypesFacade() {
|
||||
get() = PyStubElementTypes.TYPE_PARAMETER_LIST
|
||||
override val typeAliasStatement: IStubElementType<*, *>
|
||||
get() = PyStubElementTypes.TYPE_ALIAS_STATEMENT
|
||||
override val ifPartIf: IElementType
|
||||
get() = PyStubElementTypes.IF_PART_IF
|
||||
override val ifPartElif: IElementType
|
||||
get() = PyStubElementTypes.IF_PART_ELIF
|
||||
override val elsePart: IElementType
|
||||
get() = PyStubElementTypes.ELSE_PART
|
||||
|
||||
override val statementList: IReparseableElementType
|
||||
get() = PyStatementListElementType()
|
||||
@@ -170,10 +177,6 @@ class PyElementTypesFacadeImpl : PyElementTypesFacade() {
|
||||
get() = F { node -> PySetCompExpressionImpl(node) }
|
||||
override val dictCompExpressionConstructor: F
|
||||
get() = F { node -> PyDictCompExpressionImpl(node) }
|
||||
override val ifPartIfConstructor: F
|
||||
get() = F { node -> PyIfPartIfImpl(node) }
|
||||
override val ifPartElifConstructor: F
|
||||
get() = F { node -> PyIfPartElifImpl(node) }
|
||||
override val forPartConstructor: F
|
||||
get() = F { node -> PyForPartImpl(node) }
|
||||
override val whilePartConstructor: F
|
||||
@@ -182,8 +185,6 @@ class PyElementTypesFacadeImpl : PyElementTypesFacade() {
|
||||
get() = F { node -> PyTryPartImpl(node) }
|
||||
override val finallyPartConstructor: F
|
||||
get() = F { node -> PyFinallyPartImpl(node) }
|
||||
override val elsePartConstructor: F
|
||||
get() = F { node -> PyElsePartImpl(node) }
|
||||
override val fStringNodeConstructor: F
|
||||
get() = F { node -> PyFormattedStringElementImpl(node) }
|
||||
override val fStringFragmentConstructor: F
|
||||
|
||||
@@ -33,4 +33,8 @@ public interface PyStubElementTypes {
|
||||
PyStubElementType<PyTypeParameterStub, PyTypeParameter> TYPE_PARAMETER = new PyTypeParameterElementType();
|
||||
PyStubElementType<PyTypeParameterListStub, PyTypeParameterList> TYPE_PARAMETER_LIST = new PyTypeParameterListElementType();
|
||||
PyStubElementType<PyTypeAliasStatementStub, PyTypeAliasStatement> TYPE_ALIAS_STATEMENT = new PyTypeAliasStatementElementType();
|
||||
|
||||
PyStubElementType<PyIfPartIfStub, PyIfPartIf> IF_PART_IF = new PyIfPartIfElementType();
|
||||
PyStubElementType<PyIfPartElifStub, PyIfPartElif> IF_PART_ELIF = new PyIfPartElifElementType();
|
||||
PyStubElementType<PyElsePartStub, PyElsePart> ELSE_PART = new PyElsePartElementType();
|
||||
}
|
||||
|
||||
+10
-1
@@ -18,6 +18,8 @@ import com.jetbrains.python.codeInsight.dataflow.scope.ScopeVariable;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyAugAssignmentStatementNavigator;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.impl.PyVersionAwareElementVisitor;
|
||||
import com.jetbrains.python.pyi.PyiUtil;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -214,7 +216,14 @@ public class ScopeImpl implements Scope {
|
||||
final Set<String> nonlocals = new HashSet<>();
|
||||
final Set<String> augAssignments = new HashSet<>();
|
||||
final List<PyTargetExpression> targetExpressions = new ArrayList<>();
|
||||
myFlowOwner.acceptChildren(new PyRecursiveElementVisitor() {
|
||||
final LanguageLevel languageLevel;
|
||||
if (myFlowOwner instanceof PyFile || myFlowOwner instanceof PyClass) {
|
||||
languageLevel = PyiUtil.getOriginalLanguageLevel(myFlowOwner);
|
||||
}
|
||||
else {
|
||||
languageLevel = null;
|
||||
}
|
||||
myFlowOwner.acceptChildren(new PyVersionAwareElementVisitor(languageLevel) {
|
||||
@Override
|
||||
public void visitPyTargetExpression(@NotNull PyTargetExpression node) {
|
||||
targetExpressions.add(node);
|
||||
|
||||
@@ -60,7 +60,7 @@ public class PyFileElementType extends IStubFileElementType<PyFileStub> {
|
||||
@Override
|
||||
public int getStubVersion() {
|
||||
// Don't forget to update versions of indexes that use the updated stub-based elements
|
||||
return 89;
|
||||
return 90;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -642,8 +642,9 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
final Property decoratedProperty = processDecoratedProperties(filter);
|
||||
if (decoratedProperty != null) return decoratedProperty;
|
||||
|
||||
if (getStub() != null) {
|
||||
return processStubProperties(filter);
|
||||
PyClassStub stub = getStub();
|
||||
if (stub != null) {
|
||||
return processStubProperties(stub, filter);
|
||||
}
|
||||
else {
|
||||
// name = property(...) assignments from PSI
|
||||
@@ -721,10 +722,12 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Property processStubProperties(@Nullable Processor<? super Property> filter) {
|
||||
final PyClassStub stub = getStub();
|
||||
if (stub != null) {
|
||||
for (StubElement<?> subStub : stub.getChildrenStubs()) {
|
||||
private Property processStubProperties(@NotNull PyClassStub stub, @Nullable Processor<? super Property> filter) {
|
||||
class StubPropertiesProcessor implements Processor<StubElement<?>> {
|
||||
private @Nullable Property myResult;
|
||||
|
||||
@Override
|
||||
public boolean process(StubElement<?> subStub) {
|
||||
if (subStub.getStubType() == PyElementTypes.TARGET_EXPRESSION) {
|
||||
final PyTargetExpressionStub targetStub = (PyTargetExpressionStub)subStub;
|
||||
final PropertyStubStorage prop = targetStub.getCustomStub(PropertyStubStorage.class);
|
||||
@@ -735,13 +738,19 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
final String doc = prop.getDoc();
|
||||
if (getter != NONE || setter != NONE || deleter != NONE) {
|
||||
final PropertyImpl property = new PropertyImpl(targetStub.getName(), getter, setter, deleter, doc, targetStub.getPsi());
|
||||
if (filter == null || filter.process(property)) return property;
|
||||
if (filter == null || filter.process(property)) {
|
||||
myResult = property;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
StubPropertiesProcessor processor = new StubPropertiesProcessor();
|
||||
PyPsiUtils.processChildrenStubs(stub, PyiUtil.getOriginalLanguageLevel(this), processor);
|
||||
return processor.myResult;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1014,28 +1023,39 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
|
||||
@Override
|
||||
public List<PyTargetExpression> getClassAttributes() {
|
||||
LanguageLevel languageLevel = PyiUtil.getOriginalLanguageLevel(this);
|
||||
final ArrayList<PyTargetExpression> result = new ArrayList<>();
|
||||
PyClassStub stub = getStub();
|
||||
if (stub != null) {
|
||||
final PyTargetExpression[] children = stub.getChildrenByType(PyElementTypes.TARGET_EXPRESSION, PyTargetExpression.EMPTY_ARRAY);
|
||||
return Arrays.asList(children);
|
||||
PyPsiUtils.processChildrenStubs(stub, languageLevel, child -> {
|
||||
if (child.getStubType() == PyElementTypes.TARGET_EXPRESSION) {
|
||||
result.add((PyTargetExpression)child.getPsi());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
List<PyTargetExpression> result = new ArrayList<>();
|
||||
for (PsiElement psiElement : getStatementList().getChildren()) {
|
||||
if (psiElement instanceof PyAssignmentStatement assignmentStatement) {
|
||||
final PyExpression[] targets = assignmentStatement.getTargets();
|
||||
for (PyExpression target : targets) {
|
||||
if (target instanceof PyTargetExpression) {
|
||||
result.add((PyTargetExpression)target);
|
||||
else {
|
||||
getStatementList().acceptChildren(new PyVersionAwareTopLevelElementVisitor(languageLevel) {
|
||||
@Override
|
||||
protected void checkAddElement(PsiElement psiElement) {
|
||||
if (psiElement instanceof PyAssignmentStatement assignmentStatement) {
|
||||
final PyExpression[] targets = assignmentStatement.getTargets();
|
||||
for (PyExpression target : targets) {
|
||||
if (target instanceof PyTargetExpression) {
|
||||
result.add((PyTargetExpression)target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (psiElement instanceof PyTypeDeclarationStatement) {
|
||||
final PyExpression target = ((PyTypeDeclarationStatement)psiElement).getTarget();
|
||||
if (target instanceof PyTargetExpression) {
|
||||
result.add((PyTargetExpression)target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (psiElement instanceof PyTypeDeclarationStatement) {
|
||||
final PyExpression target = ((PyTypeDeclarationStatement)psiElement).getTarget();
|
||||
if (target instanceof PyTargetExpression) {
|
||||
result.add((PyTargetExpression)target);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
result.trimToSize();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1233,11 +1253,9 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
public boolean processClassLevelDeclarations(@NotNull PsiScopeProcessor processor) {
|
||||
final PyClassStub stub = getStub();
|
||||
if (stub != null) {
|
||||
final List<StubElement<?>> children = stub.getChildrenStubs();
|
||||
for (StubElement<?> child : children) {
|
||||
if (!processor.execute(child.getPsi(), ResolveState.initial())) {
|
||||
return false;
|
||||
}
|
||||
LanguageLevel languageLevel = PyiUtil.getOriginalLanguageLevel(this);
|
||||
if (!PyPsiUtils.processChildrenStubs(stub, languageLevel, child -> processor.execute(child.getPsi(), ResolveState.initial()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
+9
-1
@@ -2,14 +2,22 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.stubs.IStubElementType;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.jetbrains.python.psi.PyConditionalStatementPart;
|
||||
import com.jetbrains.python.psi.PyElementVisitor;
|
||||
|
||||
public abstract class PyConditionalStatementPartImpl extends PyElementImpl implements PyConditionalStatementPart {
|
||||
public abstract class PyConditionalStatementPartImpl<T extends StubElement<? extends PsiElement>> extends PyBaseElementImpl<T>
|
||||
implements PyConditionalStatementPart {
|
||||
public PyConditionalStatementPartImpl(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
|
||||
protected PyConditionalStatementPartImpl(T stub, IStubElementType<?, ?> elementType) {
|
||||
super(stub, elementType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
|
||||
pyVisitor.visitPyConditionalStatementPart(this);
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.jetbrains.python.PyStubElementTypes;
|
||||
import com.jetbrains.python.psi.PyElsePart;
|
||||
import com.jetbrains.python.psi.stubs.PyElsePartStub;
|
||||
|
||||
public class PyElsePartImpl extends PyElementImpl implements PyElsePart {
|
||||
|
||||
public class PyElsePartImpl extends PyBaseElementImpl<PyElsePartStub> implements PyElsePart {
|
||||
public PyElsePartImpl(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
|
||||
public PyElsePartImpl(PyElsePartStub stub) {
|
||||
super(stub, PyStubElementTypes.ELSE_PART);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,8 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
myModificationStamp = modificationStamp;
|
||||
|
||||
final StubElement<?> stub = getStub();
|
||||
processDeclarations(PyPsiUtils.collectAllStubChildren(PyFileImpl.this, stub), element -> {
|
||||
LanguageLevel languageLevel = PyiUtil.getOriginalLanguageLevel(PyFileImpl.this);
|
||||
processDeclarations(PyFileImpl.this, stub, languageLevel, element -> {
|
||||
if (element instanceof PsiNamedElement namedElement &&
|
||||
!(element instanceof PyKeywordArgument) &&
|
||||
!(stub == null && element.getParent() instanceof PyImportElement)) {
|
||||
@@ -101,13 +102,16 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
Collections.reverse(myImportedNameDefiners);
|
||||
}
|
||||
|
||||
private static boolean processDeclarations(@NotNull List<PsiElement> elements, @NotNull Processor<? super PsiElement> processor) {
|
||||
for (PsiElement child : elements) {
|
||||
private static boolean processDeclarations(@NotNull PsiElement element,
|
||||
@Nullable StubElement<?> stub,
|
||||
@NotNull LanguageLevel languageLevel,
|
||||
@NotNull Processor<? super PsiElement> processor) {
|
||||
for (PsiElement child : collectAllChildren(element, stub, languageLevel)) {
|
||||
if (!processor.process(child)) {
|
||||
return false;
|
||||
}
|
||||
if (child instanceof PyExceptPart part) {
|
||||
if (!processDeclarations(PyPsiUtils.collectAllStubChildren(part, part.getStub()), processor)) {
|
||||
if (!processDeclarations(part, part.getStub(), languageLevel, processor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -228,7 +232,7 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
|
||||
@Override
|
||||
public @NotNull List<PyTypeAliasStatement> getTypeAliasStatements() {
|
||||
return PyPsiUtils.collectStubChildren(this, getGreenStub(), PyTypeAliasStatement.class);
|
||||
return collectChildren(PyTypeAliasStatement.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -370,18 +374,18 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
@Override
|
||||
@NotNull
|
||||
public List<PyClass> getTopLevelClasses() {
|
||||
return PyPsiUtils.collectStubChildren(this, getGreenStub(), PyClass.class);
|
||||
return collectChildren(PyClass.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<PyFunction> getTopLevelFunctions() {
|
||||
return PyPsiUtils.collectStubChildren(this, getGreenStub(), PyFunction.class);
|
||||
return collectChildren(PyFunction.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PyTargetExpression> getTopLevelAttributes() {
|
||||
return PyPsiUtils.collectStubChildren(this, getGreenStub(), PyTargetExpression.class);
|
||||
return collectChildren(PyTargetExpression.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -490,7 +494,7 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
@NotNull
|
||||
public List<PyImportElement> getImportTargets() {
|
||||
final List<PyImportElement> ret = new ArrayList<>();
|
||||
final List<PyImportStatement> imports = PyPsiUtils.collectStubChildren(this, getGreenStub(), PyImportStatement.class);
|
||||
final List<PyImportStatement> imports = collectChildren(PyImportStatement.class);
|
||||
for (PyImportStatement one : imports) {
|
||||
ContainerUtil.addAll(ret, one.getImportElements());
|
||||
}
|
||||
@@ -500,7 +504,7 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
@Override
|
||||
@NotNull
|
||||
public List<PyFromImportStatement> getFromImports() {
|
||||
return PyPsiUtils.collectStubChildren(this, getGreenStub(), PyFromImportStatement.class);
|
||||
return collectChildren(PyFromImportStatement.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -845,4 +849,68 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private <T extends PyElement> List<T> collectChildren(Class<T> type) {
|
||||
return collectChildren(this, getGreenStub(), PyiUtil.getOriginalLanguageLevel(this), type);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static <T extends PyElement> List<T> collectChildren(@NotNull PsiElement element,
|
||||
@Nullable StubElement<?> stub,
|
||||
@NotNull LanguageLevel languageLevel,
|
||||
@NotNull Class<T> type) {
|
||||
final List<T> result = new ArrayList<>();
|
||||
if (stub != null) {
|
||||
PyPsiUtils.processChildrenStubs(stub, languageLevel, child -> {
|
||||
PsiElement childPsi = child.getPsi();
|
||||
if (type.isInstance(childPsi)) {
|
||||
result.add(type.cast(childPsi));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else {
|
||||
element.acceptChildren(new PyVersionAwareTopLevelElementVisitor(languageLevel) {
|
||||
@Override
|
||||
protected void checkAddElement(PsiElement node) {
|
||||
if (type.isInstance(node)) {
|
||||
result.add(type.cast(node));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyStatement(@NotNull PyStatement node) {
|
||||
if (PyStatement.class.isAssignableFrom(type) && !(node instanceof PyCompoundStatement)) {
|
||||
checkAddElement(node);
|
||||
return;
|
||||
}
|
||||
super.visitPyStatement(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiElement> collectAllChildren(@NotNull PsiElement element,
|
||||
@Nullable StubElement<?> stub,
|
||||
@NotNull LanguageLevel languageLevel) {
|
||||
List<PsiElement> result = new ArrayList<>();
|
||||
if (stub != null) {
|
||||
PyPsiUtils.processChildrenStubs(stub, languageLevel, child -> {
|
||||
result.add(child.getPsi());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else {
|
||||
element.acceptChildren(new PyVersionAwareTopLevelElementVisitor(languageLevel) {
|
||||
@Override
|
||||
protected void checkAddElement(PsiElement node) {
|
||||
result.add(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,7 @@ import com.jetbrains.python.documentation.docstrings.DocStringUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.icons.PythonPsiApiIcons;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
|
||||
import com.jetbrains.python.psi.stubs.PyAnnotationOwnerStub;
|
||||
import com.jetbrains.python.psi.stubs.PyClassStub;
|
||||
import com.jetbrains.python.psi.stubs.PyFunctionStub;
|
||||
import com.jetbrains.python.psi.stubs.PyTargetExpressionStub;
|
||||
import com.jetbrains.python.psi.stubs.*;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import com.jetbrains.python.sdk.PythonSdkUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -133,9 +130,9 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
|
||||
public @Nullable PyClass getContainingClass() {
|
||||
final PyFunctionStub stub = getStub();
|
||||
if (stub != null) {
|
||||
final StubElement parentStub = stub.getParentStub();
|
||||
if (parentStub instanceof PyClassStub) {
|
||||
return ((PyClassStub)parentStub).getPsi();
|
||||
final StubElement<?> parentStub = PyPsiUtils.getParentStubSkippingVersionChecks(stub);
|
||||
if (parentStub instanceof PyClassStub classStub) {
|
||||
return classStub.getPsi();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -2,13 +2,19 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.jetbrains.python.PyStubElementTypes;
|
||||
import com.jetbrains.python.psi.PyIfPartElif;
|
||||
import com.jetbrains.python.psi.stubs.PyIfPartElifStub;
|
||||
|
||||
/**
|
||||
* PyIfPart that represents an 'elif' part.
|
||||
*/
|
||||
public class PyIfPartElifImpl extends PyConditionalStatementPartImpl implements PyIfPartElif {
|
||||
public class PyIfPartElifImpl extends PyConditionalStatementPartImpl<PyIfPartElifStub> implements PyIfPartElif {
|
||||
public PyIfPartElifImpl(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
|
||||
public PyIfPartElifImpl(PyIfPartElifStub stub) {
|
||||
super(stub, PyStubElementTypes.IF_PART_ELIF);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.jetbrains.python.PyStubElementTypes;
|
||||
import com.jetbrains.python.psi.PyIfPartIf;
|
||||
import com.jetbrains.python.psi.stubs.PyIfPartIfStub;
|
||||
|
||||
/**
|
||||
* PyIfPart that represents an 'if' part.
|
||||
*/
|
||||
public class PyIfPartIfImpl extends PyConditionalStatementPartImpl implements PyIfPartIf {
|
||||
|
||||
public class PyIfPartIfImpl extends PyConditionalStatementPartImpl<PyIfPartIfStub> implements PyIfPartIf {
|
||||
public PyIfPartIfImpl(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
|
||||
public PyIfPartIfImpl(PyIfPartIfStub stub) {
|
||||
super(stub, PyStubElementTypes.IF_PART_IF);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -590,7 +590,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
|
||||
public PyClass getContainingClass() {
|
||||
final PyTargetExpressionStub stub = getStub();
|
||||
if (stub != null) {
|
||||
final StubElement parentStub = stub.getParentStub();
|
||||
final StubElement parentStub = PyPsiUtils.getParentStubSkippingVersionChecks(stub);
|
||||
if (parentStub instanceof PyClassStub) {
|
||||
return ((PyClassStub)parentStub).getPsi();
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.jetbrains.python.psi.PyWhilePart;
|
||||
|
||||
public class PyWhilePartImpl extends PyConditionalStatementPartImpl implements PyWhilePart {
|
||||
public class PyWhilePartImpl extends PyConditionalStatementPartImpl<StubElement<PyWhilePart>> implements PyWhilePart {
|
||||
public PyWhilePartImpl(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
|
||||
+2
-2
@@ -161,7 +161,7 @@ public class PyClassElementType extends PyStubElementType<PyClassStub, PyClass>
|
||||
final List<String> slots = PyFileElementType.readNullableList(dataStream);
|
||||
|
||||
final String docStringInStub = dataStream.readUTFFast();
|
||||
final String docString = docStringInStub.length() > 0 ? docStringInStub : null;
|
||||
final String docString = !docStringInStub.isEmpty() ? docStringInStub : null;
|
||||
|
||||
final String deprecationMessage = dataStream.readNameString();
|
||||
|
||||
@@ -177,7 +177,7 @@ public class PyClassElementType extends PyStubElementType<PyClassStub, PyClass>
|
||||
if (name != null) {
|
||||
sink.occurrence(PyClassNameIndex.KEY, name);
|
||||
sink.occurrence(PyClassNameIndexInsensitive.KEY, StringUtil.toLowerCase(name));
|
||||
if (stub.getParentStub() instanceof PyFileStub && PyUtil.getInitialUnderscores(name) == 0) {
|
||||
if (PyPsiUtils.getParentStubSkippingVersionChecks(stub) instanceof PyFileStub && PyUtil.getInitialUnderscores(name) == 0) {
|
||||
sink.occurrence(PyExportedModuleAttributeIndex.KEY, name);
|
||||
}
|
||||
}
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.jetbrains.python.psi.impl.stubs
|
||||
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.stubs.StubElement
|
||||
import com.intellij.psi.stubs.StubInputStream
|
||||
import com.intellij.psi.stubs.StubOutputStream
|
||||
import com.jetbrains.python.psi.impl.PyVersionCheck
|
||||
import com.jetbrains.python.psi.PyElsePart
|
||||
import com.jetbrains.python.psi.PyIfStatement
|
||||
import com.jetbrains.python.psi.PyStubElementType
|
||||
import com.jetbrains.python.psi.impl.PyElsePartImpl
|
||||
import com.jetbrains.python.psi.stubs.PyElsePartStub
|
||||
|
||||
class PyElsePartElementType : PyStubElementType<PyElsePartStub, PyElsePart>("ELSE_PART") {
|
||||
override fun createPsi(stub: PyElsePartStub): PyElsePart {
|
||||
return PyElsePartImpl(stub)
|
||||
}
|
||||
|
||||
override fun createStub(psi: PyElsePart, parentStub: StubElement<out PsiElement>): PyElsePartStub {
|
||||
return PyElsePartStubImpl(parentStub)
|
||||
}
|
||||
|
||||
override fun serialize(stub: PyElsePartStub, dataStream: StubOutputStream) {
|
||||
}
|
||||
|
||||
override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<*>): PyElsePartStub {
|
||||
return PyElsePartStubImpl(parentStub)
|
||||
}
|
||||
|
||||
override fun createElement(node: ASTNode): PsiElement {
|
||||
return PyElsePartImpl(node)
|
||||
}
|
||||
|
||||
override fun shouldCreateStub(node: ASTNode): Boolean {
|
||||
val ifStatement = node.treeParent?.psi as? PyIfStatement ?: return false
|
||||
if (!isFileOrClassTopLevel(ifStatement)) return false
|
||||
val ifParts = sequenceOf(ifStatement.ifPart) + ifStatement.elifParts.asSequence()
|
||||
return ifParts.all { PyVersionCheck.fromCondition(it) != null }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.jetbrains.python.psi.impl.stubs
|
||||
|
||||
import com.intellij.psi.stubs.StubBase
|
||||
import com.intellij.psi.stubs.StubElement
|
||||
import com.jetbrains.python.PyStubElementTypes
|
||||
import com.jetbrains.python.psi.PyElsePart
|
||||
import com.jetbrains.python.psi.stubs.PyElsePartStub
|
||||
|
||||
class PyElsePartStubImpl(parent: StubElement<*>) : StubBase<PyElsePart>(parent, PyStubElementTypes.ELSE_PART), PyElsePartStub
|
||||
+1
-2
@@ -19,7 +19,6 @@ import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.stubs.*;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyStubElementTypes;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyStringLiteralExpression;
|
||||
@@ -116,7 +115,7 @@ public class PyFunctionElementType extends PyStubElementType<PyFunctionStub, PyF
|
||||
final String name = stub.getName();
|
||||
if (name != null) {
|
||||
sink.occurrence(PyFunctionNameIndex.KEY, name);
|
||||
if (stub.getParentStub() instanceof PyFileStub && PyUtil.getInitialUnderscores(name) == 0) {
|
||||
if (PyPsiUtils.getParentStubSkippingVersionChecks(stub) instanceof PyFileStub && PyUtil.getInitialUnderscores(name) == 0) {
|
||||
sink.occurrence(PyExportedModuleAttributeIndex.KEY, name);
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.jetbrains.python.psi.impl.stubs
|
||||
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.stubs.StubElement
|
||||
import com.intellij.psi.stubs.StubInputStream
|
||||
import com.intellij.psi.stubs.StubOutputStream
|
||||
import com.intellij.util.takeWhileInclusive
|
||||
import com.jetbrains.python.psi.impl.PyVersionCheck
|
||||
import com.jetbrains.python.psi.PyIfPartElif
|
||||
import com.jetbrains.python.psi.PyIfStatement
|
||||
import com.jetbrains.python.psi.PyStubElementType
|
||||
import com.jetbrains.python.psi.impl.PyIfPartElifImpl
|
||||
import com.jetbrains.python.psi.stubs.PyIfPartElifStub
|
||||
|
||||
class PyIfPartElifElementType : PyStubElementType<PyIfPartElifStub, PyIfPartElif>("IF_PART_ELIF") {
|
||||
override fun createPsi(stub: PyIfPartElifStub): PyIfPartElif {
|
||||
return PyIfPartElifImpl(stub)
|
||||
}
|
||||
|
||||
override fun createStub(psi: PyIfPartElif, parentStub: StubElement<out PsiElement>): PyIfPartElifStub {
|
||||
return PyIfPartElifStubImpl(parentStub, requireNotNull(PyVersionCheck.fromCondition (psi)))
|
||||
}
|
||||
|
||||
override fun serialize(stub: PyIfPartElifStub, dataStream: StubOutputStream) {
|
||||
serializeVersionCheck(stub.versionCheck, dataStream)
|
||||
}
|
||||
|
||||
override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<*>): PyIfPartElifStub {
|
||||
return PyIfPartElifStubImpl(parentStub, deserializeVersionCheck(dataStream))
|
||||
}
|
||||
|
||||
override fun createElement(node: ASTNode): PsiElement {
|
||||
return PyIfPartElifImpl(node)
|
||||
}
|
||||
|
||||
override fun shouldCreateStub(node: ASTNode): Boolean {
|
||||
val ifStatement = node.treeParent?.psi as? PyIfStatement ?: return false
|
||||
if (!isFileOrClassTopLevel(ifStatement)) return false
|
||||
return (sequenceOf(ifStatement.ifPart) + ifStatement.elifParts.asSequence())
|
||||
.takeWhileInclusive { it !== node.psi }
|
||||
.all { PyVersionCheck.fromCondition(it) != null }
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.jetbrains.python.psi.impl.stubs
|
||||
|
||||
import com.intellij.psi.stubs.StubBase
|
||||
import com.intellij.psi.stubs.StubElement
|
||||
import com.jetbrains.python.PyStubElementTypes
|
||||
import com.jetbrains.python.psi.impl.PyVersionCheck
|
||||
import com.jetbrains.python.psi.PyIfPartElif
|
||||
import com.jetbrains.python.psi.stubs.PyIfPartElifStub
|
||||
|
||||
class PyIfPartElifStubImpl(parent: StubElement<*>, private val versionCheck: PyVersionCheck)
|
||||
: StubBase<PyIfPartElif>(parent, PyStubElementTypes.IF_PART_ELIF), PyIfPartElifStub {
|
||||
override fun getVersionCheck(): PyVersionCheck = versionCheck
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.jetbrains.python.psi.impl.stubs
|
||||
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.stubs.StubElement
|
||||
import com.intellij.psi.stubs.StubInputStream
|
||||
import com.intellij.psi.stubs.StubOutputStream
|
||||
import com.jetbrains.python.psi.impl.PyVersionCheck
|
||||
import com.jetbrains.python.psi.*
|
||||
import com.jetbrains.python.psi.impl.PyIfPartIfImpl
|
||||
import com.jetbrains.python.psi.stubs.PyIfPartIfStub
|
||||
|
||||
class PyIfPartIfElementType : PyStubElementType<PyIfPartIfStub, PyIfPartIf>("IF_PART_IF") {
|
||||
override fun createPsi(stub: PyIfPartIfStub): PyIfPartIf {
|
||||
return PyIfPartIfImpl(stub)
|
||||
}
|
||||
|
||||
override fun createStub(psi: PyIfPartIf, parentStub: StubElement<out PsiElement>): PyIfPartIfStub {
|
||||
return PyIfPartIfStubImpl(parentStub, requireNotNull(PyVersionCheck.fromCondition (psi)))
|
||||
}
|
||||
|
||||
override fun serialize(stub: PyIfPartIfStub, dataStream: StubOutputStream) {
|
||||
serializeVersionCheck(stub.versionCheck, dataStream)
|
||||
}
|
||||
|
||||
override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<*>): PyIfPartIfStub {
|
||||
return PyIfPartIfStubImpl(parentStub, deserializeVersionCheck(dataStream))
|
||||
}
|
||||
|
||||
override fun createElement(node: ASTNode): PsiElement {
|
||||
return PyIfPartIfImpl(node)
|
||||
}
|
||||
|
||||
override fun shouldCreateStub(node: ASTNode): Boolean {
|
||||
val psi = node.psi as PyIfPartIf
|
||||
return isFileOrClassTopLevel(psi) && PyVersionCheck.fromCondition(psi) != null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.jetbrains.python.psi.impl.stubs
|
||||
|
||||
import com.intellij.psi.stubs.StubBase
|
||||
import com.intellij.psi.stubs.StubElement
|
||||
import com.jetbrains.python.PyStubElementTypes
|
||||
import com.jetbrains.python.psi.impl.PyVersionCheck
|
||||
import com.jetbrains.python.psi.PyIfPartIf
|
||||
import com.jetbrains.python.psi.stubs.PyIfPartIfStub
|
||||
|
||||
class PyIfPartIfStubImpl(parent: StubElement<*>, private val versionCheck: PyVersionCheck)
|
||||
: StubBase<PyIfPartIf>(parent, PyStubElementTypes.IF_PART_IF), PyIfPartIfStub {
|
||||
override fun getVersionCheck(): PyVersionCheck = versionCheck
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.jetbrains.python.psi.impl.stubs
|
||||
|
||||
import com.intellij.openapi.util.Version
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.stubs.StubInputStream
|
||||
import com.intellij.psi.stubs.StubOutputStream
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.jetbrains.python.psi.impl.PyVersionCheck
|
||||
import com.jetbrains.python.psi.PyClass
|
||||
import com.jetbrains.python.psi.PyFile
|
||||
import com.jetbrains.python.psi.PyFunction
|
||||
|
||||
internal fun isFileOrClassTopLevel(element: PsiElement): Boolean {
|
||||
val parent = PsiTreeUtil.getParentOfType(element, PyFile::class.java, PyClass::class.java, PyFunction::class.java)
|
||||
return parent is PyFile || parent is PyClass
|
||||
}
|
||||
|
||||
internal fun serializeVersionCheck(versionCheck: PyVersionCheck, dataStream: StubOutputStream) {
|
||||
dataStream.writeBoolean(versionCheck.isLessThan)
|
||||
dataStream.writeVarInt(versionCheck.version.major)
|
||||
dataStream.writeVarInt(versionCheck.version.minor)
|
||||
}
|
||||
|
||||
internal fun deserializeVersionCheck(dataStream: StubInputStream): PyVersionCheck {
|
||||
val isLessThan = dataStream.readBoolean()
|
||||
val major = dataStream.readVarInt()
|
||||
val minor = dataStream.readVarInt()
|
||||
return PyVersionCheck(Version(major, minor, 0), isLessThan)
|
||||
}
|
||||
+2
-1
@@ -15,6 +15,7 @@ import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PythonDialectsTokenSetProvider;
|
||||
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.impl.PyTargetExpressionImpl;
|
||||
import com.jetbrains.python.psi.stubs.PyExportedModuleAttributeIndex;
|
||||
import com.jetbrains.python.psi.stubs.PyFileStub;
|
||||
@@ -149,7 +150,7 @@ public class PyTargetExpressionElementType extends PyStubElementType<PyTargetExp
|
||||
public void indexStub(@NotNull PyTargetExpressionStub stub, @NotNull IndexSink sink) {
|
||||
String name = stub.getName();
|
||||
if (name != null && PyUtil.getInitialUnderscores(name) == 0) {
|
||||
if (stub.getParentStub() instanceof PyFileStub) {
|
||||
if (PyPsiUtils.getParentStubSkippingVersionChecks(stub) instanceof PyFileStub) {
|
||||
sink.occurrence(PyVariableNameIndex.KEY, name);
|
||||
sink.occurrence(PyExportedModuleAttributeIndex.KEY, name);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.jetbrains.python.pyi;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
@@ -105,14 +106,20 @@ public final class PyiUtil {
|
||||
@NotNull
|
||||
public static LanguageLevel getOriginalLanguageLevel(@NotNull PyElement element) {
|
||||
PsiFile containingFile = element.getContainingFile();
|
||||
if (containingFile instanceof PyiFile) {
|
||||
if (containingFile instanceof PyiFile pyiFile) {
|
||||
PsiElement impl = getOriginalElement(element);
|
||||
if (impl != null) {
|
||||
return LanguageLevel.forElement(impl);
|
||||
}
|
||||
else {
|
||||
// XXX: Relying on the fact .pyi files still have the language level key set by the pusher
|
||||
return PythonLanguageLevelPusher.getLanguageLevelForVirtualFile(element.getProject(), containingFile.getVirtualFile());
|
||||
VirtualFile vFile = containingFile.getVirtualFile();
|
||||
if (vFile != null) {
|
||||
return PythonLanguageLevelPusher.getLanguageLevelForVirtualFile(element.getProject(), vFile);
|
||||
}
|
||||
else {
|
||||
return pyiFile.getLanguageLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
return LanguageLevel.forElement(element);
|
||||
|
||||
+7
-5
@@ -12,10 +12,7 @@ import com.intellij.psi.impl.source.PsiFileImpl;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.util.xmlb.annotations.OptionTag;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.ast.PyAstClass;
|
||||
import com.jetbrains.python.ast.PyAstFile;
|
||||
import com.jetbrains.python.ast.PyAstFunction;
|
||||
import com.jetbrains.python.ast.PyAstTargetExpression;
|
||||
import com.jetbrains.python.ast.*;
|
||||
import com.jetbrains.python.ast.impl.PyPsiUtilsCore;
|
||||
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareService;
|
||||
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareServiceClasses;
|
||||
@@ -101,9 +98,14 @@ public abstract class PyDocumentationSettings
|
||||
@Nullable
|
||||
private static PyAstTargetExpression getDocFormatAttribute(@NotNull List<StubElement<?>> stubs) {
|
||||
for (StubElement<?> stub : stubs) {
|
||||
if (stub.getPsi() instanceof PyAstTargetExpression targetExpression && PyNames.DOCFORMAT.equals(targetExpression.getName())) {
|
||||
PsiElement psi = stub.getPsi();
|
||||
if (psi instanceof PyAstTargetExpression targetExpression && PyNames.DOCFORMAT.equals(targetExpression.getName())) {
|
||||
return targetExpression;
|
||||
}
|
||||
if (psi instanceof PyAstIfPart || psi instanceof PyAstElsePart) {
|
||||
PyAstTargetExpression targetExpression = getDocFormatAttribute(stub.getChildrenStubs());
|
||||
if (targetExpression != null) return targetExpression;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import sys
|
||||
|
||||
if condition1:
|
||||
class MyClass:
|
||||
if condition2:
|
||||
attr0 = 0
|
||||
def f0():
|
||||
pass
|
||||
class MyClass0:
|
||||
pass
|
||||
if sys.version_info >= (3,):
|
||||
attr1 = 1
|
||||
def f1():
|
||||
pass
|
||||
class MyClass1:
|
||||
pass
|
||||
elif sys.version_info < (2, 6):
|
||||
attr2 = 2
|
||||
def f2():
|
||||
pass
|
||||
class MyClass2:
|
||||
pass
|
||||
else:
|
||||
attr3 = 3
|
||||
def f3():
|
||||
pass
|
||||
class MyClass3:
|
||||
pass
|
||||
|
||||
MyClass.<caret>
|
||||
@@ -0,0 +1,28 @@
|
||||
import sys
|
||||
|
||||
if condition:
|
||||
attr0 = 0
|
||||
def f0():
|
||||
pass
|
||||
class MyClass0:
|
||||
pass
|
||||
if sys.version_info >= (3,):
|
||||
attr1 = 1
|
||||
def f1():
|
||||
pass
|
||||
class MyClass1:
|
||||
pass
|
||||
elif sys.version_info < (2, 6):
|
||||
attr2 = 2
|
||||
def f2():
|
||||
pass
|
||||
class MyClass2:
|
||||
pass
|
||||
else:
|
||||
attr3 = 3
|
||||
def f3():
|
||||
pass
|
||||
class MyClass3:
|
||||
pass
|
||||
|
||||
<caret>
|
||||
@@ -0,0 +1,21 @@
|
||||
import sys
|
||||
|
||||
if condition1:
|
||||
class MyClass:
|
||||
if condition2:
|
||||
if sys.version_info < (4,):
|
||||
def foo(self):
|
||||
class MyNestedClass:
|
||||
def f0(self):
|
||||
pass
|
||||
if sys.version_info >= (3,):
|
||||
def f1(self):
|
||||
pass
|
||||
elif sys.version_info < (2, 6):
|
||||
def f2(self):
|
||||
pass
|
||||
else:
|
||||
def f3(self):
|
||||
pass
|
||||
|
||||
MyNestedClass().<caret>
|
||||
@@ -4,4 +4,4 @@ TypeVar("T", int, str, bound=int, covariant=True, contravariant=True)
|
||||
TypeVar("T", int, str, bound='int', covariant=True, contravariant=True)
|
||||
TypeVar("T", int, 'str', bound=int, covariant=True, contravariant=True)
|
||||
TypeVar("T", 'int', 'str', bound=int, covariant=True, contravariant=True)
|
||||
TypeVar<warning descr="Unexpected type(s):(str, int, int, int, int, int)Possible type(s):(str, Any | None, bool, bool, Any, Any)(str, Any | None, bool, bool, Any, Any)" textAttributesKey="WARNING_ATTRIBUTES">("T", 0, 1, bound=2, covariant=3, contravariant=4)</warning>
|
||||
TypeVar("T", 0, 1, bound=2, <warning descr="Expected type 'bool', got 'int' instead" textAttributesKey="WARNING_ATTRIBUTES">covariant=3</warning>, <warning descr="Expected type 'bool', got 'int' instead" textAttributesKey="WARNING_ATTRIBUTES">contravariant=4</warning>)
|
||||
@@ -1 +1 @@
|
||||
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Class"/> <code><a href="psi_element://#typename#TypingExtensionsSelf.A">TypingExtensionsSelf.A</a></code></div><div class="definition"><pre><span style="color:#000080;font-weight:bold;">def </span><span style="color:#000000;">foo</span><span style="">(</span><span style="color:#94558d;">self</span><span style="">)</span> -> <span style="color:#000000;">Self</span></pre></div></body></html>
|
||||
<html><body><div class="bottom"><icon src="AllIcons.Nodes.Class"/> <code><a href="psi_element://#typename#TypingExtensionsSelf.A">TypingExtensionsSelf.A</a></code></div><div class="definition"><pre><span style="color:#000080;font-weight:bold;">def </span><span style="color:#000000;">foo</span><span style="">(</span><span style="color:#94558d;">self</span><span style="">)</span> -> <span style="color:#000000;"><a href="psi_element://#typename#typing._SpecialForm">Self</a></span></pre></div></body></html>
|
||||
@@ -0,0 +1,13 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info < (4,):
|
||||
class MyClass:
|
||||
if sys.version_info >= (3,):
|
||||
def foo(self):
|
||||
pass
|
||||
elif sys.version_info <= (2, 5):
|
||||
def bar(self):
|
||||
pass
|
||||
else:
|
||||
def buz(self):
|
||||
pass
|
||||
@@ -0,0 +1,8 @@
|
||||
import sys
|
||||
|
||||
if True:
|
||||
if sys.version_info >= (3,):
|
||||
if sys.version_info < (3, 12):
|
||||
foo = 23
|
||||
else:
|
||||
bar = -1
|
||||
@@ -0,0 +1,21 @@
|
||||
import sys
|
||||
|
||||
if condition1:
|
||||
if sys.version_info >= (3,):
|
||||
if sys.version_info < (3, 12):
|
||||
def foo(): ...
|
||||
else:
|
||||
if sys.version_info < (2, 2):
|
||||
pass
|
||||
elif sys.version_info < (2, 5):
|
||||
pass
|
||||
else:
|
||||
class MyClass:
|
||||
if sys.version_info < (3, 12):
|
||||
class MyNestedClass:
|
||||
if sys.version_info >= (3, 11):
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
@@ -0,0 +1,15 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info < (2, ):
|
||||
pass
|
||||
else:
|
||||
class Foo:
|
||||
if sys.version_info < (3, 2):
|
||||
def foo(self) -> int:
|
||||
pass
|
||||
elif sys.version_info < (3, 5):
|
||||
def foo(self) -> float:
|
||||
pass
|
||||
else:
|
||||
def foo(self) -> str:
|
||||
pass
|
||||
@@ -0,0 +1,8 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
def foo() -> int:
|
||||
pass
|
||||
else:
|
||||
def foo() -> str:
|
||||
pass
|
||||
@@ -414,14 +414,19 @@ public class Py3CompletionTest extends PyTestCase {
|
||||
|
||||
// PY-27148
|
||||
public void testNamedTupleSpecial() {
|
||||
final List<String> suggested = doTestByText("""
|
||||
from collections import namedtuple
|
||||
class Cat1(namedtuple("Cat", "name age")):
|
||||
pass
|
||||
c1 = Cat1("name", 5)
|
||||
c1.<caret>""");
|
||||
assertNotNull(suggested);
|
||||
assertContainsElements(suggested, PyNamedTupleType.NAMEDTUPLE_SPECIAL_ATTRIBUTES);
|
||||
runWithLanguageLevel(
|
||||
LanguageLevel.PYTHON37,
|
||||
() -> {
|
||||
final List<String> suggested = doTestByText("""
|
||||
from collections import namedtuple
|
||||
class Cat1(namedtuple("Cat", "name age")):
|
||||
pass
|
||||
c1 = Cat1("name", 5)
|
||||
c1.<caret>""");
|
||||
assertNotNull(suggested);
|
||||
assertContainsElements(suggested, PyNamedTupleType.NAMEDTUPLE_SPECIAL_ATTRIBUTES);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// PY-33254, PY-12339, PY-40834
|
||||
|
||||
@@ -2381,6 +2381,28 @@ public class Py3TypeTest extends PyTestCase {
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testTopLevelFunctionUnderVersionCheck() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON310, () -> {
|
||||
doMultiFileTest("str",
|
||||
"""
|
||||
from mod import foo
|
||||
expr = foo()
|
||||
""");
|
||||
});
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testClassMethodUnderVersionCheck() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON34, () -> {
|
||||
doMultiFileTest("float",
|
||||
"""
|
||||
from mod import Foo
|
||||
expr = Foo().foo()
|
||||
""");
|
||||
});
|
||||
}
|
||||
|
||||
private void doTest(final String expectedType, final String text) {
|
||||
myFixture.configureByText(PythonFileType.INSTANCE, text);
|
||||
final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -31,6 +32,31 @@ public class PyOverloadsResolutionTest extends PyTestCase {
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testFunctionOverloadNotMatchingVersionCheckIsIgnoredInPyFile() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON34, () -> {
|
||||
doTest("object",
|
||||
"""
|
||||
from typing import overload
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3,):
|
||||
@overload
|
||||
def func(x: str) -> str:
|
||||
pass
|
||||
else:
|
||||
@overload
|
||||
def func(x: object) -> object:
|
||||
pass
|
||||
|
||||
def func(x):
|
||||
...
|
||||
|
||||
expr = func("foo")
|
||||
""");
|
||||
});
|
||||
}
|
||||
|
||||
public void testFirstMatchingFunctionOverloadSelectedInSiblingPyiFile() {
|
||||
doMultiFileStubAwareTest("str");
|
||||
}
|
||||
@@ -60,7 +86,7 @@ public class PyOverloadsResolutionTest extends PyTestCase {
|
||||
@overload
|
||||
def method(self, x: object) -> object:
|
||||
pass
|
||||
|
||||
|
||||
def method(self, x):
|
||||
pass
|
||||
|
||||
@@ -68,6 +94,31 @@ public class PyOverloadsResolutionTest extends PyTestCase {
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testMethodOverloadNotMatchingVersionCheckIsIgnoredInPyFile() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON310, () -> {
|
||||
doTest("object",
|
||||
"""
|
||||
from typing import overload
|
||||
|
||||
class C:
|
||||
if sys.version_info < (3,):
|
||||
@overload
|
||||
def method(self, x: str) -> str:
|
||||
pass
|
||||
else:
|
||||
@overload
|
||||
def method(self, x: object) -> object:
|
||||
pass
|
||||
|
||||
def method(self, x):
|
||||
pass
|
||||
|
||||
expr = C().method("foo")
|
||||
""");
|
||||
});
|
||||
}
|
||||
|
||||
public void testFirstMatchingMethodOverloadSelectedInSiblingPyiFile() {
|
||||
doMultiFileStubAwareTest("str");
|
||||
}
|
||||
|
||||
@@ -978,9 +978,7 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
final int offset = loadTest(1).get("<arg1>").getTextOffset();
|
||||
|
||||
feignCtrlP(offset).check(Arrays.asList("self: TypeVar, name: str, *constraints, bound: Any | None = None, covariant: bool = False, " +
|
||||
"contravariant: bool = False, infer_variance: bool = False",
|
||||
"self: TypeVar, name: str, *constraints, bound: Any | None = None, covariant: bool = False, " +
|
||||
"contravariant: bool = False"),
|
||||
"contravariant: bool = False, infer_variance: bool = False"),
|
||||
Arrays.asList(new String[]{"name: str, "}, new String[]{"name: str, "}),
|
||||
Arrays.asList(new String[]{"self: TypeVar, "}, new String[]{"self: TypeVar, "}));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.lang.FileASTNode;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Version;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.PsiFileImpl;
|
||||
@@ -18,6 +19,7 @@ import com.intellij.testFramework.TestDataPath;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.ast.PyAstFunction;
|
||||
import com.jetbrains.python.psi.impl.PyVersionCheck;
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.psi.*;
|
||||
@@ -130,6 +132,43 @@ public class PyStubsTest extends PyTestCase {
|
||||
);
|
||||
}
|
||||
|
||||
// PY-34617
|
||||
public void testStubStructureWithVersionChecks() {
|
||||
final PsiFileImpl file = (PsiFileImpl)getTestFile();
|
||||
element(PyFileStub.class)
|
||||
.withChildren(
|
||||
element(PyImportStatementStub.class),
|
||||
ifPart(3, 0, false)
|
||||
.withChildren(
|
||||
ifPart(3, 12, true)
|
||||
.withChildren(
|
||||
element(PyFunctionStub.class)
|
||||
)
|
||||
),
|
||||
element(PyElsePartStub.class)
|
||||
.withChildren(
|
||||
ifPart(2, 2, true),
|
||||
elifPart(2, 5, true),
|
||||
element(PyElsePartStub.class)
|
||||
.withChildren(
|
||||
element(PyClassStub.class)
|
||||
.withChildren(
|
||||
ifPart(3, 12, true)
|
||||
.withChildren(
|
||||
element(PyClassStub.class)
|
||||
.withChildren(
|
||||
ifPart(3, 11, false),
|
||||
element(PyElsePartStub.class)
|
||||
)
|
||||
),
|
||||
element(PyElsePartStub.class)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.test(file.getStub());
|
||||
}
|
||||
|
||||
public void testLoadingDeeperTreeRemainsKnownPsiElement() {
|
||||
final PyFile file = getTestFile();
|
||||
final List<PyClass> classes = file.getTopLevelClasses();
|
||||
@@ -282,13 +321,14 @@ public class PyStubsTest extends PyTestCase {
|
||||
}
|
||||
|
||||
public void testImportInExcept() {
|
||||
final PyFileImpl file = (PyFileImpl) getTestFile();
|
||||
final PsiElement element = file.getElementNamed("tzinfo");
|
||||
assertTrue(element != null ? element.toString() : "null", element instanceof PyClass);
|
||||
assertNotParsed(file);
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON26, () -> {
|
||||
final PyFileImpl file = (PyFileImpl)getTestFile();
|
||||
final PsiElement element = file.getElementNamed("tzinfo");
|
||||
assertTrue(String.valueOf(element), element instanceof PyClass);
|
||||
assertNotParsed(file);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void testImportFeatures() {
|
||||
final PyFileImpl file = (PyFileImpl) getTestFile();
|
||||
assertTrue(file.hasImportFromFuture(FutureFeature.DIVISION));
|
||||
@@ -1123,7 +1163,6 @@ public class PyStubsTest extends PyTestCase {
|
||||
doTestTypeParameterStub(typeAliasStatement, file);
|
||||
}
|
||||
|
||||
|
||||
private void doTestTypingTypedDictArguments() {
|
||||
doTestTypedDict("name", Arrays.asList("x", "y"), Arrays.asList("str", "int"), QualifiedName.fromComponents("TypedDict"));
|
||||
}
|
||||
@@ -1233,4 +1272,37 @@ public class PyStubsTest extends PyTestCase {
|
||||
assertEquals(initValue, fieldStub.initValue());
|
||||
}
|
||||
}
|
||||
|
||||
private static @NotNull StubElementValidator element(@NotNull Class<?> clazz) {
|
||||
return stub -> assertInstanceOf(stub, clazz);
|
||||
}
|
||||
|
||||
private static @NotNull StubElementValidator ifPart(int major, int minor, boolean isLessThan) {
|
||||
return stub -> {
|
||||
assertInstanceOf(stub, PyIfPartIfStub.class);
|
||||
assertEquals(new PyVersionCheck(new Version(major, minor, 0), isLessThan), ((PyIfPartIfStub)stub).getVersionCheck());
|
||||
};
|
||||
}
|
||||
|
||||
private static @NotNull StubElementValidator elifPart(int major, int minor, boolean isLessThan) {
|
||||
return stub -> {
|
||||
assertInstanceOf(stub, PyIfPartElifStub.class);
|
||||
assertEquals(new PyVersionCheck(new Version(major, minor, 0), isLessThan), ((PyIfPartElifStub)stub).getVersionCheck());
|
||||
};
|
||||
}
|
||||
|
||||
private interface StubElementValidator {
|
||||
void test(@NotNull StubElement<?> stub);
|
||||
|
||||
default @NotNull StubElementValidator withChildren(StubElementValidator @NotNull ... children) {
|
||||
return stub -> {
|
||||
test(stub);
|
||||
List<StubElement<?>> childrenStubs = stub.getChildrenStubs();
|
||||
assertSize(children.length, childrenStubs);
|
||||
for (int i = 0; i != children.length; i++) {
|
||||
children[i].test(childrenStubs.get(i));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user