pyrefly icon indicating copy to clipboard operation
pyrefly copied to clipboard

[bug] Pyrefly doesn't yet filter non-instance methods when inferring attributes

Open stroxler opened this issue 8 months ago • 1 comments

Pyrefly will currently infer the existence of attributes x and y on instances of C here:

class C:
    @classmethod
    def f0(cls):
        cls.x = 15
    @staticmethod
    def f0(arg: SomeOtherClass):
        arg.y = 15

This happens because the attribute collection is happening at bindings time, so it's naively occurring in all defs (assuming they have at least one parameter) nested inside a class body, without understanding decorator and descriptor behaviors

We should find a way to filter these out (the cls cases arguably could define class attributes, although I think that's very low-priority unless we see a concrete use case).

The two methods I can think of are:

  • Using SpecialExports to handle this in bindings
  • Saving an Idx<Key> inside BindingClassField for the type of the first parameter to check its type

The first approach would be easy and handle most cases, the second approach is more powerful:

  • It is done at the type level, so can eventually probably handle custom descriptors and such
  • It piggybacks off of work we need for parameter type inference anyway
  • It would allow us to skip functions where the annotation on self is very restrictive (low priority, maybe handy?)

stroxler avatar May 14 '25 11:05 stroxler

There's another question of whether to count these as class-level attributes or instance-level attributes. Eric Traut brought it up in this comment.

grievejia avatar May 15 '25 19:05 grievejia

This issue has someone assigned, but has not had recent activity for more than 2 weeks.

If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.

Thank you for your contributions!

github-actions[bot] avatar Jul 07 '25 00:07 github-actions[bot]

I also met this, so I left a sandbox link.

https://pyrefly.org/sandbox/?code=MQAg6gpgNgxg9gWwiALnVALZAFAngJwgDMpcQBlAQwDsATAIzgA8AaTASwGcQuRKQADpQDmEAFCgA7lkIhccAK4gYNEJPzsUENpwyVZNWiCgR91PowUpUuAe2rCe5vCgxxqAOgkSQACQgGBsqKAiYgRKYoCoTccEQc3JyGjEwAXD4AtCAA4hDWhJRQGSjsSDYCyDBYMADW9o4REAyUtZkgAKqcyIQAbqZQAPoothAAFACUqOj2nBUw1vYR%2BIRGwxWcbRBMFRpI1NaSmhiYyACSACIAouGR0RDcrpQHyJwKAgJw%2BNajEB7CHnwrHB4AhQnltCBhHBitDaMR7Jp2O42G4%2Bvg2HkYOM2mANFo%2BHQQPgFM5cK53ME4SBaOxCPNSE45Ip8CB6Pg4JIuvgxDy4fEtJwUKM0k4UON0iAiXlouYiAAiADeTAAvnKeTAoJRONwAEL6CWS1n60XqzXakAAMTgcANkoAAoKnuwYEhybQxIbqcRwtbhakQHr8OKPZ7DUwPPR9CAALwgACMPNAvX6QxGanYUCggnZtAUMGQ-DWyAC7JZj2sWkz3HkSlcyCLIDwhBIZD0G1AIIEViaPrLWClUXw5lG9gSyi1EMFQbEQA&version=3.12

def test(x: int):
  return f"{x}"

class Bar:
    bar: int

class Foo:
    @staticmethod
    def foo(x: Bar):
        x.bar = 1

ERROR 20:11-14: Attribute bar is implicitly defined by assignment in method foo, which is not a constructor [implicitly-defined-attribute]

asukaminato0721 avatar Aug 17 '25 18:08 asukaminato0721

This issue has someone assigned, but has not had recent activity for more than 2 weeks.

If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.

Thank you for your contributions!

github-actions[bot] avatar Sep 12 '25 00:09 github-actions[bot]