Skip to content

Commit d52af53

Browse files
committed
Search for clippy.toml recursively
1 parent 49c2c2c commit d52af53

File tree

2 files changed

+55
-15
lines changed

2 files changed

+55
-15
lines changed

clippy_lints/src/lib.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,23 @@ mod reexport {
136136

137137
#[cfg_attr(rustfmt, rustfmt_skip)]
138138
pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
139-
let conf = match utils::conf::file(reg.args()) {
139+
let conf = match utils::conf::file_from_args(reg.args()) {
140140
Ok(file_name) => {
141141
// if the user specified a file, it must exist, otherwise default to `clippy.toml` but
142142
// do not require the file to exist
143-
let (file_name, must_exist) = if let Some(ref file_name) = file_name {
144-
(&**file_name, true)
143+
let file_name = if let Some(file_name) = file_name {
144+
Some(file_name)
145145
} else {
146-
("clippy.toml", false)
146+
match utils::conf::lookup_conf_file() {
147+
Ok(path) => path,
148+
Err(error) => {
149+
reg.sess.struct_err(&format!("error reading Clippy's configuration file: {}", error)).emit();
150+
None
151+
}
152+
}
147153
};
148154

149-
let (conf, errors) = utils::conf::read(file_name, must_exist);
155+
let (conf, errors) = utils::conf::read(file_name.as_ref().map(|p| p.as_ref()));
150156

151157
// all conf errors are non-fatal, we just use the default conf in case of error
152158
for error in errors {

clippy_lints/src/utils/conf.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
33
#![deny(missing_docs_in_private_items)]
44

5-
use std::{fmt, fs, io};
5+
use std::{env, fmt, fs, io, path};
66
use std::io::Read;
77
use syntax::{ast, codemap};
8-
use syntax::parse::token;
98
use toml;
109

1110
/// Get the configuration file from arguments.
12-
pub fn file(args: &[codemap::Spanned<ast::NestedMetaItemKind>]) -> Result<Option<token::InternedString>, (&'static str, codemap::Span)> {
11+
pub fn file_from_args(args: &[codemap::Spanned<ast::NestedMetaItemKind>]) -> Result<Option<path::PathBuf>, (&'static str, codemap::Span)> {
1312
for arg in args.iter().filter_map(|a| a.meta_item()) {
1413
match arg.node {
1514
ast::MetaItemKind::Word(ref name) |
@@ -21,7 +20,7 @@ pub fn file(args: &[codemap::Spanned<ast::NestedMetaItemKind>]) -> Result<Option
2120
ast::MetaItemKind::NameValue(ref name, ref value) => {
2221
if name == &"conf_file" {
2322
return if let ast::LitKind::Str(ref file, _) = value.node {
24-
Ok(Some(file.clone()))
23+
Ok(Some(file.to_string().into()))
2524
} else {
2625
Err(("`conf_file` value must be a string", value.span))
2726
};
@@ -179,13 +178,51 @@ define_Conf! {
179178
("enum-variant-name-threshold", enum_variant_name_threshold, 3 => u64),
180179
}
181180

182-
/// Read the `toml` configuration file. The function will ignore “File not found” errors iif
183-
/// `!must_exist`, in which case, it will return the default configuration.
181+
/// Search for the configuration file.
182+
pub fn lookup_conf_file() -> io::Result<Option<path::PathBuf>> {
183+
/// Possible filename to search for.
184+
const CONFIG_FILE_NAMES: [&'static str; 2] = [".clippy.toml", "clippy.toml"];
185+
186+
let mut current = try!(env::current_dir());
187+
188+
loop {
189+
for config_file_name in &CONFIG_FILE_NAMES {
190+
let config_file = current.join(config_file_name);
191+
match fs::metadata(&config_file) {
192+
// Only return if it's a file to handle the unlikely situation of a directory named
193+
// `clippy.toml`.
194+
Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
195+
// Return the error if it's something other than `NotFound`; otherwise we didn't
196+
// find the project file yet, and continue searching.
197+
Err(e) => {
198+
if e.kind() != io::ErrorKind::NotFound {
199+
return Err(e);
200+
}
201+
}
202+
_ => (),
203+
}
204+
}
205+
206+
// If the current directory has no parent, we're done searching.
207+
if !current.pop() {
208+
return Ok(None);
209+
}
210+
}
211+
}
212+
213+
/// Read the `toml` configuration file.
214+
///
184215
/// In case of error, the function tries to continue as much as possible.
185-
pub fn read(path: &str, must_exist: bool) -> (Conf, Vec<Error>) {
216+
pub fn read(path: Option<&path::Path>) -> (Conf, Vec<Error>) {
186217
let mut conf = Conf::default();
187218
let mut errors = Vec::new();
188219

220+
let path = if let Some(path) = path {
221+
path
222+
} else {
223+
return (conf, errors);
224+
};
225+
189226
let file = match fs::File::open(path) {
190227
Ok(mut file) => {
191228
let mut buf = String::new();
@@ -197,9 +234,6 @@ pub fn read(path: &str, must_exist: bool) -> (Conf, Vec<Error>) {
197234

198235
buf
199236
}
200-
Err(ref err) if !must_exist && err.kind() == io::ErrorKind::NotFound => {
201-
return (conf, errors);
202-
}
203237
Err(err) => {
204238
errors.push(err.into());
205239
return (conf, errors);

0 commit comments

Comments
 (0)