Fixed collecting wrong '__contains__' attribute for structural types

The 'x in y' expression implies that 'y' has the '__contains__'
attribute, while our references in qualified expressions tell that
the 'x' references the name '__contains__'. In order to fix this we have
to treat the operators with a receiver on the right side of the
expression differently.
This commit is contained in:
Andrey Vlasovskikh
2014-12-12 15:58:23 +03:00
parent a791ff4805
commit 229c254abc
4 changed files with 20 additions and 7 deletions
@@ -472,7 +472,7 @@ public class PyNames {
}
public static boolean isRightOperatorName(@Nullable String name) {
return name != null && name.matches("__r[a-z]+__");
return name != null && (name.matches("__r[a-z]+__") || CONTAINS.equals(name));
}
/**
@@ -306,11 +306,15 @@ public class PyNamedParameterImpl extends PyBaseElementImpl<PyNamedParameterStub
final PyQualifiedExpression expr = (PyQualifiedExpression)node;
final PyExpression qualifier = expr.getQualifier();
if (qualifier != null) {
final PsiReference ref = qualifier.getReference();
if (ref != null && ref.isReferenceTo(PyNamedParameterImpl.this)) {
final String attributeName = expr.getReferencedName();
if (attributeName != null && !result.contains(attributeName)) {
result.add(attributeName);
final String attributeName = expr.getReferencedName();
final PyExpression referencedExpr = node instanceof PyBinaryExpression && PyNames.isRightOperatorName(attributeName) ?
((PyBinaryExpression)node).getRightExpression() : qualifier;
if (referencedExpr != null) {
final PsiReference ref = referencedExpr.getReference();
if (ref != null && ref.isReferenceTo(PyNamedParameterImpl.this)) {
if (attributeName != null && !result.contains(attributeName)) {
result.add(attributeName);
}
}
}
}
@@ -473,7 +473,7 @@ public class PyTypeChecker {
}
final Callable callable = ((PyFunctionType)type).getCallable();
final String operatorName = typedElement.getName();
final boolean isRight = PyNames.isRightOperatorName(operatorName) || PyNames.CONTAINS.equals(operatorName);
final boolean isRight = PyNames.isRightOperatorName(operatorName);
final PyExpression arg = isRight ? expr.getLeftExpression() : expr.getRightExpression();
final PyExpression receiver = isRight ? expr.getRightExpression() : expr.getLeftExpression();
final PyParameter[] parameters = callable.getParameterList().getParameters();
@@ -977,6 +977,15 @@ public class PyTypeTest extends PyTestCase {
" expr = x\n");
}
public void testNoContainsInContainsArgumentForStructuralType() {
doTest("{foo, __getitem__}",
"def f(x):\n" +
" x in []\n" +
" x.foo\n" +
" x[0]" +
" expr = x\n");
}
private static TypeEvalContext getTypeEvalContext(@NotNull PyExpression element) {
return TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile()).withTracing();
}