Skip to content

Python: Modernise Superclass attribute shadows subclass method query #20142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

joefarebrother
Copy link
Contributor

Modernizes py/attribute-shadows-method query to not use pointsTo; and improve precision by accounting for Properties.

@Copilot Copilot AI review requested due to automatic review settings July 30, 2025 15:38
@joefarebrother joefarebrother requested a review from a team as a code owner July 30, 2025 15:38
@joefarebrother joefarebrother added the no-change-note-required This PR does not need a change note label Jul 30, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR modernizes the py/attribute-shadows-method query by migrating away from the deprecated pointsTo analysis to use modern data flow analysis, and improves precision by properly handling Python properties. The query now better distinguishes between problematic shadowing cases and legitimate patterns.

Key changes:

  • Complete rewrite of the query logic using modern data flow analysis instead of pointsTo
  • Enhanced handling of Python properties, including proper support for read-only vs settable properties
  • Improved test coverage with new examples covering property scenarios

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
python/ql/src/Classes/SubclassShadowing/SubclassShadowing.ql New modernized query implementation with property support
python/ql/src/Classes/SubclassShadowing.ql Original query file removed
python/ql/test/query-tests/Classes/subclass-shadowing/subclass_shadowing.py Enhanced test cases with property examples
python/ql/src/Classes/SubclassShadowing/SubclassShadowing.qhelp Updated documentation
python/ql/src/Classes/SubclassShadowing/examples/*.py New example files demonstrating good and bad patterns

Copy link
Contributor

github-actions bot commented Jul 30, 2025

QHelp previews:

python/ql/src/Classes/SubclassShadowing/SubclassShadowing.qhelp

Superclass attribute shadows subclass method

When an object has an attribute that shares the same name a method on the object's class (or another class attribute), the instance attribute is prioritized during attribute lookup, shadowing the method. If a method on a subclass is shadowed by an attribute on a superclass in this way, this may lead to unexpected results or errors, as this shadowing behavior is nonlocal and may be unintended.

Recommendation

Ensure method names on subclasses don't conflict with attribute names on superclasses, and rename one. If the shadowing behavior is intended, ensure this is explicit in the superclass.

Example

In the following example, the _foo attribute of class A shadows the method _foo of class B. Calls to B()._foo() will result in a TypeError, as 3 will be called instead.

class A:
    def __init__(self):
        self._foo = 3

class B(A):
    # BAD: _foo is shadowed by attribute A._foo
    def _foo(self):
        return 2

In the following example, the behavior of the default attribute being shadowed to allow for customization during initialization is intended in within the superclass A. Overriding default in the subclass B is then OK.

class A:
    def __init__(self, default_func=None):
        if default_func is not None:
            self.default = default_func 

    # GOOD: The shadowing behavior is explicitly intended in the superclass.
    def default(self):
        return []
    
class B(A):
    
    # Subclasses may override the method `default`, which will still be shadowed by the attribute `default` if it is set.
    # As this is part of the expected behavior of the superclass, this is fine. 
    def default(self):
        return {}

joefarebrother and others added 2 commits July 31, 2025 06:05
…wingBad.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation no-change-note-required This PR does not need a change note Python
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy