mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Inline __slots__ invalidation logic into PyClass (PY-29268)
This commit is contained in:
@@ -266,7 +266,13 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, PyDocStrin
|
||||
boolean isSubclass(@NotNull String superClassQName, @Nullable TypeEvalContext context);
|
||||
|
||||
/**
|
||||
* Returns the aggregated list of names defined in __slots__ attributes of the class and its ancestors.
|
||||
* Returns the aggregated list of names defined in `__slots__` attributes of the class and its ancestors.
|
||||
* <p>
|
||||
* Returned value is `null` if class or at least one of its ancestor does not follow the next conditions:
|
||||
* <ul>
|
||||
* <li>it should be a new style class</li>
|
||||
* <li>its `__slots__` should exist and should not contain `__dict__`</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param context (will be used default if null)
|
||||
*/
|
||||
|
||||
@@ -101,7 +101,7 @@ class PyDunderSlotsInspection : PyInspection() {
|
||||
}
|
||||
|
||||
private fun attributeIsWritableInPy2(cls: PyClass, name: String): Boolean {
|
||||
val slots = PyUtil.deactivateSlots(cls, cls.getSlots(myTypeEvalContext), myTypeEvalContext)
|
||||
val slots = cls.getSlots(myTypeEvalContext)
|
||||
return slots == null ||
|
||||
slots.contains(name) && cls.findClassAttribute(name, true, myTypeEvalContext) == null ||
|
||||
cls.findProperty(name, true, myTypeEvalContext) != null
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.codeInsight.completion.CompletionUtil;
|
||||
import com.intellij.lang.ASTNode;
|
||||
@@ -259,24 +260,27 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
@Override
|
||||
@Nullable
|
||||
public List<String> getSlots(@Nullable TypeEvalContext context) {
|
||||
final List<String> ownSlots = getOwnSlots();
|
||||
if (ownSlots == null) {
|
||||
return null;
|
||||
}
|
||||
final Set<String> result = new LinkedHashSet<>();
|
||||
|
||||
final Set<String> result = new LinkedHashSet<>(ownSlots);
|
||||
final PyClassType currentType = new PyClassTypeImpl(this, true);
|
||||
final TypeEvalContext contextToUse = context != null ? context : TypeEvalContext.codeInsightFallback(getProject());
|
||||
|
||||
for (PyClass cls : getAncestorClasses(context)) {
|
||||
for (PyClassLikeType type : Iterables.concat(Collections.singletonList(currentType), getAncestorTypes(contextToUse))) {
|
||||
if (!(type instanceof PyClassType)) return null;
|
||||
|
||||
final PyClass cls = ((PyClassType)type).getPyClass();
|
||||
if (PyUtil.isObjectClass(cls)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<String> ancestorSlots = cls.getOwnSlots();
|
||||
if (ancestorSlots == null) {
|
||||
if (!cls.isNewStyleClass(contextToUse)) return null;
|
||||
|
||||
final List<String> ownSlots = cls.getOwnSlots();
|
||||
if (ownSlots == null || ownSlots.contains(PyNames.DICT)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
result.addAll(ancestorSlots);
|
||||
result.addAll(ownSlots);
|
||||
}
|
||||
|
||||
return new ArrayList<>(result);
|
||||
|
||||
@@ -747,7 +747,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
final TypeEvalContext context =
|
||||
(expressionHook != null ? TypeEvalContext.codeCompletion(myClass.getProject(), myClass.getContainingFile()) : null);
|
||||
|
||||
processor.setAllowedNames(PyUtil.deactivateSlots(myClass, myClass.getSlots(context), context));
|
||||
processor.setAllowedNames(myClass.getSlots(context));
|
||||
myClass.processInstanceLevelDeclarations(processor, expressionHook);
|
||||
|
||||
for (LookupElement le : processor.getResultList()) {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
class A:
|
||||
class A(object):
|
||||
__slots__ = ['foo', 'bar']
|
||||
|
||||
@@ -269,6 +269,25 @@ public class PyDunderSlotsInspectionTest extends PyInspectionTestCase {
|
||||
);
|
||||
}
|
||||
|
||||
// PY-29268
|
||||
public void testWriteToNewStyleInheritedFromOldStyle() {
|
||||
doTestByText("class A:\n" +
|
||||
" __slots__ = ['a']\n" +
|
||||
"\n" +
|
||||
"class B(A, object):\n" +
|
||||
" __slots__ = ['b']\n" +
|
||||
"\n" +
|
||||
"B().c = 1");
|
||||
}
|
||||
|
||||
// PY-29268
|
||||
public void testWriteToNewStyleInheritedFromUnknown() {
|
||||
doTestByText("class B(A, object):\n" +
|
||||
" __slots__ = ['b']\n" +
|
||||
"\n" +
|
||||
"B().c = 1");
|
||||
}
|
||||
|
||||
private void doTestPy2() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON26, this::doTest);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user