mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'python-fixes'
This commit is contained in:
@@ -23,7 +23,6 @@ public class PyRequirement {
|
||||
private static final Pattern VERSION_SPEC = Pattern.compile("\\s*(<=?|>=?|==|!=)\\s*((\\w|[-.])+)");
|
||||
private static final Pattern EDITABLE_EGG = Pattern.compile("\\s*(-e)?\\s*([^#]*)#egg=(.*)");
|
||||
private static final Pattern RECURSIVE_REQUIREMENT = Pattern.compile("\\s*-r\\s+(.*)");
|
||||
private static final Pattern NAME_VERSION = Pattern.compile("\\s*(\\w(\\w|[.])*)-((\\w|[-.])+)");
|
||||
|
||||
public enum Relation {
|
||||
LT("<"),
|
||||
@@ -392,20 +391,23 @@ public class PyRequirement {
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
final Matcher nameVersionMatcher = NAME_VERSION.matcher(egg);
|
||||
if (nameVersionMatcher.matches()) {
|
||||
final String name = normalizeName(nameVersionMatcher.group(1));
|
||||
final String version = normalizeVersion(nameVersionMatcher.group(3));
|
||||
return new PyRequirement(name, version, url, editable);
|
||||
}
|
||||
else {
|
||||
final Matcher nameMatcher = NAME.matcher(egg);
|
||||
if (!nameMatcher.matches()) {
|
||||
return null;
|
||||
boolean isName = true;
|
||||
final List<String> nameParts = new ArrayList<String>();
|
||||
final List<String> versionParts = new ArrayList<String>();
|
||||
for (String part : StringUtil.split(egg, "-")) {
|
||||
if (part.matches("[0-9].*") || "dev".equals(part)) {
|
||||
isName = false;
|
||||
}
|
||||
if (isName) {
|
||||
nameParts.add(part);
|
||||
}
|
||||
else {
|
||||
versionParts.add(part);
|
||||
}
|
||||
final String name = normalizeName(nameMatcher.group(1));
|
||||
return new PyRequirement(name, null, url, editable);
|
||||
}
|
||||
final String name = normalizeName(StringUtil.join(nameParts, "-"));
|
||||
final String version = !versionParts.isEmpty() ? normalizeVersion(StringUtil.join(versionParts, "-")) : null;
|
||||
return new PyRequirement(name, version, url, editable);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||
import com.intellij.psi.StubBasedPsiElement;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.util.ArrayFactory;
|
||||
import com.intellij.util.Processor;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
@@ -161,4 +162,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
@Nullable
|
||||
String getDocStringValue();
|
||||
|
||||
boolean processClassLevelDeclarations(@NotNull PsiScopeProcessor processor);
|
||||
boolean processInstanceLevelDeclarations(@NotNull PsiScopeProcessor processor, @Nullable PyExpression location);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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.psi.PyBreakStatement;
|
||||
import com.jetbrains.python.psi.PyElementVisitor;
|
||||
import com.jetbrains.python.psi.PyLoopStatement;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
@@ -22,6 +22,19 @@ public class PyBreakStatementImpl extends PyElementImpl implements PyBreakStatem
|
||||
|
||||
@Nullable
|
||||
public PyLoopStatement getLoopStatement() {
|
||||
return PsiTreeUtil.getParentOfType(this, PyLoopStatement.class);
|
||||
return getLoopStatement(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyLoopStatement getLoopStatement(@NotNull PsiElement element) {
|
||||
final PyLoopStatement loop = PsiTreeUtil.getParentOfType(element, PyLoopStatement.class);
|
||||
if (loop instanceof PyForStatement) {
|
||||
final PyForStatement forLoop = (PyForStatement)loop;
|
||||
final PyElsePart elsePart = forLoop.getElsePart();
|
||||
if (PsiTreeUtil.isAncestor(elsePart, element, true)) {
|
||||
return getLoopStatement(loop);
|
||||
}
|
||||
}
|
||||
return loop;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionUtil;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.NotNullLazyValue;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
@@ -870,12 +869,12 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
// __init__ takes priority over all other methods
|
||||
PyFunctionImpl initMethod = (PyFunctionImpl)findMethodByName(PyNames.INIT, false);
|
||||
if (initMethod != null) {
|
||||
collectInstanceAttributes(initMethod, result, null);
|
||||
collectInstanceAttributes(initMethod, result);
|
||||
}
|
||||
final PyFunction[] methods = getMethods();
|
||||
for (PyFunction method : methods) {
|
||||
if (!PyNames.INIT.equals(method.getName())) {
|
||||
collectInstanceAttributes(method, result, null);
|
||||
collectInstanceAttributes(method, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,9 +882,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
return new ArrayList<PyTargetExpression>(expressions);
|
||||
}
|
||||
|
||||
private static void collectInstanceAttributes(@NotNull PyFunction method,
|
||||
@NotNull final Map<String, PyTargetExpression> result,
|
||||
@Nullable PsiElement anchor) {
|
||||
private static void collectInstanceAttributes(@NotNull PyFunction method, @NotNull final Map<String, PyTargetExpression> result) {
|
||||
final PyParameter[] params = method.getParameterList().getParameters();
|
||||
if (params.length == 0) {
|
||||
return;
|
||||
@@ -899,27 +896,6 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (anchor != null) {
|
||||
PyResolveUtil.scopeCrawlUp(new PsiScopeProcessor() {
|
||||
@Override
|
||||
public boolean execute(@NotNull PsiElement element, ResolveState state) {
|
||||
if (element instanceof PyAssignmentStatement) {
|
||||
collectNewTargets(result, (PyAssignmentStatement)element);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getHint(@NotNull Key<T> hintKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(Event event, @Nullable Object associated) {
|
||||
}
|
||||
}, anchor, null, method);
|
||||
}
|
||||
else {
|
||||
final PyStatementList statementList = method.getStatementList();
|
||||
if (statementList != null) {
|
||||
@@ -985,13 +961,8 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
return false;
|
||||
}
|
||||
|
||||
public void processDeclarations(@NotNull PsiScopeProcessor processor, @Nullable PyExpression location) {
|
||||
if (!processClassLevelDeclarations(processor)) return;
|
||||
if (!processInstanceLevelDeclarations(processor, location)) return;
|
||||
processor.execute(this, ResolveState.initial());
|
||||
}
|
||||
|
||||
public boolean processClassLevelDeclarations(PsiScopeProcessor processor) {
|
||||
@Override
|
||||
public boolean processClassLevelDeclarations(@NotNull PsiScopeProcessor processor) {
|
||||
final PyClassStub stub = getStub();
|
||||
if (stub != null) {
|
||||
final List<StubElement> children = stub.getChildrenStubs();
|
||||
@@ -1007,12 +978,13 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean processInstanceLevelDeclarations(PsiScopeProcessor processor, @Nullable PyExpression location) {
|
||||
@Override
|
||||
public boolean processInstanceLevelDeclarations(@NotNull PsiScopeProcessor processor, @Nullable PyExpression location) {
|
||||
Map<String, PyTargetExpression> declarationsInMethod = new HashMap<String, PyTargetExpression>();
|
||||
PyFunction instanceMethod = PsiTreeUtil.getParentOfType(location, PyFunction.class);
|
||||
final PyClass containingClass = instanceMethod != null ? instanceMethod.getContainingClass() : null;
|
||||
if (instanceMethod != null && containingClass != null && CompletionUtil.getOriginalElement(containingClass) == this) {
|
||||
collectInstanceAttributes(instanceMethod, declarationsInMethod, location);
|
||||
collectInstanceAttributes(instanceMethod, declarationsInMethod);
|
||||
for (PyTargetExpression targetExpression : declarationsInMethod.values()) {
|
||||
if (!processor.execute(targetExpression, ResolveState.initial())) {
|
||||
return false;
|
||||
|
||||
@@ -241,6 +241,7 @@ public class PyQualifiedReference extends PyReferenceImpl {
|
||||
public Object[] getVariants() {
|
||||
PyExpression qualifier = myElement.getQualifier();
|
||||
assert qualifier != null;
|
||||
final PyQualifiedExpression element = CompletionUtil.getOriginalOrSelf(myElement);
|
||||
|
||||
PyType qualifierType = qualifier.getType(TypeEvalContext.slow());
|
||||
ProcessingContext ctx = new ProcessingContext();
|
||||
@@ -270,11 +271,11 @@ public class PyQualifiedReference extends PyReferenceImpl {
|
||||
namesAlready.add(targetExpr.getName());
|
||||
}
|
||||
}
|
||||
Collections.addAll(variants, qualifierType.getCompletionVariants(myElement.getName(), myElement, ctx));
|
||||
Collections.addAll(variants, qualifierType.getCompletionVariants(element.getName(), element, ctx));
|
||||
return variants.toArray();
|
||||
}
|
||||
else {
|
||||
return qualifierType.getCompletionVariants(myElement.getName(), myElement, ctx);
|
||||
return qualifierType.getCompletionVariants(element.getName(), element, ctx);
|
||||
}
|
||||
}
|
||||
return getUntypedVariants();
|
||||
|
||||
@@ -402,35 +402,45 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
|
||||
}
|
||||
if (element instanceof PsiNamedElement) {
|
||||
final String elementName = ((PsiNamedElement)element).getName();
|
||||
if ((Comparing.equal(myElement.getReferencedName(), elementName) || PyNames.INIT.equals(elementName)) && !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
|
||||
if (element instanceof PyParameter || element instanceof PyTargetExpression || element instanceof CythonVariable) {
|
||||
// Check if the reference is in the same or inner scope of the element scope, not shadowed by an intermediate declaration
|
||||
if (resolvesToSameLocal(element, elementName, ourScopeOwner, theirScopeOwner)) {
|
||||
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
|
||||
if (element instanceof PyParameter || element instanceof PyTargetExpression || element instanceof CythonVariable) {
|
||||
// Check if the reference is in the same or inner scope of the element scope, not shadowed by an intermediate declaration
|
||||
if (resolvesToSameLocal(element, elementName, ourScopeOwner, theirScopeOwner)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiElement resolveResult = (isGlobal(getElement(), elementName)) ? transitiveResolve(getElement()) : resolve();
|
||||
if (resolveResult == element) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!haveQualifiers(element) && ourScopeOwner != null && theirScopeOwner != null) {
|
||||
if (resolvesToSameGlobal(element, elementName, ourScopeOwner, theirScopeOwner, resolveResult)) return true;
|
||||
}
|
||||
|
||||
if (resolvesToWrapper(element, resolveResult)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiElement resolveResult = (isGlobal(getElement(), elementName)) ? transitiveResolve(getElement()) : resolve();
|
||||
if (resolveResult == element) {
|
||||
return true;
|
||||
if (element instanceof PyExpression) {
|
||||
final PyExpression expr = (PyExpression)element;
|
||||
if (PyUtil.isClassAttribute(myElement) && (PyUtil.isClassAttribute(expr) || PyUtil.isInstanceAttribute(expr))) {
|
||||
final PyClass c1 = PsiTreeUtil.getParentOfType(element, PyClass.class);
|
||||
final PyClass c2 = PsiTreeUtil.getParentOfType(myElement, PyClass.class);
|
||||
if (c1 != null && c2 != null && (c1.isSubclass(c2) || c2.isSubclass(c1))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveQualifiers(element) && ourScopeOwner != null && theirScopeOwner != null) {
|
||||
if (resolvesToSameGlobal(element, elementName, ourScopeOwner, theirScopeOwner, resolveResult)) return true;
|
||||
}
|
||||
|
||||
if (resolvesToWrapper(element, resolveResult)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // TODO: handle multi-resolve
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -19,7 +19,6 @@ import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.PyDynamicMember;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyClassImpl;
|
||||
import com.jetbrains.python.psi.impl.PyTypeProvider;
|
||||
import com.jetbrains.python.psi.impl.ResolveResultList;
|
||||
import com.jetbrains.python.psi.resolve.*;
|
||||
@@ -112,10 +111,10 @@ public class PyClassType extends UserDataHolderBase implements PyCallableType {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<? extends RatedResolveResult> doResolveMember(String name,
|
||||
PyExpression location,
|
||||
AccessDirection direction,
|
||||
PyResolveContext resolveContext) {
|
||||
private List<? extends RatedResolveResult> doResolveMember(@NotNull String name,
|
||||
@Nullable PyExpression location,
|
||||
@NotNull AccessDirection direction,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
if (myClass == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -159,7 +158,7 @@ public class PyClassType extends UserDataHolderBase implements PyCallableType {
|
||||
}
|
||||
}
|
||||
|
||||
PsiElement classMember = resolveClassMember(this, name, location);
|
||||
PsiElement classMember = resolveClassMember(myClass, myIsDefinition, name, location);
|
||||
if (classMember != null) {
|
||||
return ResolveResultList.to(classMember);
|
||||
}
|
||||
@@ -167,7 +166,7 @@ public class PyClassType extends UserDataHolderBase implements PyCallableType {
|
||||
for (PyClassRef superClass : myClass.iterateAncestors()) {
|
||||
final PyClass pyClass = superClass.getPyClass();
|
||||
if (pyClass != null) {
|
||||
PsiElement superMember = resolveClassMember(new PyClassType(pyClass, isDefinition()), name, null);
|
||||
PsiElement superMember = resolveClassMember(pyClass, myIsDefinition, name, null);
|
||||
if (superMember != null) {
|
||||
return ResolveResultList.to(superMember);
|
||||
}
|
||||
@@ -243,15 +242,17 @@ public class PyClassType extends UserDataHolderBase implements PyCallableType {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement resolveClassMember(PyClassType aClass, String name, @Nullable PyExpression location) {
|
||||
PsiElement result = resolveInner(aClass.getPyClass(), name, location);
|
||||
private static PsiElement resolveClassMember(@NotNull PyClass cls,
|
||||
boolean isDefinition,
|
||||
@NotNull String name,
|
||||
@Nullable PyExpression location) {
|
||||
PsiElement result = resolveInner(cls, isDefinition, name, location);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static PsiElement resolveByMembersProviders(PyClassType aClass, String name) {
|
||||
for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
|
||||
@@ -263,15 +264,15 @@ public class PyClassType extends UserDataHolderBase implements PyCallableType {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement resolveInner(PyClass aClass, String name, @Nullable PyExpression location) {
|
||||
ResolveProcessor processor = new ResolveProcessor(name);
|
||||
((PyClassImpl)aClass).processDeclarations(processor, location); // our members are strictly within us.
|
||||
final PsiElement resolveResult = processor.getResult();
|
||||
//final PsiElement resolveResult = PyResolveUtil.treeWalkUp(new PyResolveUtil.ResolveProcessor(name), myClass, null, null);
|
||||
if (resolveResult != null && resolveResult != aClass) {
|
||||
return resolveResult;
|
||||
private static PsiElement resolveInner(@NotNull PyClass cls, boolean isDefinition, @NotNull String name, @Nullable PyExpression location) {
|
||||
final ResolveProcessor processor = new ResolveProcessor(name);
|
||||
if (!isDefinition) {
|
||||
if (!cls.processInstanceLevelDeclarations(processor, location)) {
|
||||
return processor.getResult();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
cls.processClassLevelDeclarations(processor);
|
||||
return processor.getResult();
|
||||
}
|
||||
|
||||
private static Key<Set<PyClassType>> CTX_VISITED = Key.create("PyClassType.Visited");
|
||||
@@ -350,13 +351,13 @@ public class PyClassType extends UserDataHolderBase implements PyCallableType {
|
||||
if (suppressParentheses) {
|
||||
processor.suppressParentheses();
|
||||
}
|
||||
((PyClassImpl)myClass).processClassLevelDeclarations(processor);
|
||||
myClass.processClassLevelDeclarations(processor);
|
||||
|
||||
List<String> slots = myClass.isNewStyleClass() ? myClass.getSlots() : null;
|
||||
if (slots != null) {
|
||||
processor.setAllowedNames(slots);
|
||||
}
|
||||
((PyClassImpl)myClass).processInstanceLevelDeclarations(processor, expressionHook);
|
||||
myClass.processInstanceLevelDeclarations(processor, expressionHook);
|
||||
|
||||
for (LookupElement le : processor.getResultList()) {
|
||||
String name = le.getLookupString();
|
||||
@@ -477,8 +478,4 @@ public class PyClassType extends UserDataHolderBase implements PyCallableType {
|
||||
public boolean isValid() {
|
||||
return myClass == null || myClass.isValid();
|
||||
}
|
||||
|
||||
public static PyClassType fromClassName(String typeName, Project project) {
|
||||
return new PyClassType(project, typeName, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ public class PyFindUsagesTest extends PyTestCase {
|
||||
|
||||
public void testReassignedClassAttribute() { // PY-4338
|
||||
final Collection<UsageInfo> usages = myFixture.testFindUsages("findUsages/ReassignedClassAttribute.py");
|
||||
assertEquals(5, usages.size());
|
||||
assertEquals(6, usages.size());
|
||||
}
|
||||
|
||||
public void testWrappedMethod() { // PY-5458
|
||||
|
||||
@@ -419,6 +419,17 @@ public class PyTypeTest extends PyTestCase {
|
||||
assertNull(type);
|
||||
}
|
||||
|
||||
// PY-7040
|
||||
public void testInstanceAndClassAttribute() {
|
||||
doTest("int",
|
||||
"class C(object):\n" +
|
||||
" foo = 'str1'\n" +
|
||||
"\n" +
|
||||
" def __init__(self):\n" +
|
||||
" self.foo = 3\n" +
|
||||
" expr = self.foo\n");
|
||||
}
|
||||
|
||||
private PyExpression parseExpr(String text) {
|
||||
myFixture.configureByText(PythonFileType.INSTANCE, text);
|
||||
return myFixture.findElementByText("expr", PyExpression.class);
|
||||
|
||||
Reference in New Issue
Block a user