From 1d2745ec25e3b1de12dfaa4df42598ad43846ed9 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 10 Jan 2018 18:58:27 +0300 Subject: [PATCH] Reduce unstubbing in PyDataclassesTypeProvider and PyStdlibTypeProvider (PY-27398) By introducing `hasAssignedValue` for target and its stub. --- .../IpnbPyTargetExpressionElementType.java | 2 ++ .../python/psi/PyTargetExpression.java | 12 ++++++++++ .../psi/stubs/PyTargetExpressionStub.java | 2 ++ .../stdlib/PyDataclassesTypeProvider.kt | 9 +++++++- .../stdlib/PyStdlibTypeProvider.java | 12 +++++++++- .../psi/impl/PyTargetExpressionImpl.java | 9 ++++++++ .../stubs/PyTargetExpressionElementType.java | 22 ++++++++++++++----- .../stubs/PyTargetExpressionStubImpl.java | 17 ++++++++++++-- .../a.py | 3 +++ .../b.py | 6 +++++ .../PyArgumentListInspectionTest.java | 4 ++++ 11 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 python/testData/inspections/PyArgumentListInspection/InitializingImportedTypingNamedTupleInheritor/a.py create mode 100644 python/testData/inspections/PyArgumentListInspection/InitializingImportedTypingNamedTupleInheritor/b.py diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyTargetExpressionElementType.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyTargetExpressionElementType.java index 9c53cadcf4e8..bae74cbef32a 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyTargetExpressionElementType.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyTargetExpressionElementType.java @@ -27,11 +27,13 @@ public class IpnbPyTargetExpressionElementType extends PyTargetExpressionElement super("IPNB_TARGET_EXPRESSION"); } + @Override @NotNull public PsiElement createElement(@NotNull final ASTNode node) { return new IpnbPyTargetExpression(node); } + @Override public PyTargetExpression createPsi(@NotNull final PyTargetExpressionStub stub) { return new IpnbPyTargetExpression(stub); } diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyTargetExpression.java b/python/psi-api/src/com/jetbrains/python/psi/PyTargetExpression.java index a248dee2d144..18307c88ba5b 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyTargetExpression.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyTargetExpression.java @@ -66,6 +66,18 @@ public interface PyTargetExpression extends PyQualifiedExpression, PsiNamedEleme @Nullable QualifiedName getCalleeName(); + @Override @NotNull PsiReference getReference(); + + /** + * Checks if target has assigned value. + * + * This method does not access AST if underlying PSI is stub based. + * + * @return true if target has assigned expression, false otherwise (e.g. in type declaration statement). + */ + default boolean hasAssignedValue() { + return true; + } } diff --git a/python/psi-api/src/com/jetbrains/python/psi/stubs/PyTargetExpressionStub.java b/python/psi-api/src/com/jetbrains/python/psi/stubs/PyTargetExpressionStub.java index 2c9ce93f3586..da710a3d44de 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/stubs/PyTargetExpressionStub.java +++ b/python/psi-api/src/com/jetbrains/python/psi/stubs/PyTargetExpressionStub.java @@ -63,4 +63,6 @@ public interface PyTargetExpressionStub extends NamedStub, P @Nullable String getDocString(); + + boolean hasAssignedValue(); } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyDataclassesTypeProvider.kt b/python/src/com/jetbrains/python/codeInsight/stdlib/PyDataclassesTypeProvider.kt index 975abe937793..5e19c67e8935 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyDataclassesTypeProvider.kt +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyDataclassesTypeProvider.kt @@ -60,10 +60,17 @@ class PyDataclassesTypeProvider : PyTypeProviderBase() { } val parameters = ArrayList() + val ellipsis = PyElementGenerator.getInstance(cls.project).createEllipsis() cls.processClassLevelDeclarations { element, _ -> if (element is PyTargetExpression && !PyTypingTypeProvider.isClassVar(element, context)) { - parameters.add(PyCallableParameterImpl.nonPsi(element.name, getTypeForParameter(element, context), element.findAssignedValue())) + val value = when { + context.maySwitchToAST(element) -> element.findAssignedValue() + element.hasAssignedValue() -> ellipsis + else -> null + } + + parameters.add(PyCallableParameterImpl.nonPsi(element.name, getTypeForParameter(element, context), value)) } true diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index 0c6ab0917ed3..b2c12e9814f2 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -630,9 +630,19 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { } ); + final PyNoneLiteralExpression ellipsis = PyElementGenerator.getInstance(cls.getProject()).createEllipsis(); + final Collector> toNTFields = Collectors.toMap(PyTargetExpression::getName, - field -> new PyNamedTupleType.FieldTypeAndDefaultValue(context.getType(field), field.findAssignedValue()), + field -> { + final PyExpression value = context.maySwitchToAST(field) + ? field.findAssignedValue() + : field.hasAssignedValue() + ? ellipsis + : null; + + return new PyNamedTupleType.FieldTypeAndDefaultValue(context.getType(field), value); + }, (v1, v2) -> v2, LinkedHashMap::new); diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java index 68d85f305e59..4a9d93a773ef 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java @@ -792,4 +792,13 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl imp private final boolean myQualified; private final String myTypeComment; private final String myAnnotation; + private final boolean myHasAssignedValue; @Nullable private final String myDocString; private final CustomTargetExpressionStub myCustomStub; @@ -40,13 +41,15 @@ public class PyTargetExpressionStubImpl extends StubBase imp public PyTargetExpressionStubImpl(String name, @Nullable String docString, @Nullable String typeComment, - @Nullable String annotation, + @Nullable String annotation, + boolean hasAssignedValue, CustomTargetExpressionStub customStub, StubElement parent) { super(parent, PyElementTypes.TARGET_EXPRESSION); myName = name; myTypeComment = typeComment; myAnnotation = annotation; + myHasAssignedValue = hasAssignedValue; myInitializerType = InitializerType.Custom; myInitializer = null; myQualified = false; @@ -60,12 +63,14 @@ public class PyTargetExpressionStubImpl extends StubBase imp final QualifiedName initializer, final boolean qualified, @Nullable String typeComment, - String annotation, + @Nullable String annotation, + boolean hasAssignedValue, final StubElement parentStub) { super(parentStub, PyElementTypes.TARGET_EXPRESSION); myName = name; myTypeComment = typeComment; myAnnotation = annotation; + myHasAssignedValue = hasAssignedValue; assert initializerType != InitializerType.Custom; myInitializerType = initializerType; myInitializer = initializer; @@ -74,14 +79,17 @@ public class PyTargetExpressionStubImpl extends StubBase imp myDocString = docString; } + @Override public String getName() { return myName; } + @Override public InitializerType getInitializerType() { return myInitializerType; } + @Override public QualifiedName getInitializer() { return myInitializer; } @@ -118,6 +126,11 @@ public class PyTargetExpressionStubImpl extends StubBase imp return myAnnotation; } + @Override + public boolean hasAssignedValue() { + return myHasAssignedValue; + } + @Override public String toString() { return "PyTargetExpressionStub(name=" + myName + ")"; diff --git a/python/testData/inspections/PyArgumentListInspection/InitializingImportedTypingNamedTupleInheritor/a.py b/python/testData/inspections/PyArgumentListInspection/InitializingImportedTypingNamedTupleInheritor/a.py new file mode 100644 index 000000000000..8979c04dff2c --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/InitializingImportedTypingNamedTupleInheritor/a.py @@ -0,0 +1,3 @@ +import b + +b.MyTup(5, "5") \ No newline at end of file diff --git a/python/testData/inspections/PyArgumentListInspection/InitializingImportedTypingNamedTupleInheritor/b.py b/python/testData/inspections/PyArgumentListInspection/InitializingImportedTypingNamedTupleInheritor/b.py new file mode 100644 index 000000000000..c3d2eee450d8 --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/InitializingImportedTypingNamedTupleInheritor/b.py @@ -0,0 +1,6 @@ +import typing + + +class MyTup(typing.NamedTuple): + bar: int + baz: str = "" diff --git a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java index bff8f3a52585..71831e83fba9 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java @@ -333,4 +333,8 @@ public class PyArgumentListInspectionTest extends PyInspectionTestCase { public void testInitializingDataclass() { runWithLanguageLevel(LanguageLevel.PYTHON37, this::doMultiFileTest); } + + public void testInitializingImportedTypingNamedTupleInheritor() { + runWithLanguageLevel(LanguageLevel.PYTHON37, this::doMultiFileTest); + } }