From 1b9fed83974b8e300ad2286c54eb3a00b78aa1c5 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Tue, 8 Aug 2023 21:51:37 +0100 Subject: [PATCH] Error on zero tab width (#6429) ## Summary Error if `tab-size` is set to zero (it is used as a divisor). Closes #6423. Also fixes a typo. ## Test Plan Running ruff with a config ```toml [tool.ruff] tab-size = 0 ``` returns an error message to the user saying that `tab-size` must be greater than zero. --- crates/ruff/src/line_width.rs | 23 ++++++++++++----------- crates/ruff/src/message/text.rs | 4 +--- crates/ruff/src/rules/pycodestyle/mod.rs | 3 ++- crates/ruff/src/settings/options.rs | 4 ++-- crates/ruff_cache/src/cache_key.rs | 8 ++++++++ ruff.schema.json | 4 ++-- 6 files changed, 27 insertions(+), 19 deletions(-) diff --git a/crates/ruff/src/line_width.rs b/crates/ruff/src/line_width.rs index 8619b42aa3..a3db08416c 100644 --- a/crates/ruff/src/line_width.rs +++ b/crates/ruff/src/line_width.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use std::num::NonZeroU8; use unicode_width::UnicodeWidthChar; use ruff_macros::CacheKey; @@ -83,7 +84,7 @@ impl LineWidth { } fn update(mut self, chars: impl Iterator) -> Self { - let tab_size: usize = self.tab_size.into(); + let tab_size: usize = self.tab_size.as_usize(); for c in chars { match c { '\t' => { @@ -144,22 +145,22 @@ impl PartialOrd for LineWidth { /// The size of a tab. #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, CacheKey)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct TabSize(pub u8); +pub struct TabSize(pub NonZeroU8); + +impl TabSize { + fn as_usize(self) -> usize { + self.0.get() as usize + } +} impl Default for TabSize { fn default() -> Self { - Self(4) + Self(NonZeroU8::new(4).unwrap()) } } -impl From for TabSize { - fn from(tab_size: u8) -> Self { +impl From for TabSize { + fn from(tab_size: NonZeroU8) -> Self { Self(tab_size) } } - -impl From for usize { - fn from(tab_size: TabSize) -> Self { - tab_size.0 as usize - } -} diff --git a/crates/ruff/src/message/text.rs b/crates/ruff/src/message/text.rs index 3aa5ebc545..31bbb614d6 100644 --- a/crates/ruff/src/message/text.rs +++ b/crates/ruff/src/message/text.rs @@ -293,12 +293,10 @@ impl Display for MessageCodeFrame<'_> { } fn replace_whitespace(source: &str, annotation_range: TextRange) -> SourceCode { - static TAB_SIZE: TabSize = TabSize(4); // TODO(jonathan): use `tab-size` - let mut result = String::new(); let mut last_end = 0; let mut range = annotation_range; - let mut line_width = LineWidth::new(TAB_SIZE); + let mut line_width = LineWidth::new(TabSize::default()); for (index, c) in source.char_indices() { let old_width = line_width.get(); diff --git a/crates/ruff/src/rules/pycodestyle/mod.rs b/crates/ruff/src/rules/pycodestyle/mod.rs index 9810feb01c..86eb5925c6 100644 --- a/crates/ruff/src/rules/pycodestyle/mod.rs +++ b/crates/ruff/src/rules/pycodestyle/mod.rs @@ -6,6 +6,7 @@ pub(crate) mod helpers; #[cfg(test)] mod tests { + use std::num::NonZeroU8; use std::path::Path; use anyhow::Result; @@ -204,7 +205,7 @@ mod tests { let diagnostics = test_path( Path::new("pycodestyle/E501_2.py"), &settings::Settings { - tab_size: tab_size.into(), + tab_size: NonZeroU8::new(tab_size).unwrap().into(), ..settings::Settings::for_rule(Rule::LineTooLong) }, )?; diff --git a/crates/ruff/src/settings/options.rs b/crates/ruff/src/settings/options.rs index 5e3ea14207..43d3618d77 100644 --- a/crates/ruff/src/settings/options.rs +++ b/crates/ruff/src/settings/options.rs @@ -312,13 +312,13 @@ pub struct Options { "# )] /// The line length to use when enforcing long-lines violations (like - /// `E501`). + /// `E501`). Must be greater than `0`. pub line_length: Option, #[option( default = "4", value_type = "int", example = r#" - tab_size = 8 + tab-size = 8 "# )] /// The tabulation size to calculate line length. diff --git a/crates/ruff_cache/src/cache_key.rs b/crates/ruff_cache/src/cache_key.rs index e015112bda..c62e580d1e 100644 --- a/crates/ruff_cache/src/cache_key.rs +++ b/crates/ruff_cache/src/cache_key.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::collections::hash_map::DefaultHasher; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::hash::{Hash, Hasher}; +use std::num::NonZeroU8; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; @@ -205,6 +206,13 @@ impl CacheKey for i8 { } } +impl CacheKey for NonZeroU8 { + #[inline] + fn cache_key(&self, state: &mut CacheKeyHasher) { + state.write_u8(self.get()); + } +} + macro_rules! impl_cache_key_tuple { () => ( impl CacheKey for () { diff --git a/ruff.schema.json b/ruff.schema.json index 2c5bfc3090..9d47d617dc 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -376,7 +376,7 @@ ] }, "line-length": { - "description": "The line length to use when enforcing long-lines violations (like `E501`).", + "description": "The line length to use when enforcing long-lines violations (like `E501`). Must be greater than `0`.", "anyOf": [ { "$ref": "#/definitions/LineLength" @@ -2741,7 +2741,7 @@ "description": "The size of a tab.", "type": "integer", "format": "uint8", - "minimum": 0.0 + "minimum": 1.0 }, "Version": { "type": "string"