ruff
95e517fa - [`flake8-annotations`] Don't suggest `NoReturn` for functions raising `NotImplementedError` (`ANN201`, `ANN202`, `ANN205`, `ANN206`) (#21311)

Commit
94 days ago
[`flake8-annotations`] Don't suggest `NoReturn` for functions raising `NotImplementedError` (`ANN201`, `ANN202`, `ANN205`, `ANN206`) (#21311) ## Summary Fix ANN201, ANN202, ANN205, and ANN206 to not automatically suggest `NoReturn`/`Never` return type annotations for functions that raise `NotImplementedError`. These rules will still flag missing return type annotations but won't provide an auto-fix, allowing developers to manually specify the actual return type that implementations should return. Fixes #18886 ## Problem Analysis The bug occurred because the `auto_return_type` function in `flake8_annotations/helpers.rs` detects when all control flow paths in a function raise an exception (`Terminal::Raise`) and automatically suggests `NoReturn`/`Never` as the return type. However, `NotImplementedError` is semantically different from other exceptions - it's used to indicate abstract methods that should be implemented by subclasses, not methods that truly never return. When a function raises `NotImplementedError`, it's typically an abstract method pattern where: - The base class defines the method signature but doesn't implement it - Subclasses are expected to override the method with a proper implementation - The return type should reflect what implementations will actually return, not `NoReturn` For example, in Django's serializer pattern: ```py class BaseSerializer: def to_internal_value(self, data): raise NotImplementedError('`to_internal_value()` must be implemented.') ``` This method should have a return type annotation like `dict` or `Any`, not `NoReturn`, because implementations will return actual values. ## Approach The fix modifies the `auto_return_type` function to: 1. **Added semantic analysis capability**: Modified `auto_return_type` to accept a `SemanticModel` parameter, enabling it to analyze exception types in raise statements. 2. **Created detection helper**: Implemented `only_raises_not_implemented_error()` function that: - Traverses the function body using a custom visitor to collect all `raise` statements - Checks if all raises are `NotImplementedError` (or `NotImplemented`) using semantic analysis - Returns `true` only if all raises are `NotImplementedError` 3. **Modified return type inference**: When `Terminal::Raise` is detected, the function now: - First checks if all raises are `NotImplementedError` - If yes, returns `None` (no auto-fix suggested, but diagnostic still emitted) - If no, returns `AutoPythonType::Never` as before (suggesting `NoReturn`/`Never`) 4. **Updated all call sites**: Updated all 4 call sites in `definition.rs` to pass `checker.semantic()`: - ANN201 (public functions) - ANN202 (private functions) - ANN205 (static methods) - ANN206 (class methods) The fix ensures that: - Functions raising `NotImplementedError` still get flagged for missing return type annotations - But no auto-fix is suggested, allowing manual specification of the correct return type - Functions raising other exceptions (like `ValueError`) still correctly get `NoReturn`/`Never` suggestions --------- Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
Author
Parents
Loading