diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyClassRef.java b/python/psi-api/src/com/jetbrains/python/psi/PyClassRef.java index 7ac54083264f..950972b8f671 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClassRef.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClassRef.java @@ -2,31 +2,41 @@ package com.jetbrains.python.psi; import com.intellij.psi.PsiElement; import com.jetbrains.python.psi.impl.PyQualifiedName; +import com.jetbrains.python.psi.types.PyClassType; import org.jetbrains.annotations.Nullable; /** * @author yole */ public class PyClassRef { - @Nullable - private final PsiElement myElement; - - @Nullable - private final String myQName; + @Nullable private final PsiElement myElement; + @Nullable private final String myQName; + @Nullable private final PyClassType myType; public PyClassRef(@Nullable PsiElement element) { myElement = element; myQName = null; + myType = null; } public PyClassRef(@Nullable String qName) { myElement = null; myQName = qName; + myType = null; + } + + public PyClassRef(@Nullable PyClassType type) { + myElement = null; + myQName = null; + myType = type; } @Nullable public PyClass getPyClass() { - return myElement instanceof PyClass ? (PyClass) myElement : null; + if (myElement instanceof PyClass) { + return (PyClass) myElement; + } + return null; } @Nullable @@ -34,26 +44,40 @@ public class PyClassRef { return myElement; } + @Nullable + public PyClassType getType() { + return myType; + } + @Nullable public String getClassName() { if (myElement instanceof PyClass) { return ((PyClass)myElement).getName(); } - if (myQName != null) { + else if (myQName != null) { final PyQualifiedName qname = PyQualifiedName.fromDottedString(myQName); if (qname != null) { return qname.getLastComponent(); } } + else if (myType != null) { + return myType.getName(); + } return null; } @Nullable public String getQualifiedName() { - if (myQName != null) { + if (myElement instanceof PyClass) { + return ((PyClass)myElement).getQualifiedName(); + } + else if (myQName != null) { return myQName; } - return myElement instanceof PyClass ? ((PyClass)myElement).getQualifiedName() : null; + else if (myType != null) { + return myType.getName(); + } + return null; } @Override diff --git a/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java b/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java index 77d89834b3d9..38dfcac02dae 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java +++ b/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java @@ -27,7 +27,7 @@ public interface PyTypeProvider { PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context); @Nullable - PyType getIterationType(PyClass iterable); + PyType getIterationType(@NotNull PyClass iterable); @Nullable PyType getContextManagerVariableType(PyClass contextManager, PyExpression withExpression, TypeEvalContext context); diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java index 2ae7b32b5998..ed347a8183df 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java @@ -92,7 +92,7 @@ public class PyTypeProviderBase implements PyTypeProvider { } @Override - public PyType getIterationType(PyClass iterable) { + public PyType getIterationType(@NotNull PyClass iterable) { return null; } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/TypeAssertionIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/TypeAssertionIntention.java index 06f9d624f4a2..324cbca8b755 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/TypeAssertionIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/TypeAssertionIntention.java @@ -59,7 +59,7 @@ public class TypeAssertionIntention implements IntentionAction { (reference != null && reference.resolve() == null)) { return false; } - final PyType type = problemElement.getType(TypeEvalContext.slow()); + final PyType type = problemElement.getType(TypeEvalContext.fastStubOnly(file)); return (type == null || type instanceof PyReturnTypeReference); } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java index 067ef6a39817..35dc324e607a 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/TypeIntention.java @@ -59,7 +59,7 @@ public abstract class TypeIntention implements IntentionAction { protected abstract void updateText(boolean isReturn); private static boolean isTypeUndefined(PyExpression problemElement) { - final PyType type = problemElement.getType(TypeEvalContext.slow()); + final PyType type = problemElement.getType(TypeEvalContext.fastStubOnly(problemElement.getContainingFile())); if (type == null || type instanceof PyReturnTypeReference || type instanceof PyDynamicallyEvaluatedType) { PsiReference reference = problemElement.getReference(); if (problemElement instanceof PyQualifiedExpression) { @@ -74,6 +74,9 @@ public abstract class TypeIntention implements IntentionAction { } private static boolean isDefinedInAnnotation(PyExpression problemElement, PsiReference reference) { + if (LanguageLevel.forElement(problemElement).isOlderThan(LanguageLevel.PYTHON30)) { + return false; + } final PsiElement resolved = reference != null? reference.resolve() : null; PyParameter parameter = getParameter(problemElement, resolved); @@ -87,7 +90,7 @@ public abstract class TypeIntention implements IntentionAction { final PsiReference psiReference = callee.getReference(); if (psiReference != null && psiReference.resolve() == null) return false; } - final Callable callable = ((PyCallExpression)assignedValue).resolveCalleeFunction(PyResolveContext.defaultContext()); + final Callable callable = ((PyCallExpression)assignedValue).resolveCalleeFunction(getResolveContext(problemElement)); if (callable instanceof PyFunction && ((PyFunction)callable).getAnnotation() != null) return true; } @@ -129,9 +132,10 @@ public abstract class TypeIntention implements IntentionAction { if (callExpression != null) { final PyExpression callee = callExpression.getCallee(); if (callee instanceof PyQualifiedExpression) { - final Callable pyFunction = callExpression.resolveCalleeFunction(PyResolveContext.defaultContext()); + final TypeEvalContext context = TypeEvalContext.fastStubOnly(callee.getContainingFile()); + final Callable pyFunction = callExpression.resolveCalleeFunction(PyResolveContext.defaultContext().withTypeEvalContext(context)); if (pyFunction instanceof PyFunction) { - PyType type = pyFunction.getReturnType(TypeEvalContext.slow(), (PyQualifiedExpression)callee); + PyType type = pyFunction.getReturnType(context, (PyQualifiedExpression)callee); if (type == null || type instanceof PyReturnTypeReference) { final PsiReference reference = callee.getReference(); if (reference instanceof PsiPolyVariantReference) { @@ -187,13 +191,17 @@ public abstract class TypeIntention implements IntentionAction { protected static Callable getCallable(PsiElement elementAt) { PyCallExpression callExpression = getCallExpression(elementAt); - if (callExpression != null) { - final Callable callable = callExpression.resolveCalleeFunction(PyResolveContext.defaultContext()); - return callable == null? PsiTreeUtil.getParentOfType(elementAt, PyFunction.class) : callable; + if (callExpression != null && elementAt != null) { + final Callable callable = callExpression.resolveCalleeFunction(getResolveContext(elementAt)); + return callable == null ? PsiTreeUtil.getParentOfType(elementAt, PyFunction.class) : callable; } return PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); } + private static PyResolveContext getResolveContext(@NotNull PsiElement origin) { + return PyResolveContext.defaultContext().withTypeEvalContext(TypeEvalContext.fastStubOnly(origin.getContainingFile())); + } + public boolean startInWriteAction() { return true; } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java index 398a1ff20ac0..2faf7928d249 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java @@ -49,7 +49,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType if (myFields.contains(name)) { return Collections.singletonList(new RatedResolveResult(1000, new PyElementImpl(myDeclaration.getNode()))); } - return Collections.emptyList(); + return null; } @Override diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index 4e03bd564f9e..dbbb0ed2b827 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -11,6 +11,7 @@ import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyQualifiedName; import com.jetbrains.python.psi.impl.PyTypeProvider; +import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.QualifiedNameFinder; import com.jetbrains.python.psi.types.*; import org.jetbrains.annotations.NotNull; @@ -26,8 +27,8 @@ import java.util.Properties; * @author yole */ public class PyStdlibTypeProvider extends PyTypeProviderBase { - private Properties myStdlibTypes2 = new Properties(); - private Properties myStdlibTypes3 = new Properties(); + @NotNull private Properties myStdlibTypes2 = new Properties(); + @NotNull private Properties myStdlibTypes3 = new Properties(); @Nullable public static PyStdlibTypeProvider getInstance() { @@ -41,11 +42,29 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @Override public PyType getReferenceType(@NotNull PsiElement referenceTarget, @NotNull TypeEvalContext context, @Nullable PsiElement anchor) { - if (referenceTarget instanceof PyFunction && - PyNames.NAMEDTUPLE.equals(((PyFunction) referenceTarget).getName()) && - PyNames.COLLECTIONS_PY.equals(referenceTarget.getContainingFile().getName()) && - anchor instanceof PyCallExpression) { - return PyNamedTupleType.fromCall((PyCallExpression)anchor); + if (referenceTarget instanceof PyTargetExpression) { + final PyTargetExpression target = (PyTargetExpression)referenceTarget; + final PyQualifiedName calleeName = target.getCalleeName(); + if (calleeName != null && PyNames.NAMEDTUPLE.equals(calleeName.toString())) { + // TODO: Create stubs for namedtuple for preventing switch from stub to AST + final PyExpression value = target.findAssignedValue(); + if (value instanceof PyCallExpression) { + final PyCallExpression call = (PyCallExpression)value; + final PyCallExpression.PyMarkedCallee callee = call.resolveCallee(PyResolveContext.noImplicits()); + if (callee != null) { + final Callable callable = callee.getCallable(); + if (PyNames.COLLECTIONS_PY.equals(callable.getContainingFile().getName())) { + return PyNamedTupleType.fromCall(call); + } + } + } + } + } + else if (referenceTarget instanceof PyFunction && anchor instanceof PyCallExpression) { + if (PyNames.NAMEDTUPLE.equals(((PyFunction)referenceTarget).getName()) && + PyNames.COLLECTIONS_PY.equals(referenceTarget.getContainingFile().getName())) { + return PyNamedTupleType.fromCall((PyCallExpression)anchor); + } } return null; } @@ -115,8 +134,9 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { return null; } + @Nullable @Override - public PyType getIterationType(PyClass iterable) { + public PyType getIterationType(@NotNull PyClass iterable) { final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(iterable); if (builtinCache.hasInBuiltins(iterable)) { if ("file".equals(iterable.getName())) { @@ -128,7 +148,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @Nullable @Override - public PyType getContextManagerVariableType(PyClass contextManager, PyExpression withExpression, TypeEvalContext context) { + public PyType getContextManagerVariableType(@NotNull PyClass contextManager, @NotNull PyExpression withExpression, @NotNull TypeEvalContext context) { if ("contextlib.closing".equals(contextManager.getQualifiedName()) && withExpression instanceof PyCallExpression) { PyExpression closee = ((PyCallExpression)withExpression).getArgument(0, PyExpression.class); if (closee != null) { @@ -159,18 +179,8 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { } PyType argType = entry.getKey().getType(context); // Special case for the 'mode' argument of the 'open()' builtin - if (("__builtin__.open".equals(qname) || "io.open".equals(qname) || "os.fdopen".equals(qname)) && "mode".equals(name)) { - final PyBuiltinCache cache = PyBuiltinCache.getInstance(anchor); - final LanguageLevel level = LanguageLevel.forElement(anchor); - argType = cache.getUnicodeType(level); - final PyExpression modeExpr = entry.getKey(); - if (modeExpr instanceof PyStringLiteralExpression) { - final String literal = ((PyStringLiteralExpression)modeExpr).getStringValue(); - if (literal.contains("b")) { - argType = cache.getBytesType(level); - } - } - } + final PyExpression modeExpr = entry.getKey(); + argType = getOpenFunctionType(argType, qname, name, modeExpr, anchor); final PyType paramType = getParameterTypeByQName(overloadedQName, name, anchor); if (PyTypeChecker.match(paramType, argType, context)) { if (argType != null && paramType != null) { @@ -190,7 +200,29 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { return null; } - @Nullable PyType getParameterTypeByQName(@NotNull String functionQName, @NotNull String name, @NotNull PsiElement anchor) { + @Nullable + private PyType getOpenFunctionType(@Nullable PyType argType, + @NotNull String callQName, + @NotNull String parameterName, + @NotNull PyExpression modeExpr, + @NotNull PsiElement anchor) { + if (("__builtin__.open".equals(callQName) || "io.open".equals(callQName) || "os.fdopen".equals(callQName)) && + "mode".equals(parameterName)) { + final PyBuiltinCache cache = PyBuiltinCache.getInstance(anchor); + final LanguageLevel level = LanguageLevel.forElement(anchor); + argType = cache.getUnicodeType(level); + if (modeExpr instanceof PyStringLiteralExpression) { + final String literal = ((PyStringLiteralExpression)modeExpr).getStringValue(); + if (literal.contains("b")) { + argType = cache.getBytesType(level); + } + } + } + return argType; + } + + @Nullable + private PyType getParameterTypeByQName(@NotNull String functionQName, @NotNull String name, @NotNull PsiElement anchor) { final LanguageLevel level = LanguageLevel.forElement(anchor); final String key = String.format("Python%d/%s.%s", level.getVersion(), functionQName, name); final PyBuiltinCache cache = PyBuiltinCache.getInstance(anchor); @@ -212,7 +244,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { } @Nullable - private StructuredDocString getStructuredDocString(String qualifiedName, LanguageLevel level) { + private StructuredDocString getStructuredDocString(@NotNull String qualifiedName, @NotNull LanguageLevel level) { final Properties db = getStdlibTypes(level); final String docString = db.getProperty(qualifiedName); if (docString == null && level.isPy3K()) { //if we couldn't find for Py3K will search in Python2 db @@ -246,7 +278,8 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { return result; } - private Properties getStdlibTypes(LanguageLevel level) { + @NotNull + private Properties getStdlibTypes(@NotNull LanguageLevel level) { final Properties result = level.isPy3K() ? myStdlibTypes3 : myStdlibTypes2; final String name = level.isPy3K() ? "StdlibTypes3" : "StdlibTypes2"; if (result.isEmpty()) { diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 5b8dcf41b78c..bb7993ec14fb 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -682,7 +682,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { private static boolean hasUnresolvedAncestors(PyClass cls) { for (PyClassRef classRef : cls.iterateAncestors()) { - if (classRef.getPyClass() == null) { + if (classRef.getPyClass() == null && classRef.getType() == null) { return true; } } diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index a0bdbff59ac7..6cdce0a5a0e9 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -9,7 +9,6 @@ import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.lang.ASTFactory; import com.intellij.lang.ASTNode; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; @@ -38,6 +37,7 @@ import com.intellij.util.containers.HashSet; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType; import com.jetbrains.python.documentation.EpydocUtil; @@ -437,29 +437,23 @@ public class PyUtil { if (!(target instanceof PyTargetExpression)) { return false; } - PyFunction method = PsiTreeUtil.getParentOfType(target, PyFunction.class); - if (method == null || method.getContainingClass() == null) { - return false; + final ScopeOwner owner = ScopeUtil.getScopeOwner(target); + if (owner instanceof PyFunction) { + final PyFunction method = (PyFunction)owner; + if (method.getContainingClass() != null) { + final PyParameter[] params = method.getParameterList().getParameters(); + if (params.length > 0) { + final PyTargetExpression targetExpr = (PyTargetExpression)target; + final PyExpression qualifier = targetExpr.getQualifier(); + return qualifier != null && qualifier.getText().equals(params[0].getName()); + } + } } - final PyParameter[] params = method.getParameterList().getParameters(); - if (params.length == 0) { - return false; - } - final PyTargetExpression targetExpr = (PyTargetExpression)target; - PyExpression qualifier = targetExpr.getQualifier(); - return qualifier != null && qualifier.getText().equals(params[0].getName()); + return false; } public static boolean isClassAttribute(PsiElement element) { - PyAssignmentStatement statement = PsiTreeUtil.getParentOfType(element, PyAssignmentStatement.class); - if (statement == null) { - return false; - } - PyStatementList stmtList = PsiTreeUtil.getParentOfType(statement, PyStatementList.class); - if (stmtList == null || !(stmtList.getParent() instanceof PyClass)) { - return false; - } - return PsiTreeUtil.isAncestor(statement.getLeftHandSideExpression(), element, false); + return element instanceof PyTargetExpression && ScopeUtil.getScopeOwner(element) instanceof PyClass; } public static boolean isDocString(PyExpression expression) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java index 8ebf3b7b14ad..c67fdbb1efa6 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java @@ -174,6 +174,9 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress } final PyType providedType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(target, context, this); if (providedType != null) { + if (providedType instanceof PyClassType) { + return ((PyClassType)providedType).toInstance(); + } return providedType; } if (target instanceof Callable) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index d31f848985a9..4643f69118dc 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -17,6 +17,7 @@ import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.*; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.PyResolveUtil; @@ -25,6 +26,7 @@ import com.jetbrains.python.psi.stubs.PropertyStubStorage; 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.types.PyClassType; import com.jetbrains.python.psi.types.PyClassTypeImpl; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; @@ -147,20 +149,21 @@ public class PyClassImpl extends PyPresentableElementImpl implement final PyExpression[] superExpressions = getSuperClassExpressions(); List superClasses = new ArrayList(); for (PyExpression expr : superExpressions) { - expr = unfoldClass(expr); - if (expr instanceof PyReferenceExpression) { - final PsiPolyVariantReference ref = ((PyReferenceExpression)expr).getReference(PyResolveContext.noProperties()); - if (ref != null) { - final PsiElement result = ref.resolve(); - if (result != null) { - superClasses.add(result); - } - } - } + superClasses.add(classElementFromExpression(expr)); } return PsiUtilCore.toPsiElementArray(superClasses); } + @Nullable + public static PsiElement classElementFromExpression(@NotNull PyExpression expression) { + expression = unfoldClass(expression); + if (expression instanceof PyReferenceExpression) { + final PsiPolyVariantReference ref = ((PyReferenceExpression)expression).getReference(PyResolveContext.noProperties()); + return ref.resolve(); + } + return null; + } + public static PyExpression unfoldClass(PyExpression expression) { if (expression instanceof PyCallExpression) { PyCallExpression call = (PyCallExpression)expression; @@ -205,7 +208,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement } public PyDecoratorList getDecoratorList() { - return childToPsi(PyElementTypes.DECORATOR_LIST); + return getStubOrPsiChild(PyElementTypes.DECORATOR_LIST); } @Nullable @@ -265,9 +268,22 @@ public class PyClassImpl extends PyPresentableElementImpl implement List result = resolveSuperClassesFromStub(); if (result == null) { result = new ArrayList(); - PsiElement[] superClassElements = getSuperClassElements(); - for (PsiElement element : superClassElements) { - result.add(new PyClassRef(element)); + final TypeEvalContext context = TypeEvalContext.fastStubOnly(null); + final PyExpression[] superClassExpressions = getSuperClassExpressions(); + for (PyExpression expression : superClassExpressions) { + final PsiElement element = classElementFromExpression(expression); + if (element != null) { + result.add(new PyClassRef(element)); + } + else { + final PyType type = expression.getType(context); + if (type instanceof PyClassType) { + result.add(new PyClassRef((PyClassType)type)); + } + else { + result.add(new PyClassRef((PsiElement)null)); + } + } } } @@ -1078,7 +1094,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement @NotNull @Override public SearchScope getUseScope() { - final ScopeOwner scopeOwner = PsiTreeUtil.getParentOfType(this, ScopeOwner.class); + final ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(this); if (scopeOwner instanceof PyFunction) { return new LocalSearchScope(scopeOwner); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 8d8cc37eb9c3..40cd988060cd 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -21,6 +21,7 @@ import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonDocStringFinder; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.documentation.StructuredDocString; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.QualifiedNameFinder; @@ -202,7 +203,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp @Nullable private PyType getGenericReturnType(@NotNull TypeEvalContext typeEvalContext, @Nullable PyQualifiedExpression callSite) { - if (typeEvalContext.maySwitchToAST(this)) { + if (typeEvalContext.maySwitchToAST(this) && LanguageLevel.forElement(this).isAtLeast(LanguageLevel.PYTHON30)) { PyAnnotation anno = getAnnotation(); if (anno != null) { PyClass pyClass = anno.resolveToClass(); @@ -505,7 +506,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp @NotNull @Override public SearchScope getUseScope() { - final ScopeOwner scopeOwner = PsiTreeUtil.getParentOfType(this, ScopeOwner.class); + final ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(this); if (scopeOwner instanceof PyFunction) { return new LocalSearchScope(scopeOwner); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java index d038a7c8a212..ff883583f7c6 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportElementImpl.java @@ -55,9 +55,7 @@ public class PyImportElementImpl extends PyBaseElementImpl } public PyTargetExpression getAsNameElement() { - final ASTNode asNameNode = getNode().findChildByType(PyElementTypes.TARGET_EXPRESSION); - if (asNameNode == null) return null; - return (PyTargetExpression)asNameNode.getPsi(); + return getStubOrPsiChild(PyElementTypes.TARGET_EXPRESSION); } public String getAsName() { diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index 521f0e2e7f67..30088c1af90a 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -404,29 +404,6 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference return null; } - private static PsiElement transitiveResolve(PsiElement element) { - PsiElement prev = null; - while (element != prev) { - prev = element; - PsiReference ref = element.getReference(); - if (ref != null) { - PsiElement e = ref.resolve(); - if (e != null) { - element = e; - } - } - } - return element; - } - - private static boolean isGlobal(PsiElement anchor, String name) { - final ScopeOwner owner = ScopeUtil.getDeclarationScopeOwner(anchor, name); - if (owner != null) { - return ControlFlowCache.getScope(owner).isGlobal(name); - } - return false; - } - public boolean isReferenceTo(PsiElement element) { if (element instanceof PsiFileSystemItem) { // may be import via alias, so don't check if names match, do simple resolve check instead @@ -454,10 +431,6 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference final String elementName = ((PsiNamedElement)element).getName(); if ((Comparing.equal(myElement.getReferencedName(), elementName) || PyNames.INIT.equals(elementName))) { if (!haveQualifiers(element)) { - // Global elements may in fact be resolved to their outer declarations - if (isGlobal(element, elementName)) { - element = transitiveResolve(element); - } final ScopeOwner ourScopeOwner = ScopeUtil.getScopeOwner(getElement()); final ScopeOwner theirScopeOwner = ScopeUtil.getScopeOwner(element); // TODO: Cython-dependent code without CythonLanguageDialect.isInsideCythonFile() check @@ -468,7 +441,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference } } - final PsiElement resolveResult = (isGlobal(getElement(), elementName)) ? transitiveResolve(getElement()) : resolve(); + final PsiElement resolveResult = resolve(); if (resolveResult == element) { return true; } @@ -535,15 +508,16 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference private boolean resolvesToSameGlobal(PsiElement element, String elementName, ScopeOwner ourScopeOwner, ScopeOwner theirScopeOwner, PsiElement resolveResult) { // Handle situations when there is no top-level declaration for globals and transitive resolve doesn't help - final boolean ourIsGlobal = ControlFlowCache.getScope(ourScopeOwner).isGlobal(elementName); - final boolean theirIsGlobal = ControlFlowCache.getScope(theirScopeOwner).isGlobal(elementName); final PsiFile ourFile = getElement().getContainingFile(); final PsiFile theirFile = element.getContainingFile(); - - if (ourIsGlobal && theirIsGlobal && ourFile == theirFile) { - return true; + if (ourFile == theirFile) { + final boolean ourIsGlobal = ControlFlowCache.getScope(ourScopeOwner).isGlobal(elementName); + final boolean theirIsGlobal = ControlFlowCache.getScope(theirScopeOwner).isGlobal(elementName); + if (ourIsGlobal && theirIsGlobal) { + return true; + } } - if (theirIsGlobal && ScopeUtil.getScopeOwner(resolveResult) == ourFile) { + if (ScopeUtil.getScopeOwner(resolveResult) == ourFile && ControlFlowCache.getScope(theirScopeOwner).isGlobal(elementName)) { return true; } return false; diff --git a/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java b/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java index 894fe51f622e..8c3ca5edb3f5 100644 --- a/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java +++ b/python/src/com/jetbrains/python/psi/resolve/CompletionVariantsProcessor.java @@ -54,7 +54,7 @@ public class CompletionVariantsProcessor extends VariantsProcessor { final String params = StringUtil.join(parameterList.getParameters(), new Function() { @Override public String fun(PyParameter pyParameter) { - return pyParameter.getText(); + return pyParameter.getName(); } }, ", "); item = item.withTailText("(" + params + ")"); diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 8626533ad1c2..7dfd2861482f 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -11,6 +11,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiInvalidElementAccessException; +import com.intellij.psi.PsiReference; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; @@ -156,23 +157,25 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { for (PyClassRef superClass : myClass.iterateAncestors()) { final PyClass pyClass = superClass.getPyClass(); + final PsiElement element = superClass.getElement(); + final PyClassType type = superClass.getType(); if (pyClass != null) { PsiElement superMember = resolveClassMember(pyClass, myIsDefinition, name, null); if (superMember != null) { return ResolveResultList.to(superMember); } } - else { - final PsiElement element = superClass.getElement(); - if (element != null) { - for (PyTypeProvider typeProvider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { - final PyType refType = typeProvider.getReferenceType(element, resolveContext.getTypeEvalContext(), myClass); - if (refType != null) { - return refType.resolveMember(name, location, direction, resolveContext); - } + else if (element != null) { + for (PyTypeProvider typeProvider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { + final PyType refType = typeProvider.getReferenceType(element, resolveContext.getTypeEvalContext(), myClass); + if (refType != null) { + return refType.resolveMember(name, location, direction, resolveContext); } } } + else if (type != null) { + return type.resolveMember(name, location, direction, resolveContext); + } } if (isDefinition() && myClass.isNewStyleClass()) { PyClassType typeType = getMetaclassType(); @@ -209,13 +212,9 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { private PyClassType getMetaclassType() { final PyTargetExpression metaClassAttribute = myClass.findClassAttribute(PyNames.DUNDER_METACLASS, true); if (metaClassAttribute != null) { - final PyExpression metaclass = metaClassAttribute.findAssignedValue(); - if (metaclass instanceof PyReferenceExpression) { - final QualifiedResolveResult result = ((PyReferenceExpression)metaclass).followAssignmentsChain(PyResolveContext.noImplicits()); - PsiElement element = result.getElement(); - if (element instanceof PyClass) { - return new PyClassTypeImpl((PyClass)element, false); - } + final PyType type = metaClassAttribute.getType(TypeEvalContext.fastStubOnly(null)); + if (type instanceof PyClassType) { + return (PyClassType)type; } } return PyBuiltinCache.getInstance(myClass).getObjectType("type"); @@ -371,16 +370,33 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { Set namesAlready, ProcessingContext context, List ret) { - for (PyClass ancestor : myClass.getSuperClasses()) { - Object[] ancestry = (new PyClassTypeImpl(ancestor, myIsDefinition)).getCompletionVariants(name, expressionHook, context); - for (Object ob : ancestry) { - String inheritedName = ob.toString(); - if (!namesAlready.contains(inheritedName) && !isClassPrivate(inheritedName)) { - ret.add(ob); - namesAlready.add(inheritedName); + for (PyExpression expression : myClass.getSuperClassExpressions()) { + final PsiReference reference = expression.getReference(); + PsiElement element = null; + if (reference != null) { + element = reference.resolve(); + } + PyType type; + if (element instanceof PyClass) { + type = new PyClassTypeImpl((PyClass)element, myIsDefinition); + } + else { + type = expression.getType(TypeEvalContext.fastStubOnly(myClass.getContainingFile())); + if (type instanceof PyClassType && !myIsDefinition) { + type = ((PyClassType)type).toInstance(); } } - ContainerUtil.addAll(ret, ancestry); + if (type != null) { + Object[] ancestry = type.getCompletionVariants(name, expressionHook, context); + for (Object ob : ancestry) { + String inheritedName = ob.toString(); + if (!namesAlready.contains(inheritedName) && !isClassPrivate(inheritedName)) { + ret.add(ob); + namesAlready.add(inheritedName); + } + } + ContainerUtil.addAll(ret, ancestry); + } } } diff --git a/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java b/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java index 43525e5c0185..5d31de6ecd83 100644 --- a/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java +++ b/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java @@ -148,7 +148,7 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/baseClassAssignment.py b/python/testData/inspections/PyUnresolvedReferencesInspection/baseClassAssignment.py new file mode 100644 index 000000000000..a59c36b18e5a --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/baseClassAssignment.py @@ -0,0 +1,14 @@ +class A(object): + def foo(self): + pass + + +C = A + + +class B(C): + pass + + +b = B() +b.foo() #pass diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/namedTuple.py b/python/testData/inspections/PyUnresolvedReferencesInspection/namedTuple.py deleted file mode 100644 index 66b7904a60a3..000000000000 --- a/python/testData/inspections/PyUnresolvedReferencesInspection/namedTuple.py +++ /dev/null @@ -1,15 +0,0 @@ -from collections import namedtuple - -Point = namedtuple('Point', ['x', 'y'], verbose=True) - -print Point.x, Point.y - -p = Point(11, y=22) -print p.x + p.y -print p.__add__ -print p._asdict() -print Point._fields -print p._replace - -if isinstance(p, Point): - p.x \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedBaseClass.py b/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedBaseClass.py new file mode 100644 index 000000000000..ec7d11de2fe2 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedBaseClass.py @@ -0,0 +1,5 @@ +class C(B): + pass + +c = C() +c.foo() \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleAssignment/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleAssignment/a.py new file mode 100644 index 000000000000..3359d65e6f9b --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleAssignment/a.py @@ -0,0 +1,4 @@ +from b import Foo + +foo = Foo() +print(foo.bar, foo.baz) \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleAssignment/b.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleAssignment/b.py new file mode 100644 index 000000000000..fff1fef1ba2d --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleAssignment/b.py @@ -0,0 +1,4 @@ +from collections import namedtuple + + +Foo = namedtuple('Foo', 'bar spam') diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleBaseStub/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleBaseStub/a.py new file mode 100644 index 000000000000..ca7532eb3b44 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleBaseStub/a.py @@ -0,0 +1,4 @@ +from b import Bar + +bar = Bar() +print(bar.eggs, bar.bacon) diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleBaseStub/b.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleBaseStub/b.py new file mode 100644 index 000000000000..6cd989b22cca --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NamedTupleBaseStub/b.py @@ -0,0 +1,5 @@ +from collections import namedtuple + + +class Bar(namedtuple('Bar', 'spam eggs')): + pass diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/namedTuple.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/namedTuple.py new file mode 100644 index 000000000000..0caf93a11744 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/namedTuple.py @@ -0,0 +1,22 @@ +from collections import namedtuple + +Point = namedtuple('Point', ['x', 'y'], verbose=True) + +print(Point.x, Point.y) + +p = Point(11, y=22) +print(p.x + p.y + p.z) +print(p.__add__) +print(p._asdict()) +print(Point._fields) +print(p._replace) + +if isinstance(p, Point): + p.x + +class C(namedtuple('C', 'x y')): + def f(self): + return self + +c = C() +print(c.x, c.y, c.z, c.f()) diff --git a/python/testData/refactoring/rename/renameGlobal.py b/python/testData/refactoring/rename/renameGlobal.py new file mode 100644 index 000000000000..81761e47fd17 --- /dev/null +++ b/python/testData/refactoring/rename/renameGlobal.py @@ -0,0 +1,21 @@ +foo = [] + +def f1(): + global foo + foo = [1, 2, 3] + +def f2(x): + global foo + foo = foo + [x] + if 1 in foo: + return foo + +def f3(x): + return foo + [x] + +def f4(x): + global foo + return foo + [x] + +def f5(foo): + return foo diff --git a/python/testData/refactoring/rename/renameGlobal_after.py b/python/testData/refactoring/rename/renameGlobal_after.py new file mode 100644 index 000000000000..93791742a303 --- /dev/null +++ b/python/testData/refactoring/rename/renameGlobal_after.py @@ -0,0 +1,21 @@ +bar = [] + +def f1(): + global bar + bar = [1, 2, 3] + +def f2(x): + global bar + bar = bar + [x] + if 1 in bar: + return bar + +def f3(x): + return bar + [x] + +def f4(x): + global bar + return bar + [x] + +def f5(foo): + return foo diff --git a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java index 88306fd3658b..1b635b9f7ea2 100644 --- a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java +++ b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java @@ -33,6 +33,10 @@ public class Py3CompletionTest extends PyTestCase { assertTrue(strings.contains("long")); } + public void testNamedTupleBaseClass() { + doTest(); + } + private void doTest() { CamelHumpMatcher.forceStartMatching(getTestRootDisposable()); final String testName = "completion/" + getTestName(true); diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java new file mode 100644 index 000000000000..8c9d2b293cb1 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java @@ -0,0 +1,55 @@ +package com.jetbrains.python.inspections; + +import com.intellij.testFramework.LightProjectDescriptor; +import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.psi.LanguageLevel; +import org.jetbrains.annotations.NotNull; + +/** + * @author vlan + */ +public class Py3UnresolvedReferencesInspectionTest extends PyTestCase { + private static final String TEST_DIRECTORY = "inspections/PyUnresolvedReferencesInspection3K/"; + + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return ourPy3Descriptor; + } + + private void doTest() { + runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() { + @Override + public void run() { + myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py"); + myFixture.enableInspections(PyUnresolvedReferencesInspection.class); + myFixture.checkHighlighting(true, false, false); + } + }); + } + + private void doMultiFileTest(@NotNull final String filename) { + runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() { + @Override + public void run() { + final String testName = getTestName(false); + myFixture.copyDirectoryToProject(TEST_DIRECTORY + testName, ""); + myFixture.configureFromTempProjectFile(filename); + myFixture.enableInspections(PyUnresolvedReferencesInspection.class); + myFixture.checkHighlighting(true, false, false); + } + }); + } + + public void testNamedTuple() { + doTest(); + } + + public void testNamedTupleAssignment() { + doMultiFileTest("a.py"); + } + + // TODO: Currently there are no stubs for namedtuple() in the base classes list and no indicators for forcing stub->AST + public void _testNamedTupleBaseStub() { + doMultiFileTest("a.py"); + } +} diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 6e653624553d..9a55669d630c 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -58,10 +58,6 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } - public void testNamedTuple() { - doTest(); - } - // PY-2308 public void testTypeAssertions() { doTest(); @@ -231,6 +227,16 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + // PY-7301 + public void testUnresolvedBaseClass() { + doTest(); + } + + // PY-5427 + public void testBaseClassAssignment() { + doTest(); + } + private void doTest() { myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py"); myFixture.enableInspections(PyUnresolvedReferencesInspection.class); diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java index ca5e06eb7be7..da1c07175ce3 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java @@ -71,10 +71,14 @@ public class PyRenameTest extends PyTestCase { doTest("Shazam"); } + public void testRenameGlobal() { + doTest("bar"); + } + public void testRenameGlobalWithoutToplevel() { // PY-3547 doTest("bar"); } - + public void testRenameSlots() { // PY-4195 doTest("bacon"); }