diff --git a/Cargo.lock b/Cargo.lock index 60bfaefa5049..2962ff5ec9bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,7 +118,7 @@ dependencies = [ "biome_deserialize_macros", "biome_diagnostics", "biome_rowan", - "bitflags 2.5.0", + "enumflags2", "rustc-hash", "schemars", "serde", @@ -519,7 +519,6 @@ dependencies = [ "biome_grit_syntax", "biome_parser", "biome_rowan", - "bitflags 2.5.0", "insta", "quickcheck", "quickcheck_macros", diff --git a/crates/biome_analyze/Cargo.toml b/crates/biome_analyze/Cargo.toml index 4bd756e3e5da..94b961b898e6 100644 --- a/crates/biome_analyze/Cargo.toml +++ b/crates/biome_analyze/Cargo.toml @@ -18,7 +18,7 @@ biome_deserialize = { workspace = true, optional = true } biome_deserialize_macros = { workspace = true, optional = true } biome_diagnostics = { workspace = true } biome_rowan = { workspace = true } -bitflags = { workspace = true } +enumflags2 = { workspace = true } rustc-hash = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"], optional = true } diff --git a/crates/biome_analyze/src/categories.rs b/crates/biome_analyze/src/categories.rs index 2045c8e48e7e..308dfe2dbf58 100644 --- a/crates/biome_analyze/src/categories.rs +++ b/crates/biome_analyze/src/categories.rs @@ -1,7 +1,6 @@ +use enumflags2::{bitflags, BitFlags}; use std::borrow::Cow; -use bitflags::bitflags; - #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[cfg_attr( feature = "serde", @@ -172,13 +171,38 @@ pub enum SourceActionKind { Other(Cow<'static, str>), } -bitflags! { - #[derive(Debug, Copy, Clone, Eq, PartialEq)] - pub struct RuleCategories: u8 { - const SYNTAX = 1 << RuleCategory::Syntax as u8; - const LINT = 1 << RuleCategory::Lint as u8; - const ACTION = 1 << RuleCategory::Action as u8; - const TRANSFORMATION = 1 << RuleCategory::Transformation as u8; +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[bitflags] +#[repr(u8)] +pub(crate) enum Categories { + Syntax = 1 << RuleCategory::Syntax as u8, + Lint = 1 << RuleCategory::Lint as u8, + Action = 1 << RuleCategory::Action as u8, + Transformation = 1 << RuleCategory::Transformation as u8, +} + +#[derive(Debug, Copy, Clone)] +/// The categories supported by the analyzer. +/// +/// The default implementation of this type returns an instance with all the categories. +/// +/// Use [RuleCategoriesBuilder] to generate the categories you want to query. +pub struct RuleCategories(BitFlags); + +impl RuleCategories { + pub fn empty() -> Self { + let empty: BitFlags = BitFlags::empty(); + Self(empty) + } + + pub fn all() -> Self { + let empty: BitFlags = BitFlags::all(); + Self(empty) + } + + /// Checks whether the current categories contain a specific [RuleCategories] + pub fn contains(&self, other: impl Into) -> bool { + self.0.contains(other.into().0) } } @@ -190,17 +214,19 @@ impl Default for RuleCategories { impl RuleCategories { pub fn is_syntax(&self) -> bool { - *self == RuleCategories::SYNTAX + self.0.contains(Categories::Syntax) } } impl From for RuleCategories { fn from(input: RuleCategory) -> Self { match input { - RuleCategory::Syntax => RuleCategories::SYNTAX, - RuleCategory::Lint => RuleCategories::LINT, - RuleCategory::Action => RuleCategories::ACTION, - RuleCategory::Transformation => RuleCategories::TRANSFORMATION, + RuleCategory::Syntax => RuleCategories(BitFlags::from_flag(Categories::Syntax)), + RuleCategory::Lint => RuleCategories(BitFlags::from_flag(Categories::Lint)), + RuleCategory::Action => RuleCategories(BitFlags::from_flag(Categories::Action)), + RuleCategory::Transformation => { + RuleCategories(BitFlags::from_flag(Categories::Transformation)) + } } } } @@ -213,19 +239,19 @@ impl serde::Serialize for RuleCategories { { let mut flags = Vec::new(); - if self.contains(Self::SYNTAX) { + if self.0.contains(Categories::Syntax) { flags.push(RuleCategory::Syntax); } - if self.contains(Self::LINT) { + if self.0.contains(Categories::Lint) { flags.push(RuleCategory::Lint); } - if self.contains(Self::ACTION) { + if self.0.contains(Categories::Action) { flags.push(RuleCategory::Action); } - if self.contains(Self::TRANSFORMATION) { + if self.0.contains(Categories::Transformation) { flags.push(RuleCategory::Transformation); } @@ -258,7 +284,7 @@ impl<'de> serde::Deserialize<'de> for RuleCategories { let mut result = RuleCategories::empty(); while let Some(item) = seq.next_element::()? { - result |= RuleCategories::from(item); + result.0 |= RuleCategories::from(item).0; } Ok(result) @@ -279,3 +305,45 @@ impl schemars::JsonSchema for RuleCategories { >::json_schema(gen) } } + +#[derive(Debug, Default)] +/// A convenient type create a [RuleCategories] type +/// +/// ``` +/// use biome_analyze::{RuleCategoriesBuilder, RuleCategory}; +/// let mut categories = RuleCategoriesBuilder::default().with_syntax().with_lint().build(); +/// +/// assert!(categories.contains(RuleCategory::Lint)); +/// assert!(categories.contains(RuleCategory::Syntax)); +/// assert!(!categories.contains(RuleCategory::Action)); +/// assert!(!categories.contains(RuleCategory::Transformation)); +/// ``` +pub struct RuleCategoriesBuilder { + flags: BitFlags, +} + +impl RuleCategoriesBuilder { + pub fn with_syntax(mut self) -> Self { + self.flags.insert(Categories::Syntax); + self + } + + pub fn with_lint(mut self) -> Self { + self.flags.insert(Categories::Lint); + self + } + + pub fn with_action(mut self) -> Self { + self.flags.insert(Categories::Action); + self + } + + pub fn with_transformation(mut self) -> Self { + self.flags.insert(Categories::Transformation); + self + } + + pub fn build(self) -> RuleCategories { + RuleCategories(self.flags) + } +} diff --git a/crates/biome_analyze/src/lib.rs b/crates/biome_analyze/src/lib.rs index 56aabf5afdc9..13f40594c378 100644 --- a/crates/biome_analyze/src/lib.rs +++ b/crates/biome_analyze/src/lib.rs @@ -24,7 +24,8 @@ mod visitor; pub use biome_diagnostics::category_concat; pub use crate::categories::{ - ActionCategory, RefactorKind, RuleCategories, RuleCategory, SourceActionKind, + ActionCategory, RefactorKind, RuleCategories, RuleCategoriesBuilder, RuleCategory, + SourceActionKind, }; pub use crate::diagnostics::AnalyzerDiagnostic; pub use crate::diagnostics::SuppressionDiagnostic; @@ -885,7 +886,7 @@ impl<'analysis> AnalysisFilter<'analysis> { /// Return `true` if the category `C` matches this filter pub fn match_category(&self) -> bool { - self.categories.contains(C::CATEGORY.into()) + self.categories.contains(C::CATEGORY) } /// Return `true` if the group `G` matches this filter diff --git a/crates/biome_cli/src/execute/process_file/format.rs b/crates/biome_cli/src/execute/process_file/format.rs index fbc2be9c40b3..57e637ab4aed 100644 --- a/crates/biome_cli/src/execute/process_file/format.rs +++ b/crates/biome_cli/src/execute/process_file/format.rs @@ -4,9 +4,9 @@ use crate::execute::process_file::{ DiffKind, FileResult, FileStatus, Message, SharedTraversalOptions, }; use crate::execute::TraversalMode; +use biome_analyze::RuleCategoriesBuilder; use biome_diagnostics::{category, Diagnostic, DiagnosticExt, Error, Severity}; use biome_service::file_handlers::{AstroFileHandler, SvelteFileHandler, VueFileHandler}; -use biome_service::workspace::RuleCategories; use std::path::Path; use std::sync::atomic::Ordering; use tracing::debug; @@ -27,7 +27,7 @@ pub(crate) fn format_with_guard<'ctx>( let diagnostics_result = workspace_file .guard() .pull_diagnostics( - RuleCategories::SYNTAX, + RuleCategoriesBuilder::default().with_syntax().build(), max_diagnostics.into(), Vec::new(), Vec::new(), diff --git a/crates/biome_cli/src/execute/process_file/lint.rs b/crates/biome_cli/src/execute/process_file/lint.rs index 9bbe4e45a0b3..b3983388ae20 100644 --- a/crates/biome_cli/src/execute/process_file/lint.rs +++ b/crates/biome_cli/src/execute/process_file/lint.rs @@ -2,9 +2,9 @@ use crate::execute::diagnostics::ResultExt; use crate::execute::process_file::workspace_file::WorkspaceFile; use crate::execute::process_file::{FileResult, FileStatus, Message, SharedTraversalOptions}; use crate::TraversalMode; +use biome_analyze::RuleCategoriesBuilder; use biome_diagnostics::{category, Error}; use biome_service::file_handlers::{AstroFileHandler, SvelteFileHandler, VueFileHandler}; -use biome_service::workspace::RuleCategories; use std::path::Path; use std::sync::atomic::Ordering; @@ -66,7 +66,10 @@ pub(crate) fn lint_with_guard<'ctx>( let pull_diagnostics_result = workspace_file .guard() .pull_diagnostics( - RuleCategories::LINT | RuleCategories::SYNTAX, + RuleCategoriesBuilder::default() + .with_syntax() + .with_lint() + .build(), max_diagnostics.into(), only, skip, diff --git a/crates/biome_cli/src/execute/std_in.rs b/crates/biome_cli/src/execute/std_in.rs index ae3d44d4df35..ecdb12d0d69d 100644 --- a/crates/biome_cli/src/execute/std_in.rs +++ b/crates/biome_cli/src/execute/std_in.rs @@ -3,6 +3,7 @@ use crate::execute::diagnostics::{ContentDiffAdvice, FormatDiffDiagnostic}; use crate::execute::Execution; use crate::{CliDiagnostic, CliSession, TraversalMode}; +use biome_analyze::RuleCategoriesBuilder; use biome_console::{markup, ConsoleExt}; use biome_diagnostics::PrintDiagnostic; use biome_diagnostics::{Diagnostic, DiagnosticExt, Error}; @@ -10,8 +11,7 @@ use biome_fs::BiomePath; use biome_service::file_handlers::{AstroFileHandler, SvelteFileHandler, VueFileHandler}; use biome_service::workspace::{ ChangeFileParams, DropPatternParams, FeaturesBuilder, FixFileParams, FormatFileParams, - OpenFileParams, OrganizeImportsParams, PullDiagnosticsParams, RuleCategories, - SupportsFeatureParams, + OpenFileParams, OrganizeImportsParams, PullDiagnosticsParams, SupportsFeatureParams, }; use biome_service::WorkspaceError; use std::borrow::Cow; @@ -163,7 +163,10 @@ pub(crate) fn run<'a>( }; if !mode.is_check_apply_unsafe() { let result = workspace.pull_diagnostics(PullDiagnosticsParams { - categories: RuleCategories::LINT | RuleCategories::SYNTAX, + categories: RuleCategoriesBuilder::default() + .with_lint() + .with_syntax() + .build(), path: biome_path.clone(), max_diagnostics: mode.max_diagnostics.into(), only, diff --git a/crates/biome_grit_parser/Cargo.toml b/crates/biome_grit_parser/Cargo.toml index 6dea794bcf5c..e577f5672d92 100644 --- a/crates/biome_grit_parser/Cargo.toml +++ b/crates/biome_grit_parser/Cargo.toml @@ -17,7 +17,6 @@ biome_grit_factory = { workspace = true } biome_grit_syntax = { workspace = true } biome_parser = { workspace = true } biome_rowan = { workspace = true } -bitflags = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/crates/biome_js_analyze/src/lib.rs b/crates/biome_js_analyze/src/lib.rs index e0f0912342d9..2baff312c935 100644 --- a/crates/biome_js_analyze/src/lib.rs +++ b/crates/biome_js_analyze/src/lib.rs @@ -230,7 +230,7 @@ impl Error for RuleError {} #[cfg(test)] mod tests { use biome_analyze::options::RuleOptions; - use biome_analyze::{AnalyzerOptions, Never, RuleCategories, RuleFilter, RuleKey}; + use biome_analyze::{AnalyzerOptions, Never, RuleCategoriesBuilder, RuleFilter, RuleKey}; use biome_console::fmt::{Formatter, Termcolor}; use biome_console::{markup, Markup}; use biome_diagnostics::category; @@ -451,7 +451,7 @@ mod tests { ); let filter = AnalysisFilter { - categories: RuleCategories::SYNTAX, + categories: RuleCategoriesBuilder::default().with_syntax().build(), ..AnalysisFilter::default() }; diff --git a/crates/biome_js_transform/src/lib.rs b/crates/biome_js_transform/src/lib.rs index b369e8488120..8090aae12cc0 100644 --- a/crates/biome_js_transform/src/lib.rs +++ b/crates/biome_js_transform/src/lib.rs @@ -120,7 +120,7 @@ pub(crate) type JsBatchMutation = BatchMutation; #[cfg(test)] mod tests { - use biome_analyze::{AnalyzerOptions, Never, RuleCategories, RuleFilter}; + use biome_analyze::{AnalyzerOptions, Never, RuleCategoriesBuilder, RuleFilter}; use biome_js_parser::{parse, JsParserOptions}; use biome_js_syntax::JsFileSource; use std::slice; @@ -140,7 +140,9 @@ mod tests { transform( &parsed.tree(), AnalysisFilter { - categories: RuleCategories::TRANSFORMATION, + categories: RuleCategoriesBuilder::default() + .with_transformation() + .build(), enabled_rules: Some(slice::from_ref(&rule_filter)), ..AnalysisFilter::default() }, diff --git a/crates/biome_lsp/src/session.rs b/crates/biome_lsp/src/session.rs index a0b238a92633..9c2b845eed0c 100644 --- a/crates/biome_lsp/src/session.rs +++ b/crates/biome_lsp/src/session.rs @@ -4,7 +4,7 @@ use crate::extension_settings::ExtensionSettings; use crate::extension_settings::CONFIGURATION_SECTION; use crate::utils; use anyhow::Result; -use biome_analyze::RuleCategories; +use biome_analyze::RuleCategoriesBuilder; use biome_configuration::ConfigurationPathHint; use biome_console::markup; use biome_diagnostics::PrintDescription; @@ -316,18 +316,18 @@ impl Session { })?; let diagnostics: Vec = { - let mut categories = RuleCategories::SYNTAX; + let mut categories = RuleCategoriesBuilder::default().with_syntax(); if self.configuration_status().is_loaded() { if file_features.supports_lint() { - categories |= RuleCategories::LINT + categories = categories.with_lint(); } if file_features.supports_organize_imports() { - categories |= RuleCategories::ACTION + categories = categories.with_action(); } } let result = self.workspace.pull_diagnostics(PullDiagnosticsParams { path: biome_path.clone(), - categories, + categories: categories.build(), max_diagnostics: u64::MAX, only: Vec::new(), skip: Vec::new(), diff --git a/crates/biome_service/src/file_handlers/css.rs b/crates/biome_service/src/file_handlers/css.rs index 2eadb4d1bed5..d10701692196 100644 --- a/crates/biome_service/src/file_handlers/css.rs +++ b/crates/biome_service/src/file_handlers/css.rs @@ -18,7 +18,8 @@ use crate::workspace::{ use crate::WorkspaceError; use biome_analyze::options::PreferredQuote; use biome_analyze::{ - AnalysisFilter, AnalyzerConfiguration, AnalyzerOptions, ControlFlow, Never, RuleCategories, + AnalysisFilter, AnalyzerConfiguration, AnalyzerOptions, ControlFlow, Never, + RuleCategoriesBuilder, RuleCategory, }; use biome_css_analyze::analyze; use biome_css_formatter::context::CssFormatOptions; @@ -386,7 +387,7 @@ fn lint(params: LintParams) -> LintResults { // - it is a syntax-only analyzer pass, or // - if a single rule is run. let ignores_suppression_comment = - !filter.categories.contains(RuleCategories::LINT) || has_only_filter; + !filter.categories.contains(RuleCategory::Lint) || has_only_filter; let mut diagnostic_count = diagnostics.len() as u32; let mut errors = diagnostics @@ -488,10 +489,11 @@ pub(crate) fn code_actions(params: CodeActionsParams) -> PullActionsResult { let mut filter = AnalysisFilter::from_enabled_rules(filter.as_slice()); - filter.categories = RuleCategories::SYNTAX | RuleCategories::LINT; + let mut categories = RuleCategoriesBuilder::default().with_syntax().with_lint(); if settings.organize_imports.enabled { - filter.categories |= RuleCategories::ACTION; + categories = categories.with_action(); } + filter.categories = categories.build(); filter.range = Some(range); let analyzer_options = workspace.analyzer_options::(path, &language); diff --git a/crates/biome_service/src/file_handlers/javascript.rs b/crates/biome_service/src/file_handlers/javascript.rs index 4bd999460750..5c92899f071b 100644 --- a/crates/biome_service/src/file_handlers/javascript.rs +++ b/crates/biome_service/src/file_handlers/javascript.rs @@ -21,7 +21,7 @@ use crate::{ use biome_analyze::options::PreferredQuote; use biome_analyze::{ AnalysisFilter, AnalyzerConfiguration, AnalyzerOptions, ControlFlow, GroupCategory, Never, - QueryMatch, RegistryVisitor, RuleCategories, RuleCategory, RuleFilter, RuleGroup, + QueryMatch, RegistryVisitor, RuleCategoriesBuilder, RuleCategory, RuleFilter, RuleGroup, }; use biome_configuration::javascript::JsxRuntime; use biome_diagnostics::{category, Applicability, Diagnostic, DiagnosticExt, Severity}; @@ -343,7 +343,7 @@ fn debug_control_flow(parse: AnyParse, cursor: TextSize) -> String { let mut control_flow_graph = None; let filter = AnalysisFilter { - categories: RuleCategories::LINT, + categories: RuleCategoriesBuilder::default().with_lint().build(), enabled_rules: Some(&[RuleFilter::Rule("correctness", "noUnreachable")]), ..AnalysisFilter::default() }; @@ -468,7 +468,7 @@ pub(crate) fn lint(params: LintParams) -> LintResults { // - it is a syntax-only analyzer pass, or // - if a single rule is run. let ignores_suppression_comment = - !filter.categories.contains(RuleCategories::LINT) || has_only_filter; + !filter.categories.contains(RuleCategory::Lint) || has_only_filter; let mut diagnostic_count = diagnostics.len() as u32; let mut errors = diagnostics @@ -614,10 +614,11 @@ pub(crate) fn code_actions(params: CodeActionsParams) -> PullActionsResult { AnalysisFilter::default() }; - filter.categories = RuleCategories::SYNTAX | RuleCategories::LINT; + let mut categories = RuleCategoriesBuilder::default().with_syntax().with_lint(); if settings.organize_imports.enabled { - filter.categories |= RuleCategories::ACTION; + categories = categories.with_action(); } + filter.categories = categories.build(); filter.range = Some(range); let Some(source_type) = language.to_js_file_source() else { @@ -696,7 +697,10 @@ pub(crate) fn fix_all(params: FixAllParams) -> Result Result LintResults { // - it is a syntax-only analyzer pass, or // - if a single rule is run. let ignores_suppression_comment = - !filter.categories.contains(RuleCategories::LINT) || has_only_filter; + !filter.categories.contains(RuleCategory::Lint) || has_only_filter; let mut diagnostic_count = diagnostics.len() as u32; let mut errors = diagnostics diff --git a/xtask/bench/src/language.rs b/xtask/bench/src/language.rs index 19ec8fe998f8..479350c9ac26 100644 --- a/xtask/bench/src/language.rs +++ b/xtask/bench/src/language.rs @@ -1,6 +1,6 @@ use crate::test_case::TestCase; use biome_analyze::options::JsxRuntime; -use biome_analyze::{AnalysisFilter, AnalyzerOptions, ControlFlow, Never, RuleCategories}; +use biome_analyze::{AnalysisFilter, AnalyzerOptions, ControlFlow, Never, RuleCategoriesBuilder}; use biome_css_formatter::context::{CssFormatContext, CssFormatOptions}; use biome_css_parser::CssParserOptions; use biome_css_syntax::{CssRoot, CssSyntaxNode}; @@ -172,7 +172,10 @@ impl Analyze { match self { Analyze::JavaScript(root) => { let filter = AnalysisFilter { - categories: RuleCategories::SYNTAX | RuleCategories::LINT, + categories: RuleCategoriesBuilder::default() + .with_syntax() + .with_lint() + .build(), ..AnalysisFilter::default() }; let mut options = AnalyzerOptions::default(); @@ -192,7 +195,10 @@ impl Analyze { } Analyze::Css(root) => { let filter = AnalysisFilter { - categories: RuleCategories::SYNTAX | RuleCategories::LINT, + categories: RuleCategoriesBuilder::default() + .with_syntax() + .with_lint() + .build(), ..AnalysisFilter::default() }; let options = AnalyzerOptions::default();