Fixed unresolved references inspection for fields defined in __new__ (PY-6805)

The inspection may show false negative warnings for assigned attributes of objects
other than the return value of __new__.
This commit is contained in:
Andrey Vlasovskikh
2013-04-12 14:42:45 +04:00
parent eb3081f1fa
commit 072b3887c1
10 changed files with 58 additions and 3 deletions

View File

@@ -95,6 +95,10 @@ __builtin__.next = \
:type iterator: collections.Iterator of T \n\
:rtype: T \n\
__builtin__.object.__new__ = \n\
:type cls: T \n\
:rtype: T \n\
__builtin__.open = \
:type name: string \n\
:type mode: string \n\

View File

@@ -47,7 +47,7 @@ public class PyFileElementType extends IStubFileElementType<PyFileStub> {
@Override
public int getStubVersion() {
return 46;
return 47;
}
@Nullable

View File

@@ -913,7 +913,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
private List<PyTargetExpression> collectInstanceAttributes() {
Map<String, PyTargetExpression> result = new HashMap<String, PyTargetExpression>();
// __init__ takes priority over all other methods
collectAttributesInNew(result);
PyFunctionImpl initMethod = (PyFunctionImpl)findMethodByName(PyNames.INIT, false);
if (initMethod != null) {
collectInstanceAttributes(initMethod, result);
@@ -929,6 +929,15 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
return new ArrayList<PyTargetExpression>(expressions);
}
private void collectAttributesInNew(@NotNull final Map<String, PyTargetExpression> result) {
final PyFunction newMethod = findMethodByName(PyNames.NEW, false);
if (newMethod != null) {
for (PyTargetExpression target : getTargetExpressions(newMethod)) {
result.put(target.getName(), target);
}
}
}
public static void collectInstanceAttributes(@NotNull PyFunction method, @NotNull final Map<String, PyTargetExpression> result) {
final PyParameter[] params = method.getParameterList().getParameters();
if (params.length == 0) {

View File

@@ -182,7 +182,7 @@ public class PyNamedParameterImpl extends PyPresentableElementImpl<PyNamedParame
initType = stdlib.getConstructorType(containingClass);
}
}
if (initType != null && !(initType instanceof PyNoneType)) {
if (initType != null && !(initType instanceof PyNoneType || initType instanceof PyReturnTypeReference)) {
return initType;
}
return new PyClassTypeImpl(containingClass, modifier == PyFunction.Modifier.CLASSMETHOD);

View File

@@ -11,6 +11,7 @@ import com.intellij.psi.stubs.StubOutputStream;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.io.StringRef;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.impl.PyTargetExpressionImpl;
@@ -110,6 +111,10 @@ public class PyTargetExpressionElementType extends PyStubElementType<PyTargetExp
final ASTNode functionNode = TreeUtil.findParent(node, PyElementTypes.FUNCTION_DECLARATION);
final ASTNode qualifierNode = node.findChildByType(PyElementTypes.REFERENCE_EXPRESSION);
if (functionNode != null && qualifierNode != null) {
final PsiElement function = functionNode.getPsi();
if (function instanceof PyFunction && PyNames.NEW.equals(((PyFunction)function).getName())) {
return true;
}
final ASTNode parameterList = functionNode.findChildByType(PyElementTypes.PARAMETER_LIST);
assert parameterList != null;
final ASTNode[] children = parameterList.getChildren(PyElementTypes.FORMAL_PARAMETER_SET);

View File

@@ -0,0 +1,8 @@
class Test(object):
def __new__(cls, foo):
x = super(Test, cls).__new__(cls)
x.foo = foo
return x
def bar(self):
return self.f<ref>oo

View File

@@ -0,0 +1,5 @@
from a import Test
x = Test('foo')
print(x.foo)
# <ref>

View File

@@ -0,0 +1,5 @@
class Test(object):
def __new__(cls, foo):
x = super(Test, cls).__new__(cls)
x.foo = foo
return x

View File

@@ -432,6 +432,11 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
assertResolvesTo(PyFunction.class, "foobar");
}
// PY-6805
public void testAttributeDefinedInNew() {
assertResolvesTo(PyTargetExpression.class, "foo");
}
private void prepareTestDirectory() {
final String testName = getTestName(true);
myFixture.copyDirectoryToProject(testName, "");

View File

@@ -5,6 +5,8 @@ import com.intellij.psi.PsiPolyVariantReference;
import com.intellij.psi.PsiReference;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.fixtures.PyResolveTestCase;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
@@ -525,4 +527,16 @@ public class PyResolveTest extends PyResolveTestCase {
public void testGeneratorShadowing() { // PY-8725
assertResolvesTo(PyFunction.class, "_");
}
// PY-6805
public void testAttributeDefinedInNew() {
final PsiElement resolved = resolve();
assertInstanceOf(resolved, PyTargetExpression.class);
final PyTargetExpression target = (PyTargetExpression)resolved;
assertEquals("foo", target.getName());
final ScopeOwner owner = ScopeUtil.getScopeOwner(target);
assertNotNull(owner);
assertInstanceOf(owner, PyFunction.class);
assertEquals("__new__", owner.getName());
}
}