Skip to content

Commit 8b2e80b

Browse files
authored
Merge pull request #1299 from Manishearth/clippy-toml
Search for `clippy.toml` recursively
2 parents 20b7559 + d52af53 commit 8b2e80b

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
@@ -137,17 +137,23 @@ mod reexport {
137137

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

150-
let (conf, errors) = utils::conf::read(file_name, must_exist);
156+
let (conf, errors) = utils::conf::read(file_name.as_ref().map(|p| p.as_ref()));
151157

152158
// all conf errors are non-fatal, we just use the default conf in case of error
153159
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)