Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 94 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ unicode-properties = { version = "0.1", default-features = false, features = ["g
rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" }
semver = "1.0.21"

[dev-dependencies]
tempfile = "3.23.0"

# Rustc dependencies are loaded from the sysroot, Cargo doesn't know about them.

[package.metadata.rust-analyzer]
Expand Down
8 changes: 5 additions & 3 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,10 +499,12 @@ fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
// Only return if it's a file to handle the unlikely situation of a directory named
// `rustfmt.toml`.
Ok(ref md) if md.is_file() => return Ok(Some(config_file.canonicalize()?)),
// Return the error if it's something other than `NotFound`; otherwise we didn't
// find the project file yet, and continue searching.
// We didn't find the project file yet, and continue searching if:
// `NotFound` => file not found
// `NotADirectory` => rare case where expected directory is a file
// Otherwise, return the error
Err(e) => {
if e.kind() != ErrorKind::NotFound {
if !matches!(e.kind(), ErrorKind::NotFound | ErrorKind::NotADirectory) {
let ctx = format!("Failed to get metadata for config file {:?}", &config_file);
let err = anyhow::Error::new(e).context(ctx);
return Err(Error::new(ErrorKind::Other, err));
Expand Down
57 changes: 51 additions & 6 deletions tests/rustfmt/main.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
//! Integration tests for rustfmt.

use std::env;
use std::fs::remove_file;
use std::fs::{File, remove_file};
use std::path::Path;
use std::process::Command;

use rustfmt_config_proc_macro::{nightly_only_test, rustfmt_only_ci_test};

/// Run the rustfmt executable and return its output.
fn rustfmt(args: &[&str]) -> (String, String) {
/// Run the rustfmt executable with environment vars set and return its output.
fn rustfmt_with_extra(
args: &[&str],
working_dir: Option<&str>,
envs: &[(&str, &str)],
) -> (String, String) {
let mut bin_dir = env::current_exe().unwrap();
bin_dir.pop(); // chop off test exe name
if bin_dir.ends_with("deps") {
bin_dir.pop();
}
let cmd = bin_dir.join(format!("rustfmt{}", env::consts::EXE_SUFFIX));
let rustfmt_exe = bin_dir.join(format!("rustfmt{}", env::consts::EXE_SUFFIX));

// Ensure the rustfmt binary runs from the local target dir.
let path = env::var_os("PATH").unwrap_or_default();
let mut paths = env::split_paths(&path).collect::<Vec<_>>();
paths.insert(0, bin_dir);
let new_path = env::join_paths(paths).unwrap();

match Command::new(&cmd).args(args).env("PATH", new_path).output() {
let mut cmd = Command::new(rustfmt_exe);
cmd.args(args)
.env("PATH", new_path)
.envs(envs.iter().copied());
if let Some(working_dir) = working_dir {
cmd.current_dir(working_dir);
}
match cmd.output() {
Ok(output) => (
String::from_utf8(output.stdout).expect("utf-8"),
String::from_utf8(output.stderr).expect("utf-8"),
Expand All @@ -31,6 +41,10 @@ fn rustfmt(args: &[&str]) -> (String, String) {
}
}

fn rustfmt(args: &[&str]) -> (String, String) {
rustfmt_with_extra(args, None, &[])
}

macro_rules! assert_that {
($args:expr, $($check:ident $check_args:tt)&&+) => {
let (stdout, stderr) = rustfmt($args);
Expand Down Expand Up @@ -232,3 +246,34 @@ fn rustfmt_error_improvement_regarding_invalid_toml() {

assert!(stderr.contains(&expected_error_message));
}

#[test]
fn rustfmt_allow_not_a_dir_errors() {
// See also https://github.com/rust-lang/rustfmt/pull/6624

// To get a proper test, we need to make sure that neither the working dir
// nor the input file have a "rustfmt.toml" file in any ancestor dirs. Since
// this project has a "rustfmt.toml" in the root dir, we can't use a temp
// dir in the target/ dir, which includes the directory given by
// CARGO_TARGET_TMPDIR. Thus, we need the OS-specific temp dir which is
// closer to the "root" directory which is less likely to have a
// "rustfmt.toml".
let fake_home = tempfile::tempdir().unwrap();
let fake_home_str = fake_home.path().to_str().unwrap();

// create .config file
let dot_config_file = fake_home.path().join(".config");
let _ = File::create(dot_config_file).unwrap();

// create empty.rs
let empty_rs = fake_home.path().join("empty.rs");
let _ = File::create(&empty_rs).unwrap();

let args = [empty_rs.to_str().unwrap()];
let envs = &[("HOME", fake_home_str)];
let (stdout, stderr) = rustfmt_with_extra(&args, Some(fake_home_str), envs);

// Should pass without any errors
assert_eq!(stdout, "");
assert_eq!(stderr, "");
}
Loading