From f670f9b22c213a721577fb3df62c7f7134b3aff9 Mon Sep 17 00:00:00 2001 From: Harutaka Kawamura Date: Wed, 11 Oct 2023 22:53:48 +0900 Subject: [PATCH] [SIM115] Allow `open` followed by `close` (#7916) --- .../test/fixtures/flake8_simplify/SIM115.py | 4 +++ .../rules/open_file_with_context_handler.rs | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM115.py b/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM115.py index 388b1fd670..6a6925c45a 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM115.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM115.py @@ -42,3 +42,7 @@ with contextlib.ExitStack(): with contextlib.ExitStack() as exit_stack: exit_stack_ = exit_stack f = exit_stack_.enter_context(open("filename")) + +# OK (quick one-liner to clear file contents) +open("filename", "w").close() +pathlib.Path("filename").open("w").close() diff --git a/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs b/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs index a55c49d880..49e7826217 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs @@ -127,12 +127,41 @@ fn is_open(checker: &mut Checker, func: &Expr) -> bool { } } +/// Return `true` if the current expression is followed by a `close` call. +fn is_closed(semantic: &SemanticModel) -> bool { + let Some(expr) = semantic.current_expression_grandparent() else { + return false; + }; + + let Expr::Call(ast::ExprCall { + func, arguments, .. + }) = expr + else { + return false; + }; + + if !arguments.is_empty() { + return false; + } + + let Expr::Attribute(ast::ExprAttribute { attr, .. }) = func.as_ref() else { + return false; + }; + + attr.as_str() == "close" +} + /// SIM115 pub(crate) fn open_file_with_context_handler(checker: &mut Checker, func: &Expr) { if !is_open(checker, func) { return; } + // Ex) `open("foo.txt").close()` + if is_closed(checker.semantic()) { + return; + } + // Ex) `with open("foo.txt") as f: ...` if checker.semantic().current_statement().is_with_stmt() { return;