PY-26062, PY-38540, PY-25935: Various Django performance improvements.

* Do not provide references for "open" in Django (already done by another code)
* Do not use full resolve for class and functions names. In most cases "resolve", "ManyToMany" and "ModelForm" are imported as-is, not with aliases.

GitOrigin-RevId: 8a0803e6b19482654c91186aa9754ea3d91da282
This commit is contained in:
Ilya.Kazakevich
2019-12-18 17:32:32 +00:00
committed by intellij-monorepo-bot
parent 388d7bf175
commit f8544fb8ee
4 changed files with 53 additions and 11 deletions
@@ -21,12 +21,14 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.util.PsiCacheKey;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.QualifiedName;
import com.intellij.util.Function;
import com.intellij.util.ObjectUtils;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.types.TypeEvalContext;
@@ -72,9 +74,22 @@ public final class NameResolverTools {
return false;
}
/**
* Same as {@link #isName(PyElement, FQNamesProvider...)} for named elements, but only checks name.
* Aliases not supported, but much lighter that way
*/
public static boolean isNameShortCut(@NotNull PyElement element, @NotNull FQNamesProvider... namesProviders) {
String name = element.getName();
if (name == null) {
return false;
}
return Arrays.stream(namesProviders).anyMatch(o -> getLastComponents(o).contains(name));
}
/**
* Checks if FQ element name is one of provided names. May be <strong>heavy</strong>.
* It is always better to use less accurate but lighter {@link #isCalleeShortCut(PyCallExpression, FQNamesProvider)}
* and {@link #isNameShortCut(PyElement, FQNamesProvider...)}
*
* @param element element to check
* @param namesProviders some enum that has one or more names
@@ -83,7 +98,8 @@ public final class NameResolverTools {
public static boolean isName(@NotNull final PyElement element, @NotNull final FQNamesProvider... namesProviders) {
assert element.isValid();
final Pair<String, String> qualifiedAndClassName = RecursionManager.doPreventingRecursion(element, false,
() -> QUALIFIED_AND_CLASS_NAME.getValue(element));
() -> QUALIFIED_AND_CLASS_NAME
.getValue(element));
LOG.assertTrue(qualifiedAndClassName != null);
//noinspection ConstantConditions
if (qualifiedAndClassName == null) return false;
@@ -119,7 +135,7 @@ public final class NameResolverTools {
}
/**
* Same as {@link #isName(PyElement, FQNamesProvider...)} for call expr, but first checks name.
* Same as {@link #isName(PyElement, FQNamesProvider...)} for call expr, but only checks name.
* Aliases not supported, but much lighter that way
*
* @param call expr
@@ -127,7 +143,7 @@ public final class NameResolverTools {
* @return true if callee is correct
*/
public static boolean isCalleeShortCut(@NotNull final PyCallExpression call,
@NotNull final FQNamesProvider function) {
@NotNull final FQNamesProvider... function) {
final PyExpression callee = call.getCallee();
if (callee == null) {
return false;
@@ -135,8 +151,7 @@ public final class NameResolverTools {
final String callableName = callee.getName();
final Collection<String> possibleNames = getLastComponents(function);
return possibleNames.contains(callableName) && call.isCallee(function);
return Arrays.stream(function).anyMatch(o -> getLastComponents(o).contains(callableName));
}
@NotNull
@@ -178,6 +193,7 @@ public final class NameResolverTools {
/**
* Check if class has parent with some name
*
* @param child class to check
*/
public static boolean isSubclass(@NotNull final PyClass child,
@@ -205,7 +221,8 @@ public final class NameResolverTools {
@Override
public boolean value(final PsiElement element) {
if (element instanceof PyCallExpression) {
return ((PyCallExpression)element).isCallee(myNameToSearch);
PyCallExpression callExpression = (PyCallExpression)element;
return isCalleeShortCut(callExpression, myNameToSearch);
}
return false;
}
@@ -233,14 +233,17 @@ public interface PyCallExpression extends PyCallSiteExpression {
/**
* Checks if the qualified name of the callee matches any of the specified names provided by provider.
* May be <strong>heavy</strong>.
* Use {@link NameResolverTools#isCalleeShortCut(PyCallExpression, FQNamesProvider)}
* if you can.
* May be <strong>heavy</strong>, and it is not recommended to use.
* Use {@link NameResolverTools#isCalleeShortCut(PyCallExpression, FQNamesProvider...)} or
* {@link com.jetbrains.extensions.python.PyCallExpressionExtKt#isCalleeName(PyCallExpression, FQNamesProvider...)}.
*
* @param name providers that provides one or more names to check
* @return true if matches, false otherwise
* @see com.jetbrains.python.nameResolver
* @see com.jetbrains.extensions.python.PyCallExpressionExtKt#isCalleeName(PyCallExpression, FQNamesProvider...)
* @deprecated use {@link com.jetbrains.extensions.python.PyCallExpressionExtKt#isCalleeName(PyCallExpression, FQNamesProvider...)}.
*/
@Deprecated
default boolean isCallee(@NotNull FQNamesProvider... name) {
final PyExpression callee = getCallee();
return callee instanceof PyReferenceExpression && NameResolverTools.isName(callee, name);
@@ -23,6 +23,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonFQDNNames;
import com.jetbrains.python.nameResolver.NameResolverTools;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import org.jetbrains.annotations.Contract;
@@ -302,7 +303,7 @@ public class PyEvaluator {
}
// Support dict([("k", "v")]) syntax
if (myEnableResolve && expression.isCallee(PythonFQDNNames.DICT_CLASS)) {
if (myEnableResolve && NameResolverTools.isCalleeShortCut(expression, PythonFQDNNames.DICT_CLASS)) {
final Collection<PyTupleExpression> tuples = PsiTreeUtil.findChildrenOfType(expression, PyTupleExpression.class);
if (!tuples.isEmpty()) {
final Map<Object, Object> result = new HashMap<>();
@@ -470,7 +471,7 @@ public class PyEvaluator {
@Nullable
private Object evaluateOrGet(@Nullable final PyExpression expression) {
final Object result = evaluate(expression);
if (result !=null) {
if (result != null) {
return result;
}
return myAllowExpressionsAsValues ? expression : null;
@@ -0,0 +1,21 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.extensions.python
import com.jetbrains.python.nameResolver.FQNamesProvider
import com.jetbrains.python.nameResolver.NameResolverTools
import com.jetbrains.python.psi.PyAssignmentStatement
import com.jetbrains.python.psi.PyCallExpression
import com.jetbrains.python.psi.PyPossibleClassMember
/**
* Checks if ``foo = SomeExpr()`` where foo is class attribute
*/
val PyCallExpression.isClassAttribute: Boolean
get() =
(parent as? PyAssignmentStatement)?.targets?.filterIsInstance<PyPossibleClassMember>()?.any { it.containingClass != null } == true
/**
* Checks if callee has certain name. Only name is checked, so import aliases aren't supported, but it works pretty fast
*/
fun PyCallExpression.isCalleeName(vararg names: FQNamesProvider): Boolean = NameResolverTools.isCalleeShortCut(this, *names)