Repository: atollk/flake8-classmethod-staticmethod

Issue:
False positive when using super()
When you override a class method, you may call `super()` instead of referencing `cls`, which wouldn't be allowed in a static method.

So, maybe class methods like the one below shouldn't trigger `CSM131`?

```python
class LoginSpider(scrapy.Spider):

    @classmethod
    def from_crawler(cls, crawler, *args, **kwargs):
        spider = super().from_crawler(crawler, *args, **kwargs)
        crawler.signals.connect(spider.spider_opened, scrapy.signals.spider_opened)
        return spider

    ...
```

Discussion:

Patch:
diff --git a/.github/workflows/python-tox.yml b/.github/workflows/python-tox.yml
index ed5bb25..8f8ad4b 100644
--- a/.github/workflows/python-tox.yml
+++ b/.github/workflows/python-tox.yml
@@ -8,7 +8,7 @@ jobs:
     strategy:
       max-parallel: 4
       matrix:
-        python-version: ["3.8", "3.9", "3.10", "3.11"]
+        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
 
     steps:
       - uses: actions/checkout@v1
diff --git a/README.md b/README.md
index 8677619..30a6667 100644
--- a/README.md
+++ b/README.md
@@ -71,6 +71,7 @@ class MyClass:
 
 A method marked as `@classmethod` should access the parameter `cls`.
 Use `@staticmethod` otherwise.
+A special case that is exempt is a method that has a call to `super()`.
 
 **Bad** 
 ```python
diff --git a/flake8_classmethod_staticmethod/checker.py b/flake8_classmethod_staticmethod/checker.py
index dff0251..fe10b7b 100644
--- a/flake8_classmethod_staticmethod/checker.py
+++ b/flake8_classmethod_staticmethod/checker.py
@@ -121,8 +121,21 @@ def _csm130(node: ast.FunctionDef) -> Iterable[Tuple[int, int, str, type]]:
 def _csm131(node: ast.FunctionDef) -> Iterable[Tuple[int, int, str, type]]:
     if node.args.args:
         cls_param = node.args.args[0].arg
-        if not _function_uses_identifier(node, cls_param):
-            yield _error_tuple(131, node)
+        if _function_uses_identifier(node, cls_param):
+            return []
+
+        calls = [
+            child for child in ast.walk(node) if isinstance(child, ast.Call)
+        ]
+        for call in calls:
+            if (
+                isinstance(call.func, ast.Name)
+                and call.func.id == "super"
+                and len(call.args) == 0
+            ):
+                return []
+
+        yield _error_tuple(131, node)
 
 
 def _csm132(
diff --git a/requirements/py310.txt b/requirements/py310.txt
index 9eadc55..614730b 100644
--- a/requirements/py310.txt
+++ b/requirements/py310.txt
@@ -1,3 +1,3 @@
-flake8>=5,<6
+flake8>=7,<8
 pytest-flake8-path>=1.5.0,<2
 pytest>=7,<8
\ No newline at end of file
diff --git a/requirements/py311.txt b/requirements/py311.txt
index 9eadc55..614730b 100644
--- a/requirements/py311.txt
+++ b/requirements/py311.txt
@@ -1,3 +1,3 @@
-flake8>=5,<6
+flake8>=7,<8
 pytest-flake8-path>=1.5.0,<2
 pytest>=7,<8
\ No newline at end of file
diff --git a/requirements/py312.txt b/requirements/py312.txt
new file mode 100644
index 0000000..614730b
--- /dev/null
+++ b/requirements/py312.txt
@@ -0,0 +1,3 @@
+flake8>=7,<8
+pytest-flake8-path>=1.5.0,<2
+pytest>=7,<8
\ No newline at end of file
diff --git a/requirements/py38.txt b/requirements/py38.txt
index 9eadc55..614730b 100644
--- a/requirements/py38.txt
+++ b/requirements/py38.txt
@@ -1,3 +1,3 @@
-flake8>=5,<6
+flake8>=7,<8
 pytest-flake8-path>=1.5.0,<2
 pytest>=7,<8
\ No newline at end of file
diff --git a/requirements/py39.txt b/requirements/py39.txt
index 9eadc55..614730b 100644
--- a/requirements/py39.txt
+++ b/requirements/py39.txt
@@ -1,3 +1,3 @@
-flake8>=5,<6
+flake8>=7,<8
 pytest-flake8-path>=1.5.0,<2
 pytest>=7,<8
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index 4e7c78e..8954f74 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = flake8-classmethod-staticmethod
-version = 2.0
+version = 2.1
 description = flake8 plugin that checks rules regarding the staticmethod and classmethod decorators.
 long_description = file: README.md
 long_description_content_type = text/markdown
@@ -20,6 +20,7 @@ classifiers =
     Programming Language :: Python :: 3.9
     Programming Language :: Python :: 3.10
     Programming Language :: Python :: 3.11
+    Programming Language :: Python :: 3.12
 
 [options]
 packages =
diff --git a/tests/test_clst132.py b/tests/test_csm132.py
similarity index 100%
rename from tests/test_clst132.py
rename to tests/test_csm132.py
diff --git a/tox.ini b/tox.ini
index cee416a..df5afd3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 envlist =
-    py{38,39,310,311},
+    py{38,39,310,311,312},
 
 [gh-actions]
 python =
@@ -8,6 +8,7 @@ python =
     3.9: py39
     3.10: py310
     3.11: py311
+    3.12: py312
 
 [testenv]
 commands = pytest {posargs}
@@ -24,3 +25,6 @@ deps = -rrequirements/py310.txt
 [testenv:py311]
 deps = -rrequirements/py311.txt
 
+[testenv:py312]
+deps = -rrequirements/py312.txt
+


Test patch:
diff --git a/tests/test_clst100.py b/tests/test_csm100.py
similarity index 100%
rename from tests/test_clst100.py
rename to tests/test_csm100.py
diff --git a/tests/test_clst101.py b/tests/test_csm101.py
similarity index 100%
rename from tests/test_clst101.py
rename to tests/test_csm101.py
diff --git a/tests/test_clst130.py b/tests/test_csm130.py
similarity index 100%
rename from tests/test_clst130.py
rename to tests/test_csm130.py
diff --git a/tests/test_clst131.py b/tests/test_csm131.py
similarity index 81%
rename from tests/test_clst131.py
rename to tests/test_csm131.py
index d43697a..d3bcfc7 100644
--- a/tests/test_clst131.py
+++ b/tests/test_csm131.py
@@ -31,6 +31,16 @@ class Test_CSM131(BaseTest):
         result = self.run_flake8(code)
         assert result == []
 
+    def test_pass_3(self):
+        code = """
+        class FooX:
+            @classmethod
+            def bar(cls):
+                super()
+        """
+        result = self.run_flake8(code)
+        assert result == []
+
     def test_fail_1(self):
         code = """
         class FooX: