mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-20280 Fixed: Warn if class variable listed in __slots__
Introduce PyDunderSlotsInspection which detects invalid definition of __slots__ in a class
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection detects invalid definition of __slots__ in a class.
|
||||
</body>
|
||||
</html>
|
||||
@@ -383,7 +383,7 @@
|
||||
<localInspection language="Python" shortName="PyAbstractClassInspection" suppressId="PyAbstractClass" displayName="Class must implement all abstract methods" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyAbstractClassInspection"/>
|
||||
<localInspection language="Python" shortName="PyPep8NamingInspection" suppressId="PyPep8Naming" displayName="PEP 8 naming convention violation" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyPep8NamingInspection"/>
|
||||
<localInspection language="Python" shortName="PyAssignmentToLoopOrWithParameterInspection" suppressId="PyAssignmentToLoopOrWithParameter" displayName="Assignment to 'for' loop or 'with' statement parameter" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyAssignmentToLoopOrWithParameterInspection"/>
|
||||
|
||||
<localInspection language="Python" shortName="PyDunderSlotsInspection" suppressId="PyDunderSlots" displayName="Definition of __slots__ in a class" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyDunderSlotsInspection"/>
|
||||
<localInspection language="Python" shortName="PyMissingTypeHintsInspection" suppressId="PyMissingTypeHints" displayName="Missing type hinting for function definition" groupKey="INSP.GROUP.python" enabledByDefault="false" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyMissingTypeHintsInspection"/>
|
||||
|
||||
<defaultLiveTemplatesProvider implementation="com.jetbrains.python.codeInsight.liveTemplates.PyDefaultLiveTemplatesProvider"/>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.inspections
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.jetbrains.python.PyNames
|
||||
import com.jetbrains.python.psi.*
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils
|
||||
|
||||
class PyDunderSlotsInspection : PyInspection() {
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder,
|
||||
isOnTheFly: Boolean,
|
||||
session: LocalInspectionToolSession): PsiElementVisitor = Visitor(holder, session)
|
||||
|
||||
private class Visitor(holder: ProblemsHolder, session: LocalInspectionToolSession) : PyInspectionVisitor(holder, session) {
|
||||
|
||||
override fun visitPyClass(node: PyClass?) {
|
||||
if (node != null && LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON30)) {
|
||||
val slots = findSlotsValue(node)
|
||||
|
||||
when (slots) {
|
||||
is PySequenceExpression -> slots
|
||||
.elements
|
||||
.asSequence()
|
||||
.filterIsInstance<PyStringLiteralExpression>()
|
||||
.forEach {
|
||||
processSlot(node, it)
|
||||
}
|
||||
is PyStringLiteralExpression -> processSlot(node, slots)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findSlotsValue(pyClass: PyClass): PyExpression? {
|
||||
val target = pyClass.findClassAttribute(PyNames.SLOTS, false, myTypeEvalContext) as? PyTargetExpression
|
||||
val value = target?.findAssignedValue()
|
||||
|
||||
return PyPsiUtils.flattenParens(value)
|
||||
}
|
||||
|
||||
private fun processSlot(pyClass: PyClass, slot: PyStringLiteralExpression) {
|
||||
val name = slot.stringValue
|
||||
|
||||
if (pyClass.findClassAttribute(name, false, myTypeEvalContext) != null) {
|
||||
registerProblem(slot, "'$name' in __slots__ conflicts with class variable")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# PY-20280: one slot in list
|
||||
class Foo(object):
|
||||
__slots__ = [<warning descr="'foo' in __slots__ conflicts with class variable">'foo'</warning>]
|
||||
foo = 1
|
||||
|
||||
|
||||
# PY-20280: one slot in tuple
|
||||
class Foo(object):
|
||||
__slots__ = (<warning descr="'foo' in __slots__ conflicts with class variable">'foo'</warning>)
|
||||
foo = 1
|
||||
|
||||
|
||||
# PY-20280: one slot
|
||||
class Foo(object):
|
||||
__slots__ = <warning descr="'foo' in __slots__ conflicts with class variable">'foo'</warning>
|
||||
foo = 1
|
||||
|
||||
|
||||
# PY-20280: two slots in list
|
||||
class Foo(object):
|
||||
__slots__ = [<warning descr="'foo' in __slots__ conflicts with class variable">'foo'</warning>, 'bar']
|
||||
foo = 1
|
||||
|
||||
|
||||
# PY-20280: two slots in tuple
|
||||
class Foo(object):
|
||||
__slots__ = (<warning descr="'foo' in __slots__ conflicts with class variable">'foo'</warning>, 'bar')
|
||||
foo = 1
|
||||
|
||||
|
||||
# PY-20280: slots in base and class variable in derived
|
||||
class Base(object):
|
||||
__slots__ = 'foo'
|
||||
|
||||
class Derived(Base):
|
||||
foo = 1
|
||||
|
||||
|
||||
# PY-20280: class variable in base and slots in derived
|
||||
class Base(object):
|
||||
foo = 1
|
||||
|
||||
class Derived(Base):
|
||||
__slots__ = 'foo'
|
||||
|
||||
|
||||
# PY-20280: slots in base and derived, class variable in derived
|
||||
class Base(object):
|
||||
__slots__ = 'foo'
|
||||
|
||||
class Derived(Base):
|
||||
__slots__ = 'bar'
|
||||
foo = 1
|
||||
|
||||
|
||||
# PY-20280: slots in base and derived, class variable in base
|
||||
class Base(object):
|
||||
__slots__ = 'bar'
|
||||
foo = 1
|
||||
|
||||
class Derived(Base):
|
||||
__slots__ = 'foo'
|
||||
@@ -328,4 +328,8 @@ public class PythonInspectionsTest extends PyTestCase {
|
||||
public void testPyShadowingNamesInspection() {
|
||||
doHighlightingTest(PyShadowingNamesInspection.class);
|
||||
}
|
||||
|
||||
public void testPyDunderSlotsInspection() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON30, () -> doHighlightingTest(PyDunderSlotsInspection.class));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user