diff --git a/src/config/mod.rs b/src/config/mod.rs index a3f9842cd4f..780402d21fe 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -354,50 +354,7 @@ impl Config { style_edition: Option, version: Option, ) -> Result<(Config, Option), Error> { - /// Try to find a project file in the given directory and its parents. - /// Returns the path of the nearest project file if one exists, - /// or `None` if no project file was found. - fn resolve_project_file(dir: &Path) -> Result, Error> { - let mut current = if dir.is_relative() { - env::current_dir()?.join(dir) - } else { - dir.to_path_buf() - }; - - current = fs::canonicalize(current)?; - - loop { - match get_toml_path(¤t) { - Ok(Some(path)) => return Ok(Some(path)), - Err(e) => return Err(e), - _ => (), - } - - // If the current directory has no parent, we're done searching. - if !current.pop() { - break; - } - } - - // If nothing was found, check in the home directory. - if let Some(home_dir) = dirs::home_dir() { - if let Some(path) = get_toml_path(&home_dir)? { - return Ok(Some(path)); - } - } - - // If none was found there either, check in the user's configuration directory. - if let Some(mut config_dir) = dirs::config_dir() { - config_dir.push("rustfmt"); - if let Some(path) = get_toml_path(&config_dir)? { - return Ok(Some(path)); - } - } - - Ok(None) - } - - match resolve_project_file(dir)? { + match resolve_project_file(dir).or_else(|_| config_from_user_dirs())? { None => Ok(( Config::default_for_possible_style_edition(style_edition, edition, version), None, @@ -455,6 +412,53 @@ impl Config { } } +/// Looks for a configuration file in the given directory and its parents. +/// +/// Returns the path of the configuration file nearest to that directory if one exists, or `None` if +/// none was found. +fn resolve_project_file(dir: &Path) -> Result, Error> { + let mut current = if dir.is_relative() { + env::current_dir()?.join(dir) + } else { + dir.to_path_buf() + }; + + current = fs::canonicalize(current)?; + + loop { + match get_toml_path(¤t) { + Ok(Some(path)) => return Ok(Some(path)), + Err(e) => return Err(e), + _ => (), + } + + // If the current directory has no parent, we're done searching. + if !current.pop() { + break Ok(None); + } + } +} + +/// Looks for a configuration file in the user's home directory and configuration directory. +/// +/// Returns the path of the first configuration file found in that order, or `None` if none was +/// found. +fn config_from_user_dirs() -> Result, Error> { + for dir in [ + dirs::home_dir(), + dirs::config_dir().map(|d| d.join("rustfmt")), + ] + .iter() + .flatten() + { + if let Some(path) = get_toml_path(&dir)? { + return Ok(Some(path)); + } + } + + Ok(None) +} + /// Loads a config by checking the client-supplied options and if appropriate, the /// file system (including searching the file system for overrides). pub fn load_config( @@ -534,14 +538,7 @@ fn config_path(options: &dyn CliOptions) -> Result, Error> { // If a config file cannot be found from the given path, return error. match options.config_path() { Some(path) if !path.exists() => config_path_not_found(path.to_str().unwrap()), - Some(path) if path.is_dir() => { - let config_file_path = get_toml_path(path)?; - if config_file_path.is_some() { - Ok(config_file_path) - } else { - config_path_not_found(path.to_str().unwrap()) - } - } + Some(path) if path.is_dir() => resolve_project_file(path), Some(path) => Ok(Some( // Canonicalize only after checking above that the `path.exists()`. path.canonicalize()?, diff --git a/tests/config/issue_4660/Cargo.lock b/tests/config/issue_4660/Cargo.lock new file mode 100644 index 00000000000..e2e277aef87 --- /dev/null +++ b/tests/config/issue_4660/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "inner_lib" +version = "0.1.0" diff --git a/tests/config/issue_4660/Cargo.toml b/tests/config/issue_4660/Cargo.toml new file mode 100644 index 00000000000..c716e9d0575 --- /dev/null +++ b/tests/config/issue_4660/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["inner_lib"] diff --git a/tests/config/issue_4660/inner_bin/.rustfmt.unstable.toml b/tests/config/issue_4660/inner_bin/.rustfmt.unstable.toml new file mode 100644 index 00000000000..c7ad93bafe3 --- /dev/null +++ b/tests/config/issue_4660/inner_bin/.rustfmt.unstable.toml @@ -0,0 +1 @@ +disable_all_formatting = true diff --git a/tests/config/issue_4660/inner_bin/Cargo.toml b/tests/config/issue_4660/inner_bin/Cargo.toml new file mode 100644 index 00000000000..bb6a80cd0ce --- /dev/null +++ b/tests/config/issue_4660/inner_bin/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "inner_bin" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/tests/config/issue_4660/inner_bin/src/main.rs b/tests/config/issue_4660/inner_bin/src/main.rs new file mode 100644 index 00000000000..21c34773b8a --- /dev/null +++ b/tests/config/issue_4660/inner_bin/src/main.rs @@ -0,0 +1 @@ +fn main(){println!("Hello, world!")} diff --git a/tests/config/issue_4660/inner_lib/Cargo.toml b/tests/config/issue_4660/inner_lib/Cargo.toml new file mode 100644 index 00000000000..c9ddf1793b8 --- /dev/null +++ b/tests/config/issue_4660/inner_lib/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "inner_lib" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/tests/config/issue_4660/inner_lib/rustfmt.toml b/tests/config/issue_4660/inner_lib/rustfmt.toml new file mode 100644 index 00000000000..c7ad93bafe3 --- /dev/null +++ b/tests/config/issue_4660/inner_lib/rustfmt.toml @@ -0,0 +1 @@ +disable_all_formatting = true diff --git a/tests/config/issue_4660/inner_lib/src/lib.rs b/tests/config/issue_4660/inner_lib/src/lib.rs new file mode 100644 index 00000000000..42375c99d2c --- /dev/null +++ b/tests/config/issue_4660/inner_lib/src/lib.rs @@ -0,0 +1 @@ +pub fn add(left:u64,right:u64)->u64{left+right} diff --git a/tests/rustfmt/main.rs b/tests/rustfmt/main.rs index ee972d8a6af..e04fae7ad04 100644 --- a/tests/rustfmt/main.rs +++ b/tests/rustfmt/main.rs @@ -345,6 +345,37 @@ fn rustfmt_error_improvement_regarding_invalid_toml() { assert!(stderr.contains(&expected_error_message)); } +#[test] +fn config_path_walks_parent_directories_with_dir_name() { + let src_dir = "tests/config/issue_4660/inner_lib/src"; + let src_file = src_dir.to_owned() + "/lib.rs"; + let args = ["--config-path", src_dir, "--check", &src_file]; + let (stdout, stderr) = rustfmt(&args); + + assert_eq!(stderr, ""); + // Due to `disable_all_formatting = true` in `tests/config/issue_4660/inner_lib/rustfmt.toml`, + // the source file should not be modified. + assert_eq!(stdout, ""); +} + +#[test] +fn config_path_does_not_walk_parent_directories_with_toml_name() { + let src_dir = "tests/config/issue_4660/inner_bin/src"; + let toml_file = src_dir.to_owned() + "/.rustfmt.unstable.toml"; + let src_file = src_dir.to_owned() + "/main.rs"; + let args = ["--config-path", &toml_file, "--check", &src_file]; + let (stdout, stderr) = rustfmt(&args); + + assert_eq!( + stderr, + format!( + "Error: unable to find a config file for the given path: `{}`\n", + Path::new(&toml_file).display(), + ) + ); + assert_eq!(stdout, ""); +} + #[test] fn rustfmt_allow_not_a_dir_errors() { // See also https://github.com/rust-lang/rustfmt/pull/6624