From a1777d866c0aa10a617fe69eccea0a6effa95a65 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sat, 23 Sep 2023 15:26:15 -0400 Subject: [PATCH] Skip BOM when inserting start-of-file imports --- .../test/fixtures/flake8_simplify/SIM105_4.py | 4 ++++ crates/ruff_linter/src/importer/insertion.rs | 2 +- .../src/rules/flake8_simplify/mod.rs | 1 + ...8_simplify__tests__SIM105_SIM105_4.py.snap | 22 +++++++++++++++++++ crates/ruff_source_file/src/locator.rs | 14 +++++++++--- 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100755 crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM105_4.py create mode 100644 crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM105_SIM105_4.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM105_4.py b/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM105_4.py new file mode 100755 index 0000000000000..ad3a5d57185f7 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM105_4.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python +try: + from __builtin__ import bytes, str, open, super, range, zip, round, int, pow, object, input +except ImportError: pass \ No newline at end of file diff --git a/crates/ruff_linter/src/importer/insertion.rs b/crates/ruff_linter/src/importer/insertion.rs index c4fad038fdb2e..01b442dce5f19 100644 --- a/crates/ruff_linter/src/importer/insertion.rs +++ b/crates/ruff_linter/src/importer/insertion.rs @@ -64,7 +64,7 @@ impl<'a> Insertion<'a> { // Otherwise, advance to the next row. locator.full_line_end(location) } else { - TextSize::default() + locator.contents_start() }; // Skip over commented lines, with whitespace separation. diff --git a/crates/ruff_linter/src/rules/flake8_simplify/mod.rs b/crates/ruff_linter/src/rules/flake8_simplify/mod.rs index bd22a1a0ac746..82a7d33c9b0c8 100644 --- a/crates/ruff_linter/src/rules/flake8_simplify/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_simplify/mod.rs @@ -19,6 +19,7 @@ mod tests { #[test_case(Rule::SuppressibleException, Path::new("SIM105_1.py"))] #[test_case(Rule::SuppressibleException, Path::new("SIM105_2.py"))] #[test_case(Rule::SuppressibleException, Path::new("SIM105_3.py"))] + #[test_case(Rule::SuppressibleException, Path::new("SIM105_4.py"))] #[test_case(Rule::ReturnInTryExceptFinally, Path::new("SIM107.py"))] #[test_case(Rule::IfElseBlockInsteadOfIfExp, Path::new("SIM108.py"))] #[test_case(Rule::CompareWithTuple, Path::new("SIM109.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM105_SIM105_4.py.snap b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM105_SIM105_4.py.snap new file mode 100644 index 0000000000000..8f221aa111e30 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_simplify/snapshots/ruff_linter__rules__flake8_simplify__tests__SIM105_SIM105_4.py.snap @@ -0,0 +1,22 @@ +--- +source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs +--- +SIM105_4.py:2:1: SIM105 [*] Use `contextlib.suppress(ImportError)` instead of `try`-`except`-`pass` + | +1 | #!/usr/bin/env python +2 | / try: +3 | | from __builtin__ import bytes, str, open, super, range, zip, round, int, pow, object, input +4 | | except ImportError: pass + | |___________________________^ SIM105 + | + = help: Replace with `contextlib.suppress(ImportError)` + +ℹ Suggested fix +1 1 | #!/usr/bin/env python +2 |-try: + 2 |+import contextlib + 3 |+with contextlib.suppress(ImportError): +3 4 | from __builtin__ import bytes, str, open, super, range, zip, round, int, pow, object, input +4 |-except ImportError: pass + + diff --git a/crates/ruff_source_file/src/locator.rs b/crates/ruff_source_file/src/locator.rs index 97bbf911e628f..a642ce4afe957 100644 --- a/crates/ruff_source_file/src/locator.rs +++ b/crates/ruff_source_file/src/locator.rs @@ -76,7 +76,15 @@ impl<'a> Locator<'a> { if let Some(index) = memrchr2(b'\n', b'\r', bytes) { // SAFETY: Safe because `index < offset` TextSize::try_from(index).unwrap().add(TextSize::from(1)) - } else if self.contents.starts_with('\u{feff}') { + } else { + self.contents_start() + } + } + + /// Computes the start position of the file contents: either the first byte, or the byte after + /// the BOM. + pub fn contents_start(&self) -> TextSize { + if self.contents.starts_with('\u{feff}') { // Skip the BOM. '\u{feff}'.text_len() } else { @@ -85,9 +93,9 @@ impl<'a> Locator<'a> { } } + /// Returns `true` if `offset` is at the start of a line. pub fn is_at_start_of_line(&self, offset: TextSize) -> bool { - offset == TextSize::from(0) - || self.contents[TextRange::up_to(offset)].ends_with(['\n', '\r']) + self.line_start(offset) == offset } /// Computes the offset that is right after the newline character that ends `offset`'s line.