Merge branch 'python-fixes'

This commit is contained in:
Andrey Vlasovskikh
2012-08-02 21:11:13 +04:00
9 changed files with 115 additions and 105 deletions
@@ -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);