Search for clippy.toml recursively

This commit is contained in:
mcarton 2016-10-25 19:41:24 +02:00
parent 49c2c2c628
commit d52af53e04
No known key found for this signature in database
GPG Key ID: 5E427C794CBA45E8
2 changed files with 55 additions and 15 deletions

View File

@ -136,17 +136,23 @@ mod reexport {
#[cfg_attr(rustfmt, rustfmt_skip)] #[cfg_attr(rustfmt, rustfmt_skip)]
pub fn register_plugins(reg: &mut rustc_plugin::Registry) { pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
let conf = match utils::conf::file(reg.args()) { let conf = match utils::conf::file_from_args(reg.args()) {
Ok(file_name) => { Ok(file_name) => {
// if the user specified a file, it must exist, otherwise default to `clippy.toml` but // if the user specified a file, it must exist, otherwise default to `clippy.toml` but
// do not require the file to exist // do not require the file to exist
let (file_name, must_exist) = if let Some(ref file_name) = file_name { let file_name = if let Some(file_name) = file_name {
(&**file_name, true) Some(file_name)
} else { } else {
("clippy.toml", false) match utils::conf::lookup_conf_file() {
Ok(path) => path,
Err(error) => {
reg.sess.struct_err(&format!("error reading Clippy's configuration file: {}", error)).emit();
None
}
}
}; };
let (conf, errors) = utils::conf::read(file_name, must_exist); let (conf, errors) = utils::conf::read(file_name.as_ref().map(|p| p.as_ref()));
// all conf errors are non-fatal, we just use the default conf in case of error // all conf errors are non-fatal, we just use the default conf in case of error
for error in errors { for error in errors {

View File

@ -2,14 +2,13 @@
#![deny(missing_docs_in_private_items)] #![deny(missing_docs_in_private_items)]
use std::{fmt, fs, io}; use std::{env, fmt, fs, io, path};
use std::io::Read; use std::io::Read;
use syntax::{ast, codemap}; use syntax::{ast, codemap};
use syntax::parse::token;
use toml; use toml;
/// Get the configuration file from arguments. /// Get the configuration file from arguments.
pub fn file(args: &[codemap::Spanned<ast::NestedMetaItemKind>]) -> Result<Option<token::InternedString>, (&'static str, codemap::Span)> { pub fn file_from_args(args: &[codemap::Spanned<ast::NestedMetaItemKind>]) -> Result<Option<path::PathBuf>, (&'static str, codemap::Span)> {
for arg in args.iter().filter_map(|a| a.meta_item()) { for arg in args.iter().filter_map(|a| a.meta_item()) {
match arg.node { match arg.node {
ast::MetaItemKind::Word(ref name) | ast::MetaItemKind::Word(ref name) |
@ -21,7 +20,7 @@ pub fn file(args: &[codemap::Spanned<ast::NestedMetaItemKind>]) -> Result<Option
ast::MetaItemKind::NameValue(ref name, ref value) => { ast::MetaItemKind::NameValue(ref name, ref value) => {
if name == &"conf_file" { if name == &"conf_file" {
return if let ast::LitKind::Str(ref file, _) = value.node { return if let ast::LitKind::Str(ref file, _) = value.node {
Ok(Some(file.clone())) Ok(Some(file.to_string().into()))
} else { } else {
Err(("`conf_file` value must be a string", value.span)) Err(("`conf_file` value must be a string", value.span))
}; };
@ -179,13 +178,51 @@ define_Conf! {
("enum-variant-name-threshold", enum_variant_name_threshold, 3 => u64), ("enum-variant-name-threshold", enum_variant_name_threshold, 3 => u64),
} }
/// Read the `toml` configuration file. The function will ignore “File not found” errors iif /// Search for the configuration file.
/// `!must_exist`, in which case, it will return the default configuration. pub fn lookup_conf_file() -> io::Result<Option<path::PathBuf>> {
/// Possible filename to search for.
const CONFIG_FILE_NAMES: [&'static str; 2] = [".clippy.toml", "clippy.toml"];
let mut current = try!(env::current_dir());
loop {
for config_file_name in &CONFIG_FILE_NAMES {
let config_file = current.join(config_file_name);
match fs::metadata(&config_file) {
// Only return if it's a file to handle the unlikely situation of a directory named
// `clippy.toml`.
Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
// Return the error if it's something other than `NotFound`; otherwise we didn't
// find the project file yet, and continue searching.
Err(e) => {
if e.kind() != io::ErrorKind::NotFound {
return Err(e);
}
}
_ => (),
}
}
// If the current directory has no parent, we're done searching.
if !current.pop() {
return Ok(None);
}
}
}
/// Read the `toml` configuration file.
///
/// In case of error, the function tries to continue as much as possible. /// In case of error, the function tries to continue as much as possible.
pub fn read(path: &str, must_exist: bool) -> (Conf, Vec<Error>) { pub fn read(path: Option<&path::Path>) -> (Conf, Vec<Error>) {
let mut conf = Conf::default(); let mut conf = Conf::default();
let mut errors = Vec::new(); let mut errors = Vec::new();
let path = if let Some(path) = path {
path
} else {
return (conf, errors);
};
let file = match fs::File::open(path) { let file = match fs::File::open(path) {
Ok(mut file) => { Ok(mut file) => {
let mut buf = String::new(); let mut buf = String::new();
@ -197,9 +234,6 @@ pub fn read(path: &str, must_exist: bool) -> (Conf, Vec<Error>) {
buf buf
} }
Err(ref err) if !must_exist && err.kind() == io::ErrorKind::NotFound => {
return (conf, errors);
}
Err(err) => { Err(err) => {
errors.push(err.into()); errors.push(err.into());
return (conf, errors); return (conf, errors);