From 68565aedf8719d640da986447f7d89ab564a5fe1 Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Tue, 20 May 2014 22:34:03 +0400 Subject: [PATCH] PY-2980 Unresolved reference for ForeignKey defined as string --- .../python/nameResolver/FQNamesProvider.java | 5 +++ .../nameResolver/NameResolverTools.java | 42 ++++++++++--------- .../python/psi/PyCallExpression.java | 11 +++++ .../psi/impl/PyCallExpressionHelper.java | 22 ++++++++++ .../python/psi/impl/PyCallExpressionImpl.java | 7 ++++ .../python/psi/impl/PyDecoratorImpl.java | 7 ++++ 6 files changed, 74 insertions(+), 20 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/nameResolver/FQNamesProvider.java b/python/psi-api/src/com/jetbrains/python/nameResolver/FQNamesProvider.java index 1874f3b3708e..c8c80f41c591 100644 --- a/python/psi-api/src/com/jetbrains/python/nameResolver/FQNamesProvider.java +++ b/python/psi-api/src/com/jetbrains/python/nameResolver/FQNamesProvider.java @@ -12,4 +12,9 @@ public interface FQNamesProvider { */ @NotNull String[] getNames(); + + /** + * @return is name of class (true) or function (false) + */ + boolean isClass(); } diff --git a/python/psi-api/src/com/jetbrains/python/nameResolver/NameResolverTools.java b/python/psi-api/src/com/jetbrains/python/nameResolver/NameResolverTools.java index aaf32760898e..e8d8f515dd64 100644 --- a/python/psi-api/src/com/jetbrains/python/nameResolver/NameResolverTools.java +++ b/python/psi-api/src/com/jetbrains/python/nameResolver/NameResolverTools.java @@ -2,14 +2,13 @@ package com.jetbrains.python.nameResolver; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; +import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyElement; +import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyQualifiedNameOwner; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * @author Ilya.Kazakevich @@ -52,25 +51,28 @@ public final class NameResolverTools { elementToCheck = (PyElement)resolvedElement; } } + String qualifiedName = null; if (elementToCheck instanceof PyQualifiedNameOwner) { - final String qualifiedName = ((PyQualifiedNameOwner)elementToCheck).getQualifiedName(); - return getNames(namesProviders).contains(qualifiedName); + qualifiedName = ((PyQualifiedNameOwner)elementToCheck).getQualifiedName(); + } + String className = null; + if (elementToCheck instanceof PyFunction) { + final PyClass aClass = ((PyFunction)elementToCheck).getContainingClass(); + if (aClass != null) { + className = aClass.getQualifiedName(); + } + } + + for (final FQNamesProvider provider : namesProviders) { + final List names = Arrays.asList(provider.getNames()); + if (qualifiedName != null && names.contains(qualifiedName)) { + return true; + } + if (className != null && provider.isClass() && names.contains(className)) { + return true; + } } return false; } - /** - * Returns set of names all providers provide - * - * @param providers providers to check - * @return set of names - */ - @NotNull - private static Collection getNames(@NotNull final FQNamesProvider... providers) { - final Set result = new HashSet(); - for (final FQNamesProvider provider : providers) { - result.addAll(Arrays.asList(provider.getNames())); - } - return result; - } } diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyCallExpression.java b/python/psi-api/src/com/jetbrains/python/psi/PyCallExpression.java index 07dcc0fbfbb5..581c4257c430 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyCallExpression.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyCallExpression.java @@ -16,6 +16,7 @@ package com.jetbrains.python.psi; import com.intellij.psi.PsiElement; +import com.jetbrains.python.FunctionParameter; import com.jetbrains.python.nameResolver.FQNamesProvider; import com.jetbrains.python.psi.resolve.PyResolveContext; import org.jetbrains.annotations.NotNull; @@ -66,6 +67,16 @@ public interface PyCallExpression extends PyExpression { @Nullable T getArgument(int index, String keyword, Class argClass); + /** + * Returns the argument if one is present in the list. + * + * @param parameter parameter + * @param argClass argument expected type + * @return the argument or null + */ + @Nullable + T getArgument(@NotNull final FunctionParameter parameter, @NotNull Class argClass); + @Nullable PyExpression getKeywordArgument(String keyword); diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 712e8b541a1e..b9fe8c342054 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.psi.ResolveResult; import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.python.FunctionParameter; import com.jetbrains.python.PyNames; import com.jetbrains.python.nameResolver.FQNamesProvider; import com.jetbrains.python.nameResolver.NameResolverTools; @@ -361,6 +362,27 @@ public class PyCallExpressionHelper { return false; } + + /** + * Returns argument if it exists and has appropriate type + * @param parameter argument + * @param argClass expected class + * @param expression call expression + * @param expected class + * @return argument expression or null if has wrong type of does not exist + */ + @Nullable + public static T getArgument( + @NotNull final FunctionParameter parameter, + @NotNull final Class argClass, + @NotNull final PyCallExpression expression) { + final PyArgumentList list = expression.getArgumentList(); + if (list == null) { + return null; + } + return PyUtil.as(list.getValueExpressionForParam(parameter), argClass); + } + @Nullable public static PyExpression getKeywordArgument(PyCallExpression expr, String keyword) { for (PyExpression arg : expr.getArguments()) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java index 63e8a71c5741..70d48aad1b06 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java @@ -18,6 +18,7 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.python.FunctionParameter; import com.jetbrains.python.nameResolver.FQNamesProvider; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.PyResolveContext; @@ -72,6 +73,12 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress return getArgument(index, argClass); } + @Nullable + @Override + public T getArgument(@NotNull final FunctionParameter parameter, @NotNull final Class argClass) { + return PyCallExpressionHelper.getArgument(parameter, argClass, this); + } + @Override public PyExpression getKeywordArgument(String keyword) { return PyCallExpressionHelper.getKeywordArgument(this, keyword); diff --git a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java index 9be3850ed2b3..cda266ef66c1 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java @@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; import com.intellij.util.IncorrectOperationException; +import com.jetbrains.python.FunctionParameter; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonDialectsTokenSetProvider; @@ -128,6 +129,12 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase im return getArgument(index, argClass); } + @Nullable + @Override + public T getArgument(@NotNull final FunctionParameter parameter, @NotNull final Class argClass) { + return PyCallExpressionHelper.getArgument(parameter, argClass, this); + } + @Override public PyExpression getKeywordArgument(String keyword) { return PyCallExpressionHelper.getKeywordArgument(this, keyword);