Merge pull request #20142 from joefarebrother/python-qual-subclass-shadow

Python: Modernise Superclass attribute shadows subclass method query
This commit is contained in:
Joe Farebrother
2025-08-28 13:40:26 +01:00
committed by GitHub
13 changed files with 174 additions and 108 deletions

View File

@@ -1 +1,2 @@
| subclass_shadowing.py:10:5:10:21 | FunctionExpr | Method shadow is shadowed by an $@ in super class 'Base'. | subclass_shadowing.py:6:9:6:23 | AssignStmt | attribute |
| subclass_shadowing.py:11:5:11:21 | Function shadow | This method is shadowed by $@ in superclass $@. | subclass_shadowing.py:7:9:7:19 | ControlFlowNode for Attribute | attribute shadow | subclass_shadowing.py:4:1:4:11 | Class Base | Base |
| subclass_shadowing.py:41:5:41:18 | Function foo | This method is shadowed by $@ in superclass $@. (read-only property may cause an error if written to in the superclass) | subclass_shadowing.py:35:9:35:16 | ControlFlowNode for Attribute | attribute foo | subclass_shadowing.py:33:1:33:12 | Class Base3 | Base3 |

View File

@@ -1 +1,2 @@
Classes/SubclassShadowing.ql
query: Classes/SubclassShadowing/SubclassShadowing.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,30 +1,51 @@
#Subclass shadowing
class Base(object):
# BAD: `shadow` method shadows attribute
class Base:
def __init__(self):
self.shadow = 4
class Derived(Base):
def shadow(self):
def shadow(self): # $ Alert
pass
#OK if the super class defines the method as well.
#Since the original method must exist for some reason.
#See JSONEncoder.default for real example
# OK: Allow if superclass also shadows its own method, as this is likely intended.
# Example: stdlib JSONEncoder.default uses this pattern.
class Base2:
class Base2(object):
def __init__(self, default=None):
if default:
self.default = default
def __init__(self, shadowy=None):
if shadowy:
self.shadow = shadowy
def shadow(self):
def default(self):
pass
class Derived2(Base2):
def shadow(self):
def default(self): # No alert
return 0
# Properties
class Base3:
def __init__(self):
self.foo = 1
self.bar = 2
class Derived3(Base3):
# BAD: Write to foo in superclass init raises an error.
@property
def foo(self): # $ Alert
return 2
# OK: This property has a setter, so the write is OK.
@property
def bar(self): # No alert
return self._bar
@bar.setter
def bar(self, val):
self._bar = val