From d3009467c608c490b672055a081a64c4bd21f63c Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 23 Nov 2011 13:31:43 +0400 Subject: [PATCH 1/3] Fixed bug in detecting old-style iterables (PY-4890) --- .../jetbrains/python/psi/types/PyABCUtil.java | 17 ++++++----------- .../PyArgumentListInspection/args.py | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java index 53b31a696621..1c3cf15cd950 100644 --- a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java +++ b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java @@ -20,19 +20,19 @@ public class PyABCUtil { } public static boolean isSubclass(@NotNull PyClass subClass, @NotNull String superClassName) { - final String subClassName = subClass.getName(); if (PyNames.CALLABLE.equals(superClassName)) { return hasMethod(subClass, PyNames.CALL); } if (PyNames.HASHABLE.equals(superClassName)) { return hasMethod(subClass, PyNames.HASH); } - final boolean isIterable = hasMethod(subClass, PyNames.ITER); + final boolean hasIter = hasMethod(subClass, PyNames.ITER); + final boolean hasGetItem = hasMethod(subClass, PyNames.GETITEM); if (PyNames.ITERABLE.equals(superClassName)) { - return isIterable || isStringClass(subClassName); + return hasIter || hasGetItem; } if (PyNames.ITERATOR.equals(superClassName)) { - return (isIterable && hasMethod(subClass, PyNames.NEXT)) || isStringClass(subClassName); + return (hasIter && hasMethod(subClass, PyNames.NEXT)) || hasGetItem; } final boolean isSized = hasMethod(subClass, PyNames.LEN); if (PyNames.SIZED.equals(superClassName)) { @@ -42,12 +42,11 @@ public class PyABCUtil { if (PyNames.CONTAINER.equals(superClassName)) { return isContainer; } - final boolean hasGetItem = hasMethod(subClass, PyNames.GETITEM); if (PyNames.SEQUENCE.equals(superClassName)) { - return isSized && isIterable && isContainer && hasGetItem; + return isSized && hasIter && isContainer && hasGetItem; } if (PyNames.MAPPING.equals(superClassName)) { - return isSized && isIterable && isContainer && hasGetItem && hasMethod(subClass, PyNames.KEYS); + return isSized && hasIter && isContainer && hasGetItem && hasMethod(subClass, PyNames.KEYS); } return false; } @@ -74,8 +73,4 @@ public class PyABCUtil { private static boolean hasMethod(PyClass cls, String name) { return cls.findMethodByName(name, true) != null; } - - private static boolean isStringClass(String className) { - return "bytes".equals(className) || "str".equals(className) || "unicode".equals(className); - } } diff --git a/python/testData/inspections/PyArgumentListInspection/args.py b/python/testData/inspections/PyArgumentListInspection/args.py index 9f9a58094655..8bb554823f0e 100644 --- a/python/testData/inspections/PyArgumentListInspection/args.py +++ b/python/testData/inspections/PyArgumentListInspection/args.py @@ -8,4 +8,18 @@ def test_union_type(): def foo(*args): pass bar = (1,) if True else (1, 2) - foo(*bar) #pass \ No newline at end of file + foo(*bar) #pass + +# PY-4890 +def test_old_style_iterable(): + class C: + def __getitem__(self, key): + if 0 <= key < 10: + return key + else: + raise IndexError('index out of range') + def foo(*args): + pass + xs = C() + foo(*xs) #pass + foo(*'bar') #pass \ No newline at end of file From bf024a002f7201e4ad7de9f3cacd0e3ba66c22b1 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 23 Nov 2011 19:54:57 +0400 Subject: [PATCH 2/3] Fixed race condition in evaluating set of TypeEvalContext and resolve results cache --- .../python/psi/types/TypeEvalContext.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/types/TypeEvalContext.java b/python/src/com/jetbrains/python/psi/types/TypeEvalContext.java index e12d8f50588e..1a8df2786682 100644 --- a/python/src/com/jetbrains/python/psi/types/TypeEvalContext.java +++ b/python/src/com/jetbrains/python/psi/types/TypeEvalContext.java @@ -21,7 +21,12 @@ public class TypeEvalContext { private final PsiFile myOrigin; private final Map myEvaluated = new HashMap(); - private final Set myEvaluating = new HashSet(); + private final ThreadLocal> myEvaluating = new ThreadLocal>() { + @Override + protected Set initialValue() { + return new HashSet(); + } + }; private TypeEvalContext(boolean allowDataFlow, boolean allowStubToAST, PsiFile origin) { myAllowDataFlow = allowDataFlow; @@ -111,17 +116,22 @@ public class TypeEvalContext { if (myEvaluated.containsKey(element)) { return myEvaluated.get(element); } - if (myEvaluating.contains(element)) { - return null; + } + final Set evaluating = myEvaluating.get(); + if (evaluating.contains(element)) { + return null; + } + evaluating.add(element); + try { + PyType result = element.getType(this); + synchronized (myEvaluated) { + myEvaluated.put(element, result); } - myEvaluating.add(element); + return result; } - PyType result = element.getType(this); - synchronized (myEvaluated) { - myEvaluating.remove(element); - myEvaluated.put(element, result); + finally { + evaluating.remove(element); } - return result; } public boolean maySwitchToAST(StubBasedPsiElement element) { From f4a17784011140dac67ab1209949c4fd5b27b901 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 23 Nov 2011 19:57:52 +0400 Subject: [PATCH 3/3] Removed redundant equals() and hashCode() for PyImportReferenceImpl --- .../psi/impl/PyImportReferenceImpl.java | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java index 001d0a7fd797..63d79ee80359 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyImportReferenceImpl.java @@ -305,27 +305,4 @@ public class PyImportReferenceImpl extends PyReferenceImpl { editor.getCaretModel().moveToOffset(tailOffset + IMPORT_KWD.length()); } } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - - PyImportReferenceImpl that = (PyImportReferenceImpl)o; - - if (!myElement.equals(that.myElement)) return false; - if (!myContext.equals(that.myContext)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + myElement.hashCode(); - result = 31 * result + myContext.hashCode(); - return result; - } }