diff --git a/Cargo.lock b/Cargo.lock index da7026bb138..be6c0364ac8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,6 +124,15 @@ name = "constant_time_eq" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "crossbeam-channel" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-deque" version = "0.2.0" @@ -155,6 +164,15 @@ dependencies = [ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-utils" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "derive-new" version = "0.5.6" @@ -233,6 +251,11 @@ dependencies = [ "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -246,6 +269,18 @@ dependencies = [ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "globset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "humantime" version = "1.2.0" @@ -254,6 +289,23 @@ dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ignore" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itertools" version = "0.8.0" @@ -701,6 +753,7 @@ dependencies = [ "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "ignore 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -724,6 +777,14 @@ name = "ryu" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "same-file" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scoped-tls" version = "1.0.0" @@ -882,6 +943,16 @@ name = "utf8-ranges" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "walkdir" +version = "2.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.3.6" @@ -935,9 +1006,11 @@ dependencies = [ "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" +"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" +"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" "checksum derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6ca414e896ae072546f4d789f452daaecf60ddee4c9df5dc6d5936d769e3d87c" "checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" "checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" @@ -947,9 +1020,12 @@ dependencies = [ "checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797" +"checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" +"checksum ignore 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ad03ca67dc12474ecd91fdb94d758cbd20cb4e7a78ebe831df26a9b7511e1162" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum jobserver 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "dd80e58f77e0cdea53ba96acc5e04479e5ffc5d869626a6beafe50fed867eace" @@ -1000,6 +1076,7 @@ dependencies = [ "checksum rustc-workspace-hack 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" +"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" @@ -1023,6 +1100,7 @@ dependencies = [ "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" +"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" diff --git a/Cargo.toml b/Cargo.toml index 2e6cc9e8a06..d84ca1d7292 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ bytecount = "0.5" unicode-width = "0.1.5" unicode_categories = "0.1.1" dirs = "1.0.4" +ignore = "0.4.6" # 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` diff --git a/src/config/config_type.rs b/src/config/config_type.rs index 7c4298dcf1c..40da2986808 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -141,7 +141,7 @@ macro_rules! create_config { ConfigWasSet(self) } - fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config { + fn fill_from_parsed_config(mut self, parsed: PartialConfig) -> Config { $( if let Some(val) = parsed.$i { if self.$i.3 { @@ -160,7 +160,6 @@ macro_rules! create_config { )+ self.set_heuristics(); self.set_license_template(); - self.set_ignore(dir); self } @@ -287,9 +286,6 @@ macro_rules! create_config { } } - fn set_ignore(&mut self, dir: &Path) { - self.ignore.2.add_prefix(dir); - } /// Returns `true` if the config key was explicitly set and is the default value. pub fn is_default(&self, key: &str) -> bool { diff --git a/src/config/mod.rs b/src/config/mod.rs index 6be3dd2b31c..839e9bb6e21 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -190,8 +190,7 @@ impl Config { let mut file = File::open(&file_path)?; let mut toml = String::new(); file.read_to_string(&mut toml)?; - Config::from_toml(&toml, file_path.parent().unwrap()) - .map_err(|err| Error::new(ErrorKind::InvalidData, err)) + Config::from_toml(&toml).map_err(|err| Error::new(ErrorKind::InvalidData, err)) } /// Resolves the config for input in `dir`. @@ -253,7 +252,7 @@ impl Config { } } - pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result { + pub(crate) fn from_toml(toml: &str) -> Result { let parsed: ::toml::Value = toml .parse() .map_err(|e| format!("Could not parse TOML: {}", e))?; @@ -272,7 +271,7 @@ impl Config { if !err.is_empty() { eprint!("{}", err); } - Ok(Config::default().fill_from_parsed_config(parsed_config, dir)) + Ok(Config::default().fill_from_parsed_config(parsed_config)) } Err(e) => { err.push_str("Error: Decoding config file failed:\n"); @@ -426,8 +425,7 @@ mod test { #[test] fn test_was_set() { - use std::path::Path; - let config = Config::from_toml("hard_tabs = true", Path::new("")).unwrap(); + let config = Config::from_toml("hard_tabs = true").unwrap(); assert_eq!(config.was_set().hard_tabs(), true); assert_eq!(config.was_set().verbose(), false); diff --git a/src/config/options.rs b/src/config/options.rs index 02c3c87368e..bd8c5056854 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -1,11 +1,11 @@ -use std::collections::HashSet; +use std::collections::{hash_set, HashSet}; use std::path::{Path, PathBuf}; use atty; use crate::config::config_type::ConfigType; use crate::config::lists::*; -use crate::config::{Config, FileName}; +use crate::config::Config; /// Macro that will stringify the enum variants or a provided textual repr #[macro_export] @@ -399,6 +399,15 @@ impl Default for EmitMode { #[derive(Default, Deserialize, Serialize, Clone, Debug, PartialEq)] pub struct IgnoreList(HashSet); +impl<'a> IntoIterator for &'a IgnoreList { + type Item = &'a PathBuf; + type IntoIter = hash_set::Iter<'a, PathBuf>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + impl IgnoreList { pub fn add_prefix(&mut self, dir: &Path) { self.0 = self @@ -415,18 +424,6 @@ impl IgnoreList { }) .collect(); } - - fn skip_file_inner(&self, file: &Path) -> bool { - self.0.iter().any(|path| file.starts_with(path)) - } - - pub fn skip_file(&self, file: &FileName) -> bool { - if let FileName::Real(ref path) = file { - self.skip_file_inner(path) - } else { - false - } - } } impl ::std::str::FromStr for IgnoreList { diff --git a/src/formatting.rs b/src/formatting.rs index ad80e7e1aef..391a3c0e384 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -14,6 +14,7 @@ use syntax::source_map::{FilePathMapping, SourceMap, Span, DUMMY_SP}; use crate::comment::{CharClasses, FullCodeCharKind}; use crate::config::{Config, FileName, Verbosity}; +use crate::ignore_path::IgnorePathSet; use crate::issues::BadIssueSeeker; use crate::utils::{count_newlines, get_skip_macro_names}; use crate::visitor::{FmtVisitor, SnippetProvider}; @@ -90,6 +91,10 @@ fn format_project( parse_session.span_diagnostic = Handler::with_emitter(true, None, silent_emitter); let mut context = FormatContext::new(&krate, report, parse_session, config, handler); + let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) { + Ok(set) => set, + Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)), + }; let files = modules::ModResolver::new( context.parse_session.source_map(), @@ -99,7 +104,8 @@ fn format_project( .visit_crate(&krate) .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; for (path, (module, _)) in files { - if (config.skip_children() && path != main_file) || config.ignore().skip_file(&path) { + let should_ignore = !input_is_stdin && ignore_path_set.is_match(&path); + if (config.skip_children() && path != main_file) || should_ignore { continue; } should_emit_verbose(input_is_stdin, config, || println!("Formatting {}", path)); @@ -276,7 +282,10 @@ impl FormattingError { | ErrorKind::IoError(_) | ErrorKind::ParseError | ErrorKind::LostComment => "internal error:", - ErrorKind::LicenseCheck | ErrorKind::BadAttr | ErrorKind::VersionMismatch => "error:", + ErrorKind::LicenseCheck + | ErrorKind::BadAttr + | ErrorKind::InvalidGlobPattern(..) + | ErrorKind::VersionMismatch => "error:", ErrorKind::BadIssue(_) | ErrorKind::DeprecatedAttr => "warning:", } } diff --git a/src/ignore_path.rs b/src/ignore_path.rs new file mode 100644 index 00000000000..1e17337e625 --- /dev/null +++ b/src/ignore_path.rs @@ -0,0 +1,55 @@ +use ignore::{self, gitignore}; +use std::path::PathBuf; + +use crate::config::{FileName, IgnoreList}; + +pub struct IgnorePathSet { + ignore_set: gitignore::Gitignore, +} + +impl IgnorePathSet { + pub fn from_ignore_list(ignore_list: &IgnoreList) -> Result { + let mut ignore_builder = gitignore::GitignoreBuilder::new(PathBuf::from("")); + + for ignore_path in ignore_list { + ignore_builder.add_line(None, ignore_path.to_str().unwrap())?; + } + + Ok(IgnorePathSet { + ignore_set: ignore_builder.build()?, + }) + } + + pub fn is_match(&self, file_name: &FileName) -> bool { + match file_name { + FileName::Stdin => false, + FileName::Real(p) => self + .ignore_set + .matched_path_or_any_parents(p, false) + .is_ignore(), + } + } +} + +#[cfg(test)] +mod test { + use crate::config::{Config, FileName}; + use crate::ignore_path::IgnorePathSet; + use std::path::PathBuf; + + #[test] + fn test_ignore_path_set() { + let config = Config::from_toml( + "ignore = [ + \"foo.rs\", + \"bar_dir/*\", + ]", + ) + .unwrap(); + let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap(); + + assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/foo.rs")))); + assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("bar_dir/baz.rs")))); + assert!(!ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/bar.rs")))); + } +} diff --git a/src/lib.rs b/src/lib.rs index 464d318638d..b804f5637d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ use std::path::PathBuf; use std::rc::Rc; use failure::Fail; +use ignore; use syntax::{ast, parse::DirectoryOwnership}; use crate::comment::LineClasses; @@ -46,6 +47,7 @@ mod comment; pub(crate) mod config; mod expr; pub(crate) mod formatting; +mod ignore_path; mod imports; mod issues; mod items; @@ -110,6 +112,9 @@ pub enum ErrorKind { /// If we had formatted the given node, then we would have lost a comment. #[fail(display = "not formatted because a comment would be lost")] LostComment, + /// Invalid glob pattern in `ignore` configuration option. + #[fail(display = "Invalid glob pattern found in ignore list: {}", _0)] + InvalidGlobPattern(ignore::Error), } impl ErrorKind { diff --git a/src/test/mod.rs b/src/test/mod.rs index 0ee25ff5c19..199c2400476 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -561,7 +561,7 @@ fn get_config(config_file: Option<&Path>) -> Config { .read_to_string(&mut def_config) .expect("Couldn't read config"); - Config::from_toml(&def_config, Path::new("tests/config/")).expect("invalid TOML") + Config::from_toml(&def_config).expect("invalid TOML") } // Reads significant comments of the form: `// rustfmt-key: value` into a hash map.