Merge branch 'python-fixes'

This commit is contained in:
Andrey Vlasovskikh
2013-09-19 22:13:46 +04:00
12 changed files with 100 additions and 34 deletions
@@ -181,6 +181,12 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
boolean isSubclass(@NotNull String superClassQName);
/**
* Returns the aggregated list of names defined in __slots__ attributes of the class and its ancestors.
*/
@Nullable
List<String> getSlots();
/**
* Returns the list of names in the class' __slots__ attribute, or null if the class
* does not define such an attribute.
@@ -188,7 +194,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
* @return the list of names or null.
*/
@Nullable
List<String> getSlots();
List<String> getOwnSlots();
@Nullable
String getDocStringValue();
@@ -10,7 +10,10 @@ import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.types.*;
import com.jetbrains.python.psi.types.PyABCUtil;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.PyTypeChecker;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -88,6 +91,14 @@ public class PyArgumentListInspection extends PyInspection {
public static void inspectPyArgumentList(PyArgumentList node, ProblemsHolder holder, final TypeEvalContext context, int implicitOffset) {
if (node.getParent() instanceof PyClass) return; // class Foo(object) is also an arg list
CallArgumentsMapping result = node.analyzeCall(PyResolveContext.noImplicits().withTypeEvalContext(context), implicitOffset);
final PyCallExpression.PyMarkedCallee callee = result.getMarkedCallee();
if (callee != null) {
final Callable callable = callee.getCallable();
// Decorate functions may have different parameter lists. We don't match arguments with parameters of decorators yet
if (callable instanceof PyFunction && PyUtil.hasCustomDecorators((PyFunction)callable)) {
return;
}
}
highlightIncorrectArguments(holder, result, context);
highlightMissingArguments(node, holder, result);
highlightStarArgumentTypeMismatch(node, holder, context);
@@ -128,6 +128,9 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
if (type instanceof PyClassType) {
final PyClass pyClass = ((PyClassType)type).getPyClass();
if (pyClass.isNewStyleClass()) {
if (pyClass.getOwnSlots() == null) {
return;
}
final List<String> slots = pyClass.getSlots();
final String attrName = node.getReferencedName();
if (slots != null && !slots.contains(attrName) && !slots.contains(PyNames.DICT)) {
@@ -44,6 +44,7 @@ import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.QualifiedResolveResult;
import com.jetbrains.python.psi.types.*;
@@ -663,6 +664,20 @@ public class PyUtil {
return !isListComprehension || isAtLeast30;
}
public static boolean hasCustomDecorators(@NotNull PyDecoratable decoratable) {
PyDecoratorList decoratorList = decoratable.getDecoratorList();
if (decoratorList == null) {
return false;
}
for (PyDecorator decorator : decoratorList.getDecorators()) {
PyQualifiedName name = decorator.getQualifiedName();
if (name == null || (!PyNames.CLASSMETHOD.equals(name.toString()) && !PyNames.STATICMETHOD.equals(name.toString()))) {
return true;
}
}
return false;
}
public static class KnownDecoratorProviderHolder {
public static PyKnownDecoratorProvider[] KNOWN_DECORATOR_PROVIDERS = Extensions.getExtensions(PyKnownDecoratorProvider.EP_NAME);
@@ -246,20 +246,25 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
@Override
public List<String> getSlots() {
List<String> slots = getOwnSlots();
if (slots != null) {
return slots;
final Set<String> result = new LinkedHashSet<String>();
boolean found = false;
final List<String> ownSlots = getOwnSlots();
if (ownSlots != null) {
found = true;
result.addAll(ownSlots);
}
for (PyClass cls : getAncestorClasses()) {
slots = ((PyClassImpl)cls).getOwnSlots();
if (slots != null) {
return slots;
final List<String> ancestorSlots = cls.getOwnSlots();
if (ancestorSlots != null) {
found = true;
result.addAll(ancestorSlots);
}
}
return null;
return found ? new ArrayList<String>(result) : null;
}
@Nullable
@Override
public List<String> getOwnSlots() {
final PyClassStub stub = getStub();
if (stub != null) {
@@ -290,9 +290,28 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
@Nullable
private PyType getTypeFromTupleAssignment(@NotNull PyTupleExpression tuple, @NotNull PyTupleType tupleType) {
if (tuple.getElements().length == tupleType.getElementCount()) {
int selfIndex = ArrayUtil.indexOf(tuple.getElements(), this);
return tupleType.getElementType(selfIndex);
final int count = tupleType.getElementCount();
final PyExpression[] elements = tuple.getElements();
if (elements.length == count) {
final int index = ArrayUtil.indexOf(elements, this);
if (index >= 0) {
return tupleType.getElementType(index);
}
for (int i = 0; i < count; i++) {
PyExpression element = elements[i];
while (element instanceof PyParenthesizedExpression) {
element = ((PyParenthesizedExpression)element).getContainedExpression();
}
if (element instanceof PyTupleExpression) {
final PyType elementType = tupleType.getElementType(i);
if (elementType instanceof PyTupleType) {
final PyType result = getTypeFromTupleAssignment((PyTupleExpression)element, (PyTupleType)elementType);
if (result != null) {
return result;
}
}
}
}
}
return null;
}
@@ -319,12 +338,11 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
}
}
}
if (source != null && target != null) {
if (source != null) {
final PyType sourceType = context.getType(source);
final PyType type = getIterationType(sourceType, source, context);
final PsiElement parent = getParent();
if (type instanceof PyTupleType && parent instanceof PyTupleExpression) {
return getTypeFromTupleAssignment((PyTupleExpression)parent, (PyTupleType)type);
if (type instanceof PyTupleType && target instanceof PyTupleExpression) {
return getTypeFromTupleAssignment((PyTupleExpression)target, (PyTupleType)type);
}
if (target == this && type != null) {
return type;
@@ -49,7 +49,7 @@ public class PyClassElementType extends PyStubElementType<PyClassStub, PyClass>
final PyStringLiteralExpression docStringExpression = psi.getDocStringExpression();
return new PyClassStubImpl(psi.getName(), parentStub,
superClasses.toArray(new PyQualifiedName[superClasses.size()]),
((PyClassImpl)psi).getOwnSlots(),
psi.getOwnSlots(),
PyPsiUtils.strValue(docStringExpression),
getStubElementType());
}
@@ -48,7 +48,7 @@ public class CompletionVariantsProcessor extends VariantsProcessor {
if (!myPlainNamesOnly) {
if (!mySuppressParentheses &&
object instanceof PyFunction && ((PyFunction)object).getProperty() == null &&
hasNoCustomDecorators((PyFunction)object) &&
!PyUtil.hasCustomDecorators((PyFunction)object) &&
!isSingleArgDecoratorCall(myContext, (PyFunction)object)) {
item = item.withInsertHandler(PyFunctionInsertHandler.INSTANCE);
final TypeEvalContext context = TypeEvalContext.userInitiated(myContext != null ? myContext.getContainingFile() : null);
@@ -105,21 +105,6 @@ public class CompletionVariantsProcessor extends VariantsProcessor {
return item;
}
private static boolean hasNoCustomDecorators(PyFunction function) {
PyDecoratorList decoratorList = function.getDecoratorList();
if (decoratorList == null) {
return true;
}
for (PyDecorator decorator : decoratorList.getDecorators()) {
PyQualifiedName name = decorator.getQualifiedName();
if (name == null || (!PyNames.CLASSMETHOD.equals(name.toString()) && !PyNames.STATICMETHOD.equals(name.toString()))) {
return false;
}
}
return true;
}
private static boolean isSingleArgDecoratorCall(PsiElement elementInCall, PyFunction callee) {
// special case hack to avoid the need of patching generator3.py
PyClass containingClass = callee.getContainingClass();
@@ -0,0 +1,10 @@
def fill(f):
return lambda: f('test')
@fill
def test(x):
return x
test()
@@ -8,7 +8,7 @@ class C(B):
pass
c = C()
c.<warning descr="'C' object has no attribute 'bar'">bar</warning> = 1
c.bar = 1
def test_slots_with_dict():
class C(object):
@@ -752,6 +752,14 @@ public class PyTypeTest extends PyTestCase {
" expr = x if isinstance(x, str) else 10\n");
}
// PY-9334
public void testIterateOverListOfNestedTuples() {
doTest("str",
"def f():\n" +
" for i, (expr, v) in [(0, ('foo', []))]:\n" +
" print(expr)\n");
}
private static TypeEvalContext getTypeEvalContext(@NotNull PyExpression element) {
return TypeEvalContext.userInitiated(element.getContainingFile()).withTracing();
}
@@ -133,4 +133,9 @@ public class PyArgumentListInspectionTest extends PyTestCase {
public void testFloatConstructor() {
doTest();
}
// PY-10601
public void testDecoratedChangedParameters() {
doTest();
}
}