diff --git a/crates/ruff_linter/src/rules/pylint/rules/unnecessary_dunder_call.rs b/crates/ruff_linter/src/rules/pylint/rules/unnecessary_dunder_call.rs index 28163c94fc..6da31dbd85 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/unnecessary_dunder_call.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/unnecessary_dunder_call.rs @@ -15,6 +15,34 @@ use ruff_python_ast::PythonVersion; /// Dunder names are not meant to be called explicitly and, in most cases, can /// be replaced with builtins or operators. /// +/// ## Fix safety +/// This fix is always unsafe. When replacing dunder method calls with operators +/// or builtins, the behavior can change in the following ways: +/// +/// 1. Types may implement only a subset of related dunder methods. Calling a +/// missing dunder method directly returns `NotImplemented`, but using the +/// equivalent operator raises a `TypeError`. +/// ```python +/// class C: pass +/// c = C() +/// c.__gt__(1) # before fix: NotImplemented +/// c > 1 # after fix: raises TypeError +/// ``` +/// 2. Instance-assigned dunder methods are ignored by operators and builtins. +/// ```python +/// class C: pass +/// c = C() +/// c.__bool__ = lambda: False +/// c.__bool__() # before fix: False +/// bool(c) # after fix: True +/// ``` +/// +/// 3. Even with built-in types, behavior can differ. +/// ```python +/// (1).__gt__(1.0) # before fix: NotImplemented +/// 1 > 1.0 # after fix: False +/// ``` +/// /// ## Example /// ```python /// three = (3.0).__str__()