diff --git a/Cargo.lock b/Cargo.lock index 06bead7f25d..729ad141e02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -894,6 +894,7 @@ dependencies = [ "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 26427ed9a5a..69d9c7050e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,9 @@ structopt = "0.3" rustfmt-config_proc_macro = { version = "0.2", path = "config_proc_macro" } +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["errhandlingapi", "fileapi"] } + # A noop dependency that changes in the Rust repository, it's a bit of a hack. # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` # for more information. diff --git a/src/bin/main.rs b/src/bin/main.rs index 7290c2f30d5..cb3dd59d00a 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -14,7 +14,7 @@ use std::str::FromStr; use getopts::{Matches, Options}; use crate::rustfmt::{ - load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, + absolute_path, load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, FormatReportFormatterBuilder, Input, Session, Verbosity, }; @@ -453,9 +453,9 @@ fn determine_operation(matches: &Matches) -> Result { let files: Vec<_> = free_matches .map(|s| { let p = PathBuf::from(s); - // we will do comparison later, so here tries to canonicalize first - // to get the expected behavior. - p.canonicalize().unwrap_or(p) + // we will do comparison later, so here tries to get the absolute + // path first to get the expected behavior. + absolute_path(&p).unwrap_or(p) }) .collect(); diff --git a/src/cargo-fmt/main.rs b/src/cargo-fmt/main.rs index c06df111566..6a45c50bf3a 100644 --- a/src/cargo-fmt/main.rs +++ b/src/cargo-fmt/main.rs @@ -3,11 +3,11 @@ #![deny(warnings)] use cargo_metadata; +use rustfmt_nightly as rustfmt; use std::cmp::Ordering; use std::collections::{BTreeMap, BTreeSet}; use std::env; -use std::fs; use std::hash::{Hash, Hasher}; use std::io::{self, Write}; use std::iter::FromIterator; @@ -58,6 +58,8 @@ pub struct Opts { format_all: bool, } +use crate::rustfmt::absolute_path; + fn main() { let exit_status = execute(); std::io::stdout().flush().unwrap(); @@ -250,10 +252,10 @@ pub struct Target { impl Target { pub fn from_target(target: &cargo_metadata::Target) -> Self { let path = PathBuf::from(&target.src_path); - let canonicalized = fs::canonicalize(&path).unwrap_or(path); + let path = absolute_path(&path).unwrap_or(path); Target { - path: canonicalized, + path, kind: target.kind[0].clone(), edition: target.edition.clone(), } @@ -338,14 +340,14 @@ fn get_targets_root_only( targets: &mut BTreeSet, ) -> Result<(), io::Error> { let metadata = get_cargo_metadata(manifest_path, false)?; - let workspace_root_path = PathBuf::from(&metadata.workspace_root).canonicalize()?; + let workspace_root_path = absolute_path(PathBuf::from(&metadata.workspace_root))?; let (in_workspace_root, current_dir_manifest) = if let Some(target_manifest) = manifest_path { ( workspace_root_path == target_manifest, target_manifest.canonicalize()?, ) } else { - let current_dir = env::current_dir()?.canonicalize()?; + let current_dir = absolute_path(env::current_dir()?)?; ( workspace_root_path == current_dir, current_dir.join("Cargo.toml"), diff --git a/src/config/file_lines.rs b/src/config/file_lines.rs index f0dc6c66597..cc538278928 100644 --- a/src/config/file_lines.rs +++ b/src/config/file_lines.rs @@ -8,9 +8,10 @@ use std::{cmp, fmt, iter, str}; use serde::{ser, Deserialize, Deserializer, Serialize, Serializer}; use serde_json as json; - use syntax::source_map::{self, SourceFile}; +use crate::utils::absolute_path; + /// A range of lines in a file, inclusive of both ends. pub struct LineRange { pub file: Rc, @@ -242,7 +243,7 @@ impl FileLines { Some(ref map) => map, }; - match canonicalize_path_string(file_name).and_then(|file| map.get(&file)) { + match absolute_path_string(file_name).and_then(|file| map.get(&file)) { Some(ranges) => ranges.iter().any(f), None => false, } @@ -281,9 +282,9 @@ impl<'a> iter::Iterator for Files<'a> { } } -fn canonicalize_path_string(file: &FileName) -> Option { +fn absolute_path_string(file: &FileName) -> Option { match *file { - FileName::Real(ref path) => path.canonicalize().ok().map(FileName::Real), + FileName::Real(ref path) => absolute_path(path).ok().map(FileName::Real), _ => Some(file.clone()), } } @@ -313,8 +314,8 @@ pub struct JsonSpan { impl JsonSpan { fn into_tuple(self) -> Result<(FileName, Range), String> { let (lo, hi) = self.range; - let canonical = canonicalize_path_string(&self.file) - .ok_or_else(|| format!("Can't canonicalize {}", &self.file))?; + let canonical = absolute_path_string(&self.file) + .ok_or_else(|| format!("Can't get absolute path {}", &self.file))?; Ok((canonical, Range::new(lo, hi))) } } diff --git a/src/lib.rs b/src/lib.rs index 31df6b1c72c..026226c1a81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,6 +33,7 @@ pub use crate::config::{ load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, Range, Verbosity, }; +pub use crate::utils::absolute_path; pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder}; diff --git a/src/utils.rs b/src/utils.rs index bad9bc1d788..99a1498ccdd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,6 @@ use std::borrow::Cow; +use std::io; +use std::path; use rustc_target::spec::abi; use syntax::ast::{ @@ -653,6 +655,47 @@ pub(crate) fn unicode_str_width(s: &str) -> usize { s.width() } +#[cfg(windows)] +pub fn absolute_path>(p: P) -> io::Result { + use std::ffi::OsString; + use std::iter::once; + use std::os::windows::ffi::{OsStrExt, OsStringExt}; + use std::ptr::null_mut; + use winapi::um::errhandlingapi::GetLastError; + use winapi::um::fileapi::GetFullPathNameW; + + // FIXME: This `MAX_PATH` may be valid only from Windows 10, version 1607. + // https://docs.microsoft.com/ja-jp/windows/desktop/FileIO/naming-a-file#paths + const MAX_PATH: usize = 32767; + let wide: Vec = p + .as_ref() + .as_os_str() + .encode_wide() + .chain(once(0)) + .collect(); + let mut buffer: Vec = vec![0; MAX_PATH]; + unsafe { + let result = GetFullPathNameW( + wide.as_ptr(), + MAX_PATH as u32, + buffer.as_mut_ptr(), + null_mut(), + ); + if result == 0 { + Err(std::io::Error::from_raw_os_error(GetLastError() as i32)) + } else { + Ok(path::PathBuf::from(OsString::from_wide( + &buffer[..result as usize], + ))) + } + } +} + +#[cfg(not(windows))] +pub fn absolute_path>(p: P) -> io::Result { + std::fs::canonicalize(p) +} + #[cfg(test)] mod test { use super::*; diff --git a/tests/source/mods-relative-paths/lib.rs b/tests/source/mods-relative-paths/lib.rs new file mode 100644 index 00000000000..c8648249177 --- /dev/null +++ b/tests/source/mods-relative-paths/lib.rs @@ -0,0 +1,2 @@ +#[path = "../mods-relative-paths/mod_b.rs"] +mod b; diff --git a/tests/source/mods-relative-paths/mod_b.rs b/tests/source/mods-relative-paths/mod_b.rs new file mode 100644 index 00000000000..85f9f3f0a4f --- /dev/null +++ b/tests/source/mods-relative-paths/mod_b.rs @@ -0,0 +1,7 @@ +fn + + + +foo() { + println!("toto") +} diff --git a/tests/target/mods-relative-paths/lib.rs b/tests/target/mods-relative-paths/lib.rs new file mode 100644 index 00000000000..c8648249177 --- /dev/null +++ b/tests/target/mods-relative-paths/lib.rs @@ -0,0 +1,2 @@ +#[path = "../mods-relative-paths/mod_b.rs"] +mod b; diff --git a/tests/target/mods-relative-paths/mod_b.rs b/tests/target/mods-relative-paths/mod_b.rs new file mode 100644 index 00000000000..3b0991e5071 --- /dev/null +++ b/tests/target/mods-relative-paths/mod_b.rs @@ -0,0 +1,3 @@ +fn foo() { + println!("toto") +}