mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-18816 Type comments for target expressions are stored inside their stubs
This commit is contained in:
committed by
Andrey Vlasovskikh
parent
57bbfd50d4
commit
a68e6981dc
@@ -27,7 +27,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public interface PyTargetExpression extends PyQualifiedExpression, PsiNamedElement, PsiNameIdentifierOwner, PyDocStringOwner,
|
||||
PyQualifiedNameOwner, PyReferenceOwner, StubBasedPsiElement<PyTargetExpressionStub>,
|
||||
PyPossibleClassMember {
|
||||
PyPossibleClassMember, PyTypeCommentOwner {
|
||||
PyTargetExpression[] EMPTY_ARRAY = new PyTargetExpression[0];
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,4 +63,7 @@ public interface PyTargetExpressionStub extends NamedStub<PyTargetExpression> {
|
||||
|
||||
@Nullable
|
||||
String getDocString();
|
||||
|
||||
@Nullable
|
||||
String getTypeComment();
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiPolyVariantReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
@@ -173,9 +172,9 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
|
||||
@Override
|
||||
public PyType getReferenceType(@NotNull PsiElement referenceTarget, TypeEvalContext context, @Nullable PsiElement anchor) {
|
||||
if (referenceTarget instanceof PyTargetExpression && context.maySwitchToAST(referenceTarget)) {
|
||||
if (referenceTarget instanceof PyTargetExpression) {
|
||||
final PyTargetExpression target = (PyTargetExpression)referenceTarget;
|
||||
final String comment = getTypeComment(target);
|
||||
final String comment = target.getTypeCommentAnnotation();
|
||||
if (comment != null) {
|
||||
final PyType type = getStringBasedType(comment, referenceTarget, new Context(context));
|
||||
if (type instanceof PyTupleType) {
|
||||
@@ -190,20 +189,6 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getTypeComment(@NotNull PyTargetExpression target) {
|
||||
final PsiElement commentContainer = PsiTreeUtil.getParentOfType(target, PyAssignmentStatement.class, PyWithStatement.class,
|
||||
PyForPart.class);
|
||||
if (commentContainer != null) {
|
||||
final PsiComment comment = getSameLineTrailingCommentChild(commentContainer);
|
||||
if (comment != null) {
|
||||
final String text = comment.getText();
|
||||
return getTypeCommentValue(text);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that text of a comment starts with the "type:" prefix and returns trimmed part afterwards. This trailing part is supposed to
|
||||
* contain type annotation in PEP 484 compatible format, that can be parsed with either {@link PyTypeParser#parse(PsiElement, String)}
|
||||
@@ -218,23 +203,6 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiComment getSameLineTrailingCommentChild(@NotNull PsiElement element) {
|
||||
PsiElement child = element.getFirstChild();
|
||||
while (true) {
|
||||
if (child == null) {
|
||||
return null;
|
||||
}
|
||||
if (child instanceof PsiComment) {
|
||||
return (PsiComment)child;
|
||||
}
|
||||
if (child.getText().contains("\n")) {
|
||||
return null;
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAny(@NotNull PyType type) {
|
||||
return type instanceof PyClassType && "typing.Any".equals(((PyClassType)type).getPyClass().getQualifiedName());
|
||||
}
|
||||
|
||||
@@ -62,7 +62,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 54;
|
||||
return 55;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.lang.ASTNode;
|
||||
import com.intellij.navigation.ItemPresentation;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiPolyVariantReference;
|
||||
import com.intellij.psi.PsiReference;
|
||||
@@ -35,6 +36,7 @@ import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.PythonDialectsTokenSetProvider;
|
||||
import com.jetbrains.python.codeInsight.PyTypingTypeProvider;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
|
||||
@@ -677,4 +679,52 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
|
||||
public String getQualifiedName() {
|
||||
return QualifiedNameFinder.getQualifiedName(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiComment getTypeComment() {
|
||||
final PsiElement commentContainer = PsiTreeUtil.getParentOfType(this,
|
||||
PyAssignmentStatement.class,
|
||||
PyWithStatement.class,
|
||||
PyForPart.class);
|
||||
if (commentContainer != null) {
|
||||
final PsiComment comment = getSameLineTrailingCommentChild(commentContainer);
|
||||
if (comment != null && PyTypingTypeProvider.getTypeCommentValue(comment.getText()) != null) {
|
||||
return comment;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiComment getSameLineTrailingCommentChild(@NotNull PsiElement element) {
|
||||
PsiElement child = element.getFirstChild();
|
||||
while (true) {
|
||||
if (child == null) {
|
||||
return null;
|
||||
}
|
||||
if (child instanceof PsiComment) {
|
||||
return (PsiComment)child;
|
||||
}
|
||||
if (child.getText().contains("\n")) {
|
||||
return null;
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTypeCommentAnnotation() {
|
||||
final PyTargetExpressionStub stub = getStub();
|
||||
if (stub != null) {
|
||||
return stub.getTypeComment();
|
||||
}
|
||||
|
||||
final PsiComment comment = getTypeComment();
|
||||
if (comment != null) {
|
||||
return PyTypingTypeProvider.getTypeCommentValue(comment.getText());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,10 +70,11 @@ public class PyTargetExpressionElementType extends PyStubElementType<PyTargetExp
|
||||
final String name = psi.getName();
|
||||
final PyExpression assignedValue = psi.findAssignedValue();
|
||||
final String docString = DocStringUtil.getDocStringValue(psi);
|
||||
final String typeComment = psi.getTypeCommentAnnotation();
|
||||
for (CustomTargetExpressionStubType customStubType : getCustomStubTypes()) {
|
||||
CustomTargetExpressionStub customStub = customStubType.createStub(psi);
|
||||
if (customStub != null) {
|
||||
return new PyTargetExpressionStubImpl(name, docString, customStub, parentStub);
|
||||
return new PyTargetExpressionStubImpl(name, docString, typeComment, customStub, parentStub);
|
||||
}
|
||||
}
|
||||
PyTargetExpressionStub.InitializerType initializerType = PyTargetExpressionStub.InitializerType.Other;
|
||||
@@ -89,7 +90,7 @@ public class PyTargetExpressionElementType extends PyStubElementType<PyTargetExp
|
||||
initializer = ((PyReferenceExpression) callee).asQualifiedName();
|
||||
}
|
||||
}
|
||||
return new PyTargetExpressionStubImpl(name, docString, initializerType, initializer, psi.isQualified(), parentStub);
|
||||
return new PyTargetExpressionStubImpl(name, docString, initializerType, initializer, psi.isQualified(), typeComment, parentStub);
|
||||
}
|
||||
|
||||
public void serialize(@NotNull final PyTargetExpressionStub stub, @NotNull final StubOutputStream stream)
|
||||
@@ -98,6 +99,7 @@ public class PyTargetExpressionElementType extends PyStubElementType<PyTargetExp
|
||||
final String docString = stub.getDocString();
|
||||
stream.writeUTFFast(docString != null ? docString : "");
|
||||
stream.writeVarInt(stub.getInitializerType().getIndex());
|
||||
stream.writeName(stub.getTypeComment());
|
||||
final CustomTargetExpressionStub customStub = stub.getCustomStub(CustomTargetExpressionStub.class);
|
||||
if (customStub != null) {
|
||||
stream.writeName(customStub.getTypeClass().getCanonicalName());
|
||||
@@ -118,19 +120,21 @@ public class PyTargetExpressionElementType extends PyStubElementType<PyTargetExp
|
||||
docString = null;
|
||||
}
|
||||
PyTargetExpressionStub.InitializerType initializerType = PyTargetExpressionStub.InitializerType.fromIndex(stream.readVarInt());
|
||||
final StringRef typeCommentRef = stream.readName();
|
||||
final String typeComment = typeCommentRef == null ? null : typeCommentRef.getString();
|
||||
if (initializerType == PyTargetExpressionStub.InitializerType.Custom) {
|
||||
final String typeName = stream.readName().getString();
|
||||
for(CustomTargetExpressionStubType type: getCustomStubTypes()) {
|
||||
if (type.getClass().getCanonicalName().equals(typeName)) {
|
||||
CustomTargetExpressionStub stub = type.deserializeStub(stream);
|
||||
return new PyTargetExpressionStubImpl(name, docString, stub, parentStub);
|
||||
return new PyTargetExpressionStubImpl(name, docString, typeComment, stub, parentStub);
|
||||
}
|
||||
}
|
||||
throw new IOException("Unknown custom stub type " + typeName);
|
||||
}
|
||||
QualifiedName initializer = QualifiedName.deserialize(stream);
|
||||
boolean isQualified = stream.readBoolean();
|
||||
return new PyTargetExpressionStubImpl(name, docString, initializerType, initializer, isQualified, parentStub);
|
||||
return new PyTargetExpressionStubImpl(name, docString, initializerType, initializer, isQualified, typeComment, parentStub);
|
||||
}
|
||||
|
||||
public boolean shouldCreateStub(final ASTNode node) {
|
||||
|
||||
@@ -31,16 +31,19 @@ public class PyTargetExpressionStubImpl extends StubBase<PyTargetExpression> imp
|
||||
private final InitializerType myInitializerType;
|
||||
private final QualifiedName myInitializer;
|
||||
private final boolean myQualified;
|
||||
@Nullable private final String myDocString;
|
||||
private final String myTypeComment;
|
||||
|
||||
@Nullable private final String myDocString;
|
||||
private final CustomTargetExpressionStub myCustomStub;
|
||||
|
||||
public PyTargetExpressionStubImpl(String name,
|
||||
@Nullable String docString,
|
||||
@Nullable String typeComment,
|
||||
CustomTargetExpressionStub customStub,
|
||||
StubElement parent) {
|
||||
super(parent, PyElementTypes.TARGET_EXPRESSION);
|
||||
myName = name;
|
||||
myTypeComment = typeComment;
|
||||
myInitializerType = InitializerType.Custom;
|
||||
myInitializer = null;
|
||||
myQualified = false;
|
||||
@@ -48,12 +51,16 @@ public class PyTargetExpressionStubImpl extends StubBase<PyTargetExpression> imp
|
||||
myDocString = docString;
|
||||
}
|
||||
|
||||
public PyTargetExpressionStubImpl(final String name, @Nullable String docString, final InitializerType initializerType,
|
||||
public PyTargetExpressionStubImpl(final String name,
|
||||
@Nullable String docString,
|
||||
final InitializerType initializerType,
|
||||
final QualifiedName initializer,
|
||||
final boolean qualified,
|
||||
@Nullable String typeComment,
|
||||
final StubElement parentStub) {
|
||||
super(parentStub, PyElementTypes.TARGET_EXPRESSION);
|
||||
myName = name;
|
||||
myTypeComment = typeComment;
|
||||
assert initializerType != InitializerType.Custom;
|
||||
myInitializerType = initializerType;
|
||||
myInitializer = initializer;
|
||||
@@ -94,6 +101,12 @@ public class PyTargetExpressionStubImpl extends StubBase<PyTargetExpression> imp
|
||||
return myDocString;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getTypeComment() {
|
||||
return myTypeComment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PyTargetExpressionStub(name=" + myName + ")";
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
x = unknown() # type: int
|
||||
@@ -445,9 +445,24 @@ public class PyStubsTest extends PyTestCase {
|
||||
assertEquals("int", annotation);
|
||||
assertNotParsed(file);
|
||||
|
||||
final TypeEvalContext context = TypeEvalContext.codeInsightFallback(myFixture.getProject());
|
||||
final TypeEvalContext context = TypeEvalContext.codeAnalysis(myFixture.getProject(), file);
|
||||
final PyType paramType = context.getType(param);
|
||||
assertInstanceOf(paramType, PyClassType.class);
|
||||
assertNotParsed(file);
|
||||
}
|
||||
|
||||
public void testTargetExpressionTypeComment() {
|
||||
final PyFile file = getTestFile();
|
||||
final PyTargetExpression target = file.findTopLevelAttribute("x");
|
||||
assertNotNull(target);
|
||||
|
||||
final String annotation = target.getTypeCommentAnnotation();
|
||||
assertEquals("int", annotation);
|
||||
assertNotParsed(file);
|
||||
|
||||
final TypeEvalContext context = TypeEvalContext.codeAnalysis(myFixture.getProject(), file);
|
||||
final PyType paramType = context.getType(target);
|
||||
assertInstanceOf(paramType, PyClassType.class);
|
||||
assertNotParsed(file);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user