mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'python-fixes'
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user