diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9199be5..5000dcd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
# Change Log
+New error codes:
+* Introduce Y068: Don't use `@override` in stub files.
+
## 5.5.0
New error codes:
diff --git a/ERRORCODES.md b/ERRORCODES.md
index eae5916..db2565c 100644
--- a/ERRORCODES.md
+++ b/ERRORCODES.md
@@ -80,8 +80,8 @@ The following warnings are currently emitted by default:
| Y064 | Use simpler syntax to define final literal types. For example, use `x: Final = 42` instead of `x: Final[Literal[42]]`. | Style
| Y065 | Don't use bare `Incomplete` in argument and return annotations. Instead, leave them unannotated. Omitting an annotation entirely from a function will cause some type checkers to view the parameter or return type as "untyped"; this may result in stricter type-checking on code that makes use of the stubbed function. | Style
| Y066 | When using if/else with `sys.version_info`, put the code for new Python versions first. | Style
-| Y067 | Don't use `Incomplete | None = None` in
-argument annotations. Instead, just use `=None`. | Style
+| Y067 | Don't use `Incomplete \| None = None` in argument annotations. Instead, just use `=None`. | Style
+| Y068 | Don't use `@override` in stub files. Problems with a function signature deviating from its superclass are inherited from the implementation, and other tools such as stubtest are better placed to recognize deviations between stubs and the implementation. | Understanding stubs
## Warnings disabled by default
diff --git a/flake8_pyi/errors.py b/flake8_pyi/errors.py
index 239d76e..756b6a2 100644
--- a/flake8_pyi/errors.py
+++ b/flake8_pyi/errors.py
@@ -127,6 +127,8 @@ class Error(NamedTuple):
'put the code for new Python versions first, e.g. "{new_syntax}"'
)
Y067 = 'Y067 Use "=None" instead of "Incomplete | None = None"'
+Y068 = 'Y068 Do not use "@override" in stub files.'
+
Y090 = (
'Y090 "{original}" means '
'"a tuple of length 1, in which the sole element is of type {typ!r}". '
diff --git a/flake8_pyi/visitor.py b/flake8_pyi/visitor.py
index e2fd4b2..4bb1cb6 100644
--- a/flake8_pyi/visitor.py
+++ b/flake8_pyi/visitor.py
@@ -226,6 +226,7 @@ def _is_object(node: ast.AST | None, name: str, *, from_: Container[str]) -> boo
)
_is_Generic = partial(_is_object, name="Generic", from_=_TYPING_MODULES)
_is_Unpack = partial(_is_object, name="Unpack", from_=_TYPING_MODULES)
+_is_override = partial(_is_object, name="override", from_=_TYPING_MODULES)
def _is_union(node: ast.expr | None) -> TypeIs[ast.BinOp]:
@@ -2032,6 +2033,12 @@ def check_protocol_param_kinds(
pos_or_kw, errors.Y091.format(arg=pos_or_kw.arg, method=node.name)
)
+ def check_for_override(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
+ for deco in node.decorator_list:
+ if _is_override(deco):
+ self.error(deco, errors.Y068)
+ return
+
@staticmethod
def _is_positional_pre_570_argname(name: str) -> bool:
# https://peps.python.org/pep-0484/#positional-only-arguments
@@ -2094,6 +2101,7 @@ def _visit_function(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
self.check_self_typevars(node, decorator_names)
if self.enclosing_class_ctx.is_protocol_class:
self.check_protocol_param_kinds(node, decorator_names)
+ self.check_for_override(node)
def visit_arg(self, node: ast.arg) -> None:
if _is_NoReturn(node.annotation):
diff --git a/tests/override.pyi b/tests/override.pyi
new file mode 100644
index 0000000..34b4329
--- /dev/null
+++ b/tests/override.pyi
@@ -0,0 +1,24 @@
+import typing
+import typing as t
+from typing import override, override as over
+
+import typing_extensions
+
+class Foo:
+ def f(self) -> None: ...
+ def g(self) -> None: ...
+ def h(self) -> None: ...
+ def j(self) -> None: ...
+ def k(self) -> None: ...
+
+class Bar(Foo):
+ @override # Y068 Do not use "@override" in stub files.
+ def f(self) -> None: ...
+ @typing.override # Y068 Do not use "@override" in stub files.
+ def g(self) -> None: ...
+ @t.override # Ideally we'd catch this too, but the infrastructure is not in place.
+ def h(self) -> None: ...
+ @over # Ideally we'd catch this too, but the infrastructure is not in place.
+ def j(self) -> None: ...
+ @typing_extensions.override # Y068 Do not use "@override" in stub files.
+ def k(self) -> None: ...