From bf0551d0e622311d67dde82b7c5d285bd59f6e05 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Wed, 28 Aug 2024 18:50:17 +0200 Subject: [PATCH] refcator: avoid UTF-8 validation of extensions --- .../biome_cli/src/execute/migrate/eslint.rs | 4 +-- .../biome_cli/src/execute/migrate/prettier.rs | 5 ++- .../src/execute/process_file/assists.rs | 10 +++--- .../src/execute/process_file/format.rs | 9 +++--- .../src/execute/process_file/lint.rs | 17 +++++----- .../execute/process_file/organize_imports.rs | 10 +++--- .../execute/process_file/workspace_file.rs | 5 +-- crates/biome_cli/src/execute/std_in.rs | 32 +++++++++---------- crates/biome_fs/src/fs/os.rs | 7 ++-- crates/biome_fs/src/path.rs | 5 --- crates/biome_grit_patterns/src/grit_query.rs | 2 +- crates/biome_js_analyze/tests/spec_tests.rs | 12 +++---- crates/biome_lsp/src/handlers/analysis.rs | 9 +++--- crates/biome_lsp/src/handlers/formatting.rs | 18 +++++------ crates/biome_lsp/src/session.rs | 9 +++--- .../tests/manifest_spec_tests.rs | 15 ++++----- crates/biome_service/src/configuration.rs | 24 +++++++------- crates/biome_service/src/diagnostics.rs | 7 ++-- .../biome_service/src/file_handlers/json.rs | 14 ++++---- crates/biome_service/src/workspace.rs | 22 +++++++------ crates/biome_service/src/workspace/server.rs | 5 ++- crates/biome_service/tests/spec_tests.rs | 14 ++++---- crates/tests_macros/src/lib.rs | 4 +-- 23 files changed, 129 insertions(+), 130 deletions(-) diff --git a/crates/biome_cli/src/execute/migrate/eslint.rs b/crates/biome_cli/src/execute/migrate/eslint.rs index 9f69addb28dd..255224bcf4ed 100644 --- a/crates/biome_cli/src/execute/migrate/eslint.rs +++ b/crates/biome_cli/src/execute/migrate/eslint.rs @@ -6,6 +6,7 @@ use biome_fs::{FileSystem, OpenOptions}; use biome_json_parser::JsonParserOptions; use biome_service::DynRef; use std::borrow::Cow; +use std::ffi::OsStr; use std::path::{Path, PathBuf}; use crate::diagnostics::MigrationDiagnostic; @@ -154,8 +155,7 @@ fn load_legacy_config_data( path: &Path, console: &mut dyn Console, ) -> Result { - let (deserialized, diagnostics) = match path.extension().and_then(|file_ext| file_ext.to_str()) - { + let (deserialized, diagnostics) = match path.extension().and_then(OsStr::to_str) { None | Some("json") => { let mut file = fs.open_with_options(path, OpenOptions::default().read(true))?; let mut content = String::new(); diff --git a/crates/biome_cli/src/execute/migrate/prettier.rs b/crates/biome_cli/src/execute/migrate/prettier.rs index e0739b03dc99..eae4d3231e91 100644 --- a/crates/biome_cli/src/execute/migrate/prettier.rs +++ b/crates/biome_cli/src/execute/migrate/prettier.rs @@ -12,7 +12,7 @@ use biome_fs::{FileSystem, OpenOptions}; use biome_js_formatter::context::{ArrowParentheses, QuoteProperties, Semicolons, TrailingCommas}; use biome_json_parser::JsonParserOptions; use biome_service::DynRef; -use std::path::Path; +use std::{ffi::OsStr, path::Path}; use super::{eslint_eslint::ShorthandVec, node}; @@ -416,8 +416,7 @@ fn load_config( path: &Path, console: &mut dyn Console, ) -> Result { - let (deserialized, diagnostics) = match path.extension().and_then(|file_ext| file_ext.to_str()) - { + let (deserialized, diagnostics) = match path.extension().and_then(OsStr::to_str) { None | Some("json") => { let mut file = fs.open_with_options(path, OpenOptions::default().read(true))?; let mut content = String::new(); diff --git a/crates/biome_cli/src/execute/process_file/assists.rs b/crates/biome_cli/src/execute/process_file/assists.rs index 9ad57a298e34..6afe2c6cf1fb 100644 --- a/crates/biome_cli/src/execute/process_file/assists.rs +++ b/crates/biome_cli/src/execute/process_file/assists.rs @@ -1,3 +1,5 @@ +use std::ffi::OsStr; + use crate::execute::diagnostics::ResultExt; use crate::execute::process_file::workspace_file::WorkspaceFile; use crate::execute::process_file::{ @@ -38,14 +40,14 @@ pub(crate) fn assists_with_guard<'ctx>( let mut output = fix_result.code; - match workspace_file.as_extension() { - Some("astro") => { + match workspace_file.as_extension().map(OsStr::as_encoded_bytes) { + Some(b"astro") => { output = AstroFileHandler::output(input.as_str(), output.as_str()); } - Some("vue") => { + Some(b"vue") => { output = VueFileHandler::output(input.as_str(), output.as_str()); } - Some("svelte") => { + Some(b"svelte") => { output = SvelteFileHandler::output(input.as_str(), output.as_str()); } _ => {} diff --git a/crates/biome_cli/src/execute/process_file/format.rs b/crates/biome_cli/src/execute/process_file/format.rs index b58596b827f1..306067d56769 100644 --- a/crates/biome_cli/src/execute/process_file/format.rs +++ b/crates/biome_cli/src/execute/process_file/format.rs @@ -7,6 +7,7 @@ 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 std::ffi::OsStr; use std::path::Path; use std::sync::atomic::Ordering; use tracing::debug; @@ -90,21 +91,21 @@ pub(crate) fn format_with_guard<'ctx>( return Ok(FileStatus::Ignored); } - match workspace_file.as_extension() { - Some("astro") => { + match workspace_file.as_extension().map(OsStr::as_encoded_bytes) { + Some(b"astro") => { if output.is_empty() { return Ok(FileStatus::Unchanged); } output = AstroFileHandler::output(input.as_str(), output.as_str()); } - Some("vue") => { + Some(b"vue") => { if output.is_empty() { return Ok(FileStatus::Unchanged); } output = VueFileHandler::output(input.as_str(), output.as_str()); } - Some("svelte") => { + Some(b"svelte") => { if output.is_empty() { return Ok(FileStatus::Unchanged); } diff --git a/crates/biome_cli/src/execute/process_file/lint.rs b/crates/biome_cli/src/execute/process_file/lint.rs index 6fa4e8c1f06f..b5e4a7db823c 100644 --- a/crates/biome_cli/src/execute/process_file/lint.rs +++ b/crates/biome_cli/src/execute/process_file/lint.rs @@ -6,6 +6,7 @@ use biome_analyze::RuleCategoriesBuilder; use biome_diagnostics::{category, Error}; use biome_rowan::TextSize; use biome_service::file_handlers::{AstroFileHandler, SvelteFileHandler, VueFileHandler}; +use std::ffi::OsStr; use std::path::Path; use std::sync::atomic::Ordering; @@ -53,14 +54,14 @@ pub(crate) fn lint_with_guard<'ctx>( let mut output = fix_result.code; - match workspace_file.as_extension() { - Some("astro") => { + match workspace_file.as_extension().map(OsStr::as_encoded_bytes) { + Some(b"astro") => { output = AstroFileHandler::output(input.as_str(), output.as_str()); } - Some("vue") => { + Some(b"vue") => { output = VueFileHandler::output(input.as_str(), output.as_str()); } - Some("svelte") => { + Some(b"svelte") => { output = SvelteFileHandler::output(input.as_str(), output.as_str()); } _ => {} @@ -93,10 +94,10 @@ pub(crate) fn lint_with_guard<'ctx>( && pull_diagnostics_result.skipped_diagnostics == 0; if !no_diagnostics { - let offset = match workspace_file.as_extension() { - Some("vue") => VueFileHandler::start(input.as_str()), - Some("astro") => AstroFileHandler::start(input.as_str()), - Some("svelte") => SvelteFileHandler::start(input.as_str()), + let offset = match workspace_file.as_extension().map(OsStr::as_encoded_bytes) { + Some(b"vue") => VueFileHandler::start(input.as_str()), + Some(b"astro") => AstroFileHandler::start(input.as_str()), + Some(b"svelte") => SvelteFileHandler::start(input.as_str()), _ => None, }; diff --git a/crates/biome_cli/src/execute/process_file/organize_imports.rs b/crates/biome_cli/src/execute/process_file/organize_imports.rs index 6bdbbbefd84e..eb2bb9902222 100644 --- a/crates/biome_cli/src/execute/process_file/organize_imports.rs +++ b/crates/biome_cli/src/execute/process_file/organize_imports.rs @@ -1,3 +1,5 @@ +use std::ffi::OsStr; + use crate::execute::diagnostics::ResultExt; use crate::execute::process_file::workspace_file::WorkspaceFile; use crate::execute::process_file::{ @@ -24,21 +26,21 @@ pub(crate) fn organize_imports_with_guard<'ctx>( let input = workspace_file.input()?; let mut output = sorted.code; - match workspace_file.as_extension() { - Some("astro") => { + match workspace_file.as_extension().map(OsStr::as_encoded_bytes) { + Some(b"astro") => { if output.is_empty() { return Ok(FileStatus::Unchanged); } output = AstroFileHandler::output(input.as_str(), output.as_str()); } - Some("vue") => { + Some(b"vue") => { if output.is_empty() { return Ok(FileStatus::Unchanged); } output = VueFileHandler::output(input.as_str(), output.as_str()); } - Some("svelte") => { + Some(b"svelte") => { if output.is_empty() { return Ok(FileStatus::Unchanged); } diff --git a/crates/biome_cli/src/execute/process_file/workspace_file.rs b/crates/biome_cli/src/execute/process_file/workspace_file.rs index 6503c64c6baa..8855bd84e06e 100644 --- a/crates/biome_cli/src/execute/process_file/workspace_file.rs +++ b/crates/biome_cli/src/execute/process_file/workspace_file.rs @@ -4,6 +4,7 @@ use biome_diagnostics::{category, Error}; use biome_fs::{BiomePath, File, OpenOptions}; use biome_service::workspace::{FileGuard, OpenFileParams}; use biome_service::{Workspace, WorkspaceError}; +use std::ffi::OsStr; use std::path::{Path, PathBuf}; /// Small wrapper that holds information and operations around the current processed file @@ -59,8 +60,8 @@ impl<'ctx, 'app> WorkspaceFile<'ctx, 'app> { self.guard().get_file_content() } - pub(crate) fn as_extension(&self) -> Option<&str> { - self.path.extension().and_then(|s| s.to_str()) + pub(crate) fn as_extension(&self) -> Option<&OsStr> { + self.path.extension() } /// It updates the workspace file with `new_content` diff --git a/crates/biome_cli/src/execute/std_in.rs b/crates/biome_cli/src/execute/std_in.rs index e29d45859bce..cf793abea81d 100644 --- a/crates/biome_cli/src/execute/std_in.rs +++ b/crates/biome_cli/src/execute/std_in.rs @@ -56,10 +56,10 @@ pub(crate) fn run<'a>( })?; let code = printed.into_code(); - let output = match biome_path.extension_as_str() { - Some("astro") => AstroFileHandler::output(content, code.as_str()), - Some("vue") => VueFileHandler::output(content, code.as_str()), - Some("svelte") => SvelteFileHandler::output(content, code.as_str()), + let output = match biome_path.extension().map(|ext| ext.as_encoded_bytes()) { + Some(b"astro") => AstroFileHandler::output(content, code.as_str()), + Some(b"vue") => VueFileHandler::output(content, code.as_str()), + Some(b"svelte") => SvelteFileHandler::output(content, code.as_str()), _ => code, }; console.append(markup! { @@ -127,10 +127,10 @@ pub(crate) fn run<'a>( .build(), })?; let code = fix_file_result.code; - let output = match biome_path.extension_as_str() { - Some("astro") => AstroFileHandler::output(&new_content, code.as_str()), - Some("vue") => VueFileHandler::output(&new_content, code.as_str()), - Some("svelte") => SvelteFileHandler::output(&new_content, code.as_str()), + let output = match biome_path.extension().map(|ext| ext.as_encoded_bytes()) { + Some(b"astro") => AstroFileHandler::output(&new_content, code.as_str()), + Some(b"vue") => VueFileHandler::output(&new_content, code.as_str()), + Some(b"svelte") => SvelteFileHandler::output(&new_content, code.as_str()), _ => code, }; if output != new_content { @@ -149,10 +149,10 @@ pub(crate) fn run<'a>( path: biome_path.clone(), })?; let code = result.code; - let output = match biome_path.extension_as_str() { - Some("astro") => AstroFileHandler::output(&new_content, code.as_str()), - Some("vue") => VueFileHandler::output(&new_content, code.as_str()), - Some("svelte") => SvelteFileHandler::output(&new_content, code.as_str()), + let output = match biome_path.extension().map(|ext| ext.as_encoded_bytes()) { + Some(b"astro") => AstroFileHandler::output(&new_content, code.as_str()), + Some(b"vue") => VueFileHandler::output(&new_content, code.as_str()), + Some(b"svelte") => SvelteFileHandler::output(&new_content, code.as_str()), _ => code, }; if output != new_content { @@ -172,10 +172,10 @@ pub(crate) fn run<'a>( path: biome_path.clone(), })?; let code = printed.into_code(); - let output = match biome_path.extension_as_str() { - Some("astro") => AstroFileHandler::output(&new_content, code.as_str()), - Some("vue") => VueFileHandler::output(&new_content, code.as_str()), - Some("svelte") => SvelteFileHandler::output(&new_content, code.as_str()), + let output = match biome_path.extension().map(|ext| ext.as_encoded_bytes()) { + Some(b"astro") => AstroFileHandler::output(&new_content, code.as_str()), + Some(b"vue") => VueFileHandler::output(&new_content, code.as_str()), + Some(b"svelte") => SvelteFileHandler::output(&new_content, code.as_str()), _ => code, }; if (mode.is_check_apply() || mode.is_check_apply_unsafe()) && output != new_content { diff --git a/crates/biome_fs/src/fs/os.rs b/crates/biome_fs/src/fs/os.rs index 088d5158304c..e8e581036b9c 100644 --- a/crates/biome_fs/src/fs/os.rs +++ b/crates/biome_fs/src/fs/os.rs @@ -8,7 +8,6 @@ use crate::{ use biome_diagnostics::{adapters::IoError, DiagnosticExt, Error, Severity}; use oxc_resolver::{Resolution, ResolveError, ResolveOptions, Resolver}; use rayon::{scope, Scope}; -use std::ffi::OsStr; use std::fs::{DirEntry, FileType}; use std::panic::AssertUnwindSafe; use std::process::Command; @@ -220,7 +219,7 @@ impl<'scope> TraversalScope<'scope> for OsTraversalScope<'scope> { // TODO: remove in Biome 2.0, and directly use `.gitignore` /// Default list of ignored directories, in the future will be supplanted by /// detecting and parsing .ignore files -const DEFAULT_IGNORE: &[&str; 5] = &[".git", ".svn", ".hg", ".yarn", "node_modules"]; +const DEFAULT_IGNORE: &[&[u8]] = &[b".git", b".svn", b".hg", b".yarn", b"node_modules"]; /// Traverse a single directory fn handle_dir<'scope>( @@ -230,8 +229,8 @@ fn handle_dir<'scope>( // The unresolved origin path in case the directory is behind a symbolic link origin_path: Option, ) { - if let Some(file_name) = path.file_name().and_then(OsStr::to_str) { - if DEFAULT_IGNORE.contains(&file_name) { + if let Some(file_name) = path.file_name() { + if DEFAULT_IGNORE.contains(&file_name.as_encoded_bytes()) { return; } } diff --git a/crates/biome_fs/src/path.rs b/crates/biome_fs/src/path.rs index eaeaf85756f8..75359fecf4e2 100644 --- a/crates/biome_fs/src/path.rs +++ b/crates/biome_fs/src/path.rs @@ -134,11 +134,6 @@ impl BiomePath { read_to_string(path) } - /// Returns the extension of the path - pub fn extension_as_str(&self) -> Option<&str> { - self.extension().and_then(OsStr::to_str) - } - /// The priority of the file. /// - `biome.json` and `biome.jsonc` have the highest priority /// - `package.json` and `tsconfig.json`/`jsconfig.json` have the second-highest priority, and they are considered as manifest files diff --git a/crates/biome_grit_patterns/src/grit_query.rs b/crates/biome_grit_patterns/src/grit_query.rs index 24059aaf9880..aba58ae58e87 100644 --- a/crates/biome_grit_patterns/src/grit_query.rs +++ b/crates/biome_grit_patterns/src/grit_query.rs @@ -142,7 +142,7 @@ impl GritQuery { let name = path .and_then(Path::file_stem) .map(OsStr::to_string_lossy) - .map(|stem| stem.to_string()); + .map(|stem| stem.into_owned()); let language = context.lang; let variable_locations = VariableLocations::new(vars_array); diff --git a/crates/biome_js_analyze/tests/spec_tests.rs b/crates/biome_js_analyze/tests/spec_tests.rs index 2795550e0c71..e9858cb0ce32 100644 --- a/crates/biome_js_analyze/tests/spec_tests.rs +++ b/crates/biome_js_analyze/tests/spec_tests.rs @@ -235,12 +235,12 @@ pub(crate) fn run_suppression_test(input: &'static str, _: &str, _: &str, _: &st let input_file = Path::new(input); let file_name = input_file.file_name().and_then(OsStr::to_str).unwrap(); - let source_type = match input_file.extension().and_then(OsStr::to_str).unwrap() { - "js" | "mjs" | "jsx" => JsFileSource::jsx(), - "cjs" => JsFileSource::js_script(), - "ts" => JsFileSource::ts(), - "mts" | "cts" => JsFileSource::ts_restricted(), - "tsx" => JsFileSource::tsx(), + let source_type = match input_file.extension().map(OsStr::as_encoded_bytes) { + Some(b"js" | b"mjs" | b"jsx") => JsFileSource::jsx(), + Some(b"cjs") => JsFileSource::js_script(), + Some(b"ts") => JsFileSource::ts(), + Some(b"mts" | b"cts") => JsFileSource::ts_restricted(), + Some(b"tsx") => JsFileSource::tsx(), _ => { panic!("Unknown file extension: {:?}", input_file.extension()); } diff --git a/crates/biome_lsp/src/handlers/analysis.rs b/crates/biome_lsp/src/handlers/analysis.rs index f856f59a79bf..e115b27479a5 100644 --- a/crates/biome_lsp/src/handlers/analysis.rs +++ b/crates/biome_lsp/src/handlers/analysis.rs @@ -16,6 +16,7 @@ use biome_service::workspace::{ use biome_service::WorkspaceError; use std::borrow::Cow; use std::collections::HashMap; +use std::ffi::OsStr; use std::ops::Sub; use tower_lsp::lsp_types::{ self as lsp, CodeActionKind, CodeActionOrCommand, CodeActionParams, CodeActionResponse, @@ -84,10 +85,10 @@ pub(crate) fn code_actions( let content = session.workspace.get_file_content(GetFileContentParams { path: biome_path.clone(), })?; - let offset = match biome_path.extension().and_then(|s| s.to_str()) { - Some("vue") => VueFileHandler::start(content.as_str()), - Some("astro") => AstroFileHandler::start(content.as_str()), - Some("svelte") => SvelteFileHandler::start(content.as_str()), + let offset = match biome_path.extension().map(OsStr::as_encoded_bytes) { + Some(b"vue") => VueFileHandler::start(content.as_str()), + Some(b"astro") => AstroFileHandler::start(content.as_str()), + Some(b"svelte") => SvelteFileHandler::start(content.as_str()), _ => None, }; let cursor_range = from_proto::text_range(&doc.line_index, params.range, position_encoding) diff --git a/crates/biome_lsp/src/handlers/formatting.rs b/crates/biome_lsp/src/handlers/formatting.rs index 53db2d63fc1e..138f7697a62c 100644 --- a/crates/biome_lsp/src/handlers/formatting.rs +++ b/crates/biome_lsp/src/handlers/formatting.rs @@ -10,6 +10,7 @@ use biome_service::workspace::{ GetFileContentParams, SupportsFeatureParams, }; use biome_service::{extension_error, WorkspaceError}; +use std::ffi::OsStr; use std::ops::{Add, Sub}; use tower_lsp::lsp_types::*; use tracing::debug; @@ -36,21 +37,20 @@ pub(crate) fn format( })?; let mut output = printed.into_code(); - let file_extension = biome_path.extension().and_then(|s| s.to_str()); let input = session.workspace.get_file_content(GetFileContentParams { path: biome_path.clone(), })?; if output.is_empty() { return Ok(None); } - match file_extension { - Some("astro") => { + match biome_path.extension().map(OsStr::as_encoded_bytes) { + Some(b"astro") => { output = AstroFileHandler::output(input.as_str(), output.as_str()); } - Some("vue") => { + Some(b"vue") => { output = VueFileHandler::output(input.as_str(), output.as_str()); } - Some("svelte") => { + Some(b"svelte") => { output = SvelteFileHandler::output(input.as_str(), output.as_str()); } _ => {} @@ -104,10 +104,10 @@ pub(crate) fn format_range( let content = session.workspace.get_file_content(GetFileContentParams { path: biome_path.clone(), })?; - let offset = match biome_path.extension().and_then(|s| s.to_str()) { - Some("vue") => VueFileHandler::start(content.as_str()), - Some("astro") => AstroFileHandler::start(content.as_str()), - Some("svelte") => SvelteFileHandler::start(content.as_str()), + let offset = match biome_path.extension().map(OsStr::as_encoded_bytes) { + Some(b"vue") => VueFileHandler::start(content.as_str()), + Some(b"astro") => AstroFileHandler::start(content.as_str()), + Some(b"svelte") => SvelteFileHandler::start(content.as_str()), _ => None, }; let format_range = if let Some(offset) = offset { diff --git a/crates/biome_lsp/src/session.rs b/crates/biome_lsp/src/session.rs index 9373daa325df..44ce8d84a0ef 100644 --- a/crates/biome_lsp/src/session.rs +++ b/crates/biome_lsp/src/session.rs @@ -25,6 +25,7 @@ use futures::stream::futures_unordered::FuturesUnordered; use futures::StreamExt; use rustc_hash::FxHashMap; use serde_json::Value; +use std::ffi::OsStr; use std::path::PathBuf; use std::sync::atomic::Ordering; use std::sync::atomic::{AtomicBool, AtomicU8}; @@ -339,10 +340,10 @@ impl Session { let content = self.workspace.get_file_content(GetFileContentParams { path: biome_path.clone(), })?; - let offset = match biome_path.extension().and_then(|s| s.to_str()) { - Some("vue") => VueFileHandler::start(content.as_str()), - Some("astro") => AstroFileHandler::start(content.as_str()), - Some("svelte") => SvelteFileHandler::start(content.as_str()), + let offset = match biome_path.extension().map(OsStr::as_encoded_bytes) { + Some(b"vue") => VueFileHandler::start(content.as_str()), + Some(b"astro") => AstroFileHandler::start(content.as_str()), + Some(b"svelte") => SvelteFileHandler::start(content.as_str()), _ => None, }; diff --git a/crates/biome_project/tests/manifest_spec_tests.rs b/crates/biome_project/tests/manifest_spec_tests.rs index fcdc881ae20b..468420cf7866 100644 --- a/crates/biome_project/tests/manifest_spec_tests.rs +++ b/crates/biome_project/tests/manifest_spec_tests.rs @@ -17,13 +17,12 @@ mod tsconfig { fn run_invalid_manifests(input: &'static str, _: &str, _: &str, _: &str) { let input_file = Path::new(input); let file_name = input_file.file_name().and_then(OsStr::to_str).unwrap(); - let extension = input_file.extension().and_then(OsStr::to_str).unwrap(); let input_code = read_to_string(input_file) .unwrap_or_else(|err| panic!("failed to read {input_file:?}: {err:?}")); let mut project = NodeJsProject::default(); - match extension { - "json" => { + match input_file.extension().map(OsStr::as_encoded_bytes) { + Some(b"json") => { let parsed = parse_json(input_code.as_str(), JsonParserOptions::default()); project.deserialize_manifest(&parsed.tree()); } @@ -72,13 +71,12 @@ fn run_invalid_manifests(input: &'static str, _: &str, _: &str, _: &str) { fn run_invalid_tsconfig(input: &'static str, _: &str, _: &str, _: &str) { let input_file = Path::new(input); let file_name = input_file.file_name().and_then(OsStr::to_str).unwrap(); - let extension = input_file.extension().and_then(OsStr::to_str).unwrap(); let input_code = read_to_string(input_file) .unwrap_or_else(|err| panic!("failed to read {input_file:?}: {err:?}")); let mut project = NodeJsProject::default(); - match extension { - "json" => { + match input_file.extension().map(OsStr::as_encoded_bytes) { + Some(b"json") => { let parsed = parse_json( input_code.as_str(), JsonParserOptions::default().with_allow_comments(), @@ -130,13 +128,12 @@ fn run_invalid_tsconfig(input: &'static str, _: &str, _: &str, _: &str) { fn run_valid_tsconfig(input: &'static str, _: &str, _: &str, _: &str) { let input_file = Path::new(input); let file_name = input_file.file_name().and_then(OsStr::to_str).unwrap(); - let extension = input_file.extension().and_then(OsStr::to_str).unwrap(); let input_code = read_to_string(input_file) .unwrap_or_else(|err| panic!("failed to read {input_file:?}: {err:?}")); let mut project = NodeJsProject::default(); - match extension { - "json" => { + match input_file.extension().map(OsStr::as_encoded_bytes) { + Some(b"json") => { let parsed = parse_json( input_code.as_str(), JsonParserOptions::default().with_allow_comments(), diff --git a/crates/biome_service/src/configuration.rs b/crates/biome_service/src/configuration.rs index dd61a7936348..2e47befb6013 100644 --- a/crates/biome_service/src/configuration.rs +++ b/crates/biome_service/src/configuration.rs @@ -187,11 +187,11 @@ fn load_config( // If the configuration path hint is from user and is a file path, // we'll load it directly - if let ConfigurationPathHint::FromUser(ref configuration_file_path) = base_path { - if file_system.path_is_file(configuration_file_path) { - let content = file_system.read_file_from_path(configuration_file_path)?; - let parser_options = match configuration_file_path.extension().and_then(OsStr::to_str) { - Some("json") => JsonParserOptions::default(), + if let ConfigurationPathHint::FromUser(ref config_file_path) = base_path { + if file_system.path_is_file(config_file_path) { + let content = file_system.read_file_from_path(config_file_path)?; + let parser_options = match config_file_path.extension().map(OsStr::as_encoded_bytes) { + Some(b"json") => JsonParserOptions::default(), _ => JsonParserOptions::default() .with_allow_comments() .with_allow_trailing_commas(), @@ -200,7 +200,7 @@ fn load_config( deserialize_from_json_str::(&content, parser_options, ""); return Ok(Some(ConfigurationPayload { deserialized, - configuration_file_path: PathBuf::from(configuration_file_path), + configuration_file_path: PathBuf::from(config_file_path), external_resolution_base_path, })); } @@ -243,8 +243,8 @@ fn load_config( } { let AutoSearchResult { content, file_path } = auto_search_result; - let parser_options = match file_path.extension().and_then(OsStr::to_str) { - Some("json") => JsonParserOptions::default(), + let parser_options = match file_path.extension().map(OsStr::as_encoded_bytes) { + Some(b"json") => JsonParserOptions::default(), _ => JsonParserOptions::default() .with_allow_comments() .with_allow_trailing_commas(), @@ -470,8 +470,8 @@ impl PartialConfigurationExt for PartialConfiguration { let extend_configuration_file_path = if extend_entry_as_path.starts_with(".") // TODO: Remove extension in Biome 2.0 || matches!( - extend_entry_as_path.extension().and_then(OsStr::to_str), - Some("json" | "jsonc") + extend_entry_as_path.extension().map(OsStr::as_encoded_bytes), + Some(b"json" | b"jsonc") ) { relative_resolution_base_path.join(extend_entry) } else { @@ -517,9 +517,9 @@ impl PartialConfigurationExt for PartialConfiguration { content.as_str(), match extend_configuration_file_path .extension() - .and_then(OsStr::to_str) + .map(OsStr::as_encoded_bytes) { - Some("json") => JsonParserOptions::default(), + Some(b"json") => JsonParserOptions::default(), _ => JsonParserOptions::default() .with_allow_comments() .with_allow_trailing_commas(), diff --git a/crates/biome_service/src/diagnostics.rs b/crates/biome_service/src/diagnostics.rs index 634b15c530ce..cc6f1c38e896 100644 --- a/crates/biome_service/src/diagnostics.rs +++ b/crates/biome_service/src/diagnostics.rs @@ -396,7 +396,7 @@ pub fn extension_error(path: &BiomePath) -> WorkspaceError { path.clone() .extension() .and_then(OsStr::to_str) - .map(|s| s.to_string()), + .map(str::to_string), ) } @@ -621,10 +621,7 @@ mod test { WorkspaceError::SourceFileNotSupported(SourceFileNotSupported { file_source: DocumentFileSource::Unknown, path: path.display().to_string(), - extension: path - .extension() - .and_then(OsStr::to_str) - .map(|s| s.to_string()), + extension: path.extension().and_then(OsStr::to_str).map(str::to_string), }) .with_file_path("not_supported.toml"), ) diff --git a/crates/biome_service/src/file_handlers/json.rs b/crates/biome_service/src/file_handlers/json.rs index dbd4e53cd278..6d98907cdb22 100644 --- a/crates/biome_service/src/file_handlers/json.rs +++ b/crates/biome_service/src/file_handlers/json.rs @@ -102,12 +102,14 @@ impl ServiceLanguage for JsonLanguage { .unwrap_or_default(); // ensure it never formats biome.json into a form it can't parse - let trailing_commas = - if matches!(path.file_name().and_then(OsStr::to_str), Some("biome.json")) { - TrailingCommas::None - } else { - language.and_then(|l| l.trailing_commas).unwrap_or_default() - }; + let trailing_commas = if matches!( + path.file_name().map(OsStr::as_encoded_bytes), + Some(b"biome.json") + ) { + TrailingCommas::None + } else { + language.and_then(|l| l.trailing_commas).unwrap_or_default() + }; let options = JsonFormatOptions::new() .with_line_ending(line_ending) diff --git a/crates/biome_service/src/workspace.rs b/crates/biome_service/src/workspace.rs index 1a5ed2c4c8a8..5416b5faad7f 100644 --- a/crates/biome_service/src/workspace.rs +++ b/crates/biome_service/src/workspace.rs @@ -66,12 +66,12 @@ use biome_formatter::Printed; use biome_fs::BiomePath; use biome_js_syntax::{TextRange, TextSize}; use biome_text_edit::TextEdit; +use core::str; use enumflags2::{bitflags, BitFlags}; #[cfg(feature = "schema")] use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema}; use slotmap::{new_key_type, DenseSlotMap}; use std::collections::HashMap; -use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::{borrow::Cow, panic::RefUnwindSafe, sync::Arc}; use tracing::debug; @@ -101,22 +101,21 @@ pub struct FileFeaturesResult { impl FileFeaturesResult { /// Sorted array of files that should not be processed no matter the cases. /// These files are handled by other tools. - const PROTECTED_FILES: &'static [&'static str; 4] = &[ + const PROTECTED_FILES: &'static [&'static [u8]] = &[ // Composer - "composer.lock", + b"composer.lock", // NPM - "npm-shrinkwrap.json", - "package-lock.json", + b"npm-shrinkwrap.json", + b"package-lock.json", // Yarn - "yarn.lock", + b"yarn.lock", ]; /// Checks whether this file is protected. /// A protected file is handled by a specific tool and should be ignored. pub(crate) fn is_protected_file(path: &Path) -> bool { path.file_name() - .and_then(OsStr::to_str) - .is_some_and(|file_name| FileFeaturesResult::PROTECTED_FILES.contains(&file_name)) + .is_some_and(|filename| Self::PROTECTED_FILES.contains(&filename.as_encoded_bytes())) } /// By default, all features are not supported by a file. @@ -1106,7 +1105,12 @@ impl<'app, W: Workspace + ?Sized> Drop for FileGuard<'app, W> { #[test] fn test_order() { for items in FileFeaturesResult::PROTECTED_FILES.windows(2) { - assert!(items[0] < items[1], "{} < {}", items[0], items[1]); + assert!( + items[0] < items[1], + "{} < {}", + str::from_utf8(items[0]).unwrap(), + str::from_utf8(items[1]).unwrap() + ); } } diff --git a/crates/biome_service/src/workspace/server.rs b/crates/biome_service/src/workspace/server.rs index 21ddc1a51a03..fa409085f581 100644 --- a/crates/biome_service/src/workspace/server.rs +++ b/crates/biome_service/src/workspace/server.rs @@ -132,9 +132,8 @@ impl WorkspaceServer { let language = DocumentFileSource::from_path(path).or(file_source); WorkspaceError::source_file_not_supported( language, - path.clone().display().to_string(), - path.clone() - .extension() + path.display().to_string(), + path.extension() .and_then(OsStr::to_str) .map(|s| s.to_string()), ) diff --git a/crates/biome_service/tests/spec_tests.rs b/crates/biome_service/tests/spec_tests.rs index ce1176709932..9848a3091031 100644 --- a/crates/biome_service/tests/spec_tests.rs +++ b/crates/biome_service/tests/spec_tests.rs @@ -12,17 +12,16 @@ tests_macros::gen_tests! {"tests/valid/**/*.{json,jsonc}", crate::run_valid_conf fn run_invalid_configurations(input: &'static str, _: &str, _: &str, _: &str) { let input_file = Path::new(input); let file_name = input_file.file_name().and_then(OsStr::to_str).unwrap(); - let extension = input_file.extension().and_then(OsStr::to_str).unwrap(); let input_code = read_to_string(input_file) .unwrap_or_else(|err| panic!("failed to read {input_file:?}: {err:?}")); - let result = match extension { - "json" => deserialize_from_json_str::( + let result = match input_file.extension().map(OsStr::as_encoded_bytes) { + Some(b"json") => deserialize_from_json_str::( input_code.as_str(), JsonParserOptions::default(), "", ), - "jsonc" => deserialize_from_json_str::( + Some(b"jsonc") => deserialize_from_json_str::( input_code.as_str(), JsonParserOptions::default() .with_allow_comments() @@ -63,17 +62,16 @@ fn run_invalid_configurations(input: &'static str, _: &str, _: &str, _: &str) { fn run_valid_configurations(input: &'static str, _: &str, _: &str, _: &str) { let input_file = Path::new(input); let file_name = input_file.file_name().and_then(OsStr::to_str).unwrap(); - let extension = input_file.extension().and_then(OsStr::to_str).unwrap(); let input_code = read_to_string(input_file) .unwrap_or_else(|err| panic!("failed to read {input_file:?}: {err:?}")); - let result = match extension { - "json" => deserialize_from_json_str::( + let result = match input_file.extension().map(OsStr::as_encoded_bytes) { + Some(b"json") => deserialize_from_json_str::( input_code.as_str(), JsonParserOptions::default(), "", ), - "jsonc" => deserialize_from_json_str::( + Some(b"jsonc") => deserialize_from_json_str::( input_code.as_str(), JsonParserOptions::default() .with_allow_comments() diff --git a/crates/tests_macros/src/lib.rs b/crates/tests_macros/src/lib.rs index c0052ddfba94..8b83af543e79 100644 --- a/crates/tests_macros/src/lib.rs +++ b/crates/tests_macros/src/lib.rs @@ -198,8 +198,8 @@ impl Arguments { .into_iter() .flat_map(|path| path.components()) .map(Component::as_os_str) - .filter_map(OsStr::to_str) - .skip_while(|item| *item != "specs"); + .skip_while(|item| item.as_encoded_bytes() != b"specs") + .filter_map(OsStr::to_str); let span = self.pattern.lit.span(); let test_name = syn::Ident::new(&test_name, span);