Skip to content

Python: Modernize py/not-named-self and py/not-named-cls queries #18599

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

Merged

Conversation

joefarebrother
Copy link
Contributor

@joefarebrother joefarebrother commented Jan 27, 2025

Rewrites queries to not rely on pointsTo analysis and instead use API graphs and dataflow where necessary.

@joefarebrother joefarebrother changed the title [Draft] Python: Update NonSelf and NonCls quality queries to not depend on PointsTo Python: Modernize not-named-self and not-named-cls queries Feb 4, 2025
@joefarebrother joefarebrother changed the title Python: Modernize not-named-self and not-named-cls queries Python: Modernize py/not-named-self and py/not-named-cls queries Feb 4, 2025
@joefarebrother joefarebrother force-pushed the python-qual-not-named-self-cls branch from e739f6e to 287cf01 Compare February 4, 2025 15:28
Copy link
Contributor

github-actions bot commented Feb 4, 2025

QHelp previews:

python/ql/src/Functions/NonCls.qhelp

First parameter of a class method is not named 'cls'

The first parameter of a class method (including certain special methods such as __new__), or a method of a metaclass, should be named cls.

Recommendation

Ensure that the first parameter of class methods is named cls, as recommended by the style guidelines in PEP 8.

Example

In the following example, the first parameter of the class method make is named self instead of cls.

class Entry(object):
    @classmethod
    def make(self):
        return Entry()

References

python/ql/src/Functions/NonSelf.qhelp

First parameter of a method is not named 'self'

Normal methods should have at least one parameter and the first parameter should be called self.

Recommendation

Ensure that the first parameter of a normal method is named self, as recommended by the style guidelines in PEP 8.

If a self parameter is unneeded, the method should be decorated with staticmethod, or moved out of the class as a regular function.

Example

In the following cases, the first argument of Point.__init__ is named val instead; whereas in Point2.__init__ it is correctly named self.

class Point:
    def __init__(val, x, y):  # BAD: first parameter is mis-named 'val'
        val._x = x
        val._y = y

class Point2:
    def __init__(self, x, y):  # GOOD: first parameter is correctly named 'self'
        self._x = x
        self._y = y

References

@joefarebrother joefarebrother marked this pull request as ready for review February 4, 2025 15:54
@joefarebrother joefarebrother requested a review from a team as a code owner February 4, 2025 15:54
@joefarebrother joefarebrother added the no-change-note-required This PR does not need a change note label Feb 4, 2025
Copy link
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

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

Generally very nice, thank you for also updating the tests and the docs, I do think they read nicer.
Only one actual gripe, where the logic could read easier. You may also be able to reuse some existing predicates.

c.getABase() = API::builtin("type").getASubclass*().asSource().asExpr()
}

/** Holds if `f` is a class method. */
Copy link
Contributor

Choose a reason for hiding this comment

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

These two predicates also exist in DataFlowDispatch.qll.

Comment on lines 103 to 108
/** Holds if the first parameter of `f` should be named `cls`, but isn't. */
predicate firstArgShouldBeNamedClsAndIsnt(Function f) {
shouldBeCls(f, _) and
not firstArgNamedCls(f) and
not metaclassNamedSelf(f, _)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This reads a bit confusing at the moment. If you want to group metaclass method argument named self with class arguments named class, could we at least rename shouldBeClass to shouldReferToClass and firstArgShouldBeNamedClsAndIsnt to firstArgShouldReferToClsAndDosnt?

Alternatively, rename firstArgNamedSelf to something like normalFirstArgNamedSelf and treat the metaclassNamedSelf case inside firstArgShouldBeNamedSelfAndIsnt. Then you will have to update the shouldBeXXX logic, of course..

yoff
yoff previously approved these changes Feb 10, 2025
Copy link
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

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

LGTM

@yoff
Copy link
Contributor

yoff commented Feb 10, 2025

Stage timings look quite a bit worse 6-7x, but then we are finding nearly 1000 new alerts.. Are the new results looking good?

@joefarebrother
Copy link
Contributor Author

Wasn't certain about the stage timings.
Results in cpython look fine; but those in FreeCAD and aws-parallelcluster look like mostly the result of decorators that allows a method to have no arguments.
Would it make sense to exclude alerts on decorated no-arg methods?

Copy link
Contributor

@yoff yoff left a comment

Choose a reason for hiding this comment

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

I thought we were only going to allow the two decorators found during triage, but I think I actually like this approach better; we get a higher precision this way.

@joefarebrother joefarebrother merged commit 180e45d into github:main Feb 17, 2025
14 checks passed
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.

2 participants