2018-02-07 22:48:52 +09:00
|
|
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
use std::{env, fs};
|
|
|
|
use std::cell::Cell;
|
|
|
|
use std::default::Default;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{Error, ErrorKind, Read};
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
|
|
|
#[macro_use]
|
|
|
|
mod config_type;
|
|
|
|
#[macro_use]
|
|
|
|
mod options;
|
|
|
|
|
|
|
|
pub mod file_lines;
|
|
|
|
pub mod lists;
|
|
|
|
pub mod summary;
|
|
|
|
|
2018-03-02 14:28:34 +13:00
|
|
|
use config::config_type::ConfigType;
|
|
|
|
use config::file_lines::FileLines;
|
|
|
|
pub use config::lists::*;
|
|
|
|
pub use config::options::*;
|
|
|
|
use config::summary::Summary;
|
2018-02-07 22:48:52 +09:00
|
|
|
|
|
|
|
/// This macro defines configuration options used in rustfmt. Each option
|
|
|
|
/// is defined as follows:
|
|
|
|
///
|
|
|
|
/// `name: value type, default value, is stable, description;`
|
|
|
|
create_config! {
|
|
|
|
// Fundamental stuff
|
|
|
|
max_width: usize, 100, true, "Maximum width of each line";
|
|
|
|
hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment";
|
|
|
|
tab_spaces: usize, 4, true, "Number of spaces per tab";
|
|
|
|
newline_style: NewlineStyle, NewlineStyle::Unix, true, "Unix or Windows line endings";
|
|
|
|
indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items.";
|
2018-02-17 21:00:26 +01:00
|
|
|
use_small_heuristics: bool, true, false, "Whether to use different formatting for items and \
|
2018-02-07 22:48:52 +09:00
|
|
|
expressions if they satisfy a heuristic notion of 'small'.";
|
|
|
|
|
|
|
|
// strings and comments
|
|
|
|
format_strings: bool, false, false, "Format string literals where necessary";
|
|
|
|
wrap_comments: bool, false, true, "Break comments to fit on the line";
|
|
|
|
comment_width: usize, 80, false,
|
|
|
|
"Maximum length of comments. No effect unless wrap_comments = true";
|
|
|
|
normalize_comments: bool, false, true, "Convert /* */ comments to // comments where possible";
|
Attempt at checking for license (#209)
I'm not quite sure how best to handle loading the license template from
a path -- I mean obviously I know *how* to do it, but I'm not sure where
to fit it in the codebase :) So this first attempt puts the license
template directly into the config file.
These are my misgivings about the license template config option as a
path to a file (I'd love feedback if some of these are wrong or can be
easily circumvented!):
1. I thought the obvious choice for the type of `license_template` in
`create_config!` should be `PathBuf`, but `PathBuf` doesn't implement
`FromStr` (yet? see https://github.com/rust-lang/rust/issues/44431), so
it would have to be wrapped in a tuple struct, and I went down that road
for a little while but then it seemed like too much ceremony for too
little gain.
2. So a plain `String` then (which, mind you, also means the same
`doc_hint()`, i.e. `<string>`, not `<path>` or something like that). The
fact that it's a valid path will be checked once we try to read the
file.
3. But where in the code should the license template be read? The
obvious choice for me would be somewhere in `Config::from_toml()`, but
since `Config` is defined via the `create_config!` macro, that would
mean tight coupling between the macro invocation (which defines the
configuration option `license_template`) and its definition (which would
rely on the existence of that option to run the template loading code).
4. `license_template` could also be made a special option which is
hardwired into the macro. This gets rid of the tight coupling, but
special-casing one of the config options would make the code harder to
navigate.
5. Instead, the macro could maybe be rewritten to allow for config
options that load additional resources from files when the config is
being parsed, but that's beyond my skill level I'm afraid (and probably
overengineering the problem if it's only ever going to be used for this
one option).
6. Finally, the file can be loaded at some later point in time, e.g. in
`format_lines()`, right before `check_license()` is called. But to
face a potential *IO* error at so late a stage, when the source files
have already been parsed... I don't know, it doesn't feel right.
BTW I don't like that I'm actually parsing the license template as late
as inside `check_license()` either, but for much the same reasons, I
don't know where else to put it. If the `Config` were hand-rolled
instead of a macro, I'd just define a custom `license_template` option
and load and parse the template in the `Config`'s init. But the way
things are, I'm a bit at a loss.
However, if someone more familiar with the project would kindly provide
a few hints as to how the path approach can be done in a way that is as
clean as possible in the context of the codebase, I'll be more than
happy to implement it! :)
2018-02-16 23:21:57 +01:00
|
|
|
license_template: String, String::default(), false, "Check for license";
|
2018-02-07 22:48:52 +09:00
|
|
|
|
|
|
|
// Single line expressions and items.
|
|
|
|
empty_item_single_line: bool, true, false,
|
|
|
|
"Put empty-body functions and impls on a single line";
|
|
|
|
struct_lit_single_line: bool, true, false,
|
|
|
|
"Put small struct literals on a single line";
|
|
|
|
fn_single_line: bool, false, false, "Put single-expression functions on a single line";
|
|
|
|
where_single_line: bool, false, false, "To force single line where layout";
|
|
|
|
|
|
|
|
// Imports
|
|
|
|
imports_indent: IndentStyle, IndentStyle::Visual, false, "Indent of imports";
|
|
|
|
imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block";
|
|
|
|
|
|
|
|
// Ordering
|
|
|
|
reorder_extern_crates: bool, true, false, "Reorder extern crate statements alphabetically";
|
|
|
|
reorder_extern_crates_in_group: bool, true, false, "Reorder extern crate statements in group";
|
|
|
|
reorder_imports: bool, false, false, "Reorder import statements alphabetically";
|
|
|
|
reorder_imports_in_group: bool, false, false, "Reorder import statements in group";
|
|
|
|
reorder_imported_names: bool, true, false,
|
|
|
|
"Reorder lists of names in import statements alphabetically";
|
|
|
|
reorder_modules: bool, false, false, "Reorder module statemtents alphabetically in group";
|
|
|
|
|
|
|
|
// Spaces around punctuation
|
|
|
|
binop_separator: SeparatorPlace, SeparatorPlace::Front, false,
|
|
|
|
"Where to put a binary operator when a binary expression goes multiline.";
|
|
|
|
type_punctuation_density: TypeDensity, TypeDensity::Wide, false,
|
|
|
|
"Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
|
|
|
|
space_before_colon: bool, false, false, "Leave a space before the colon";
|
|
|
|
space_after_colon: bool, true, false, "Leave a space after the colon";
|
|
|
|
spaces_around_ranges: bool, false, false, "Put spaces around the .. and ... range operators";
|
|
|
|
spaces_within_parens_and_brackets: bool, false, false,
|
|
|
|
"Put spaces within non-empty parentheses or brackets";
|
|
|
|
|
|
|
|
// Misc.
|
|
|
|
combine_control_expr: bool, true, false, "Combine control expressions with function calls.";
|
|
|
|
struct_field_align_threshold: usize, 0, false, "Align struct fields if their diffs fits within \
|
|
|
|
threshold.";
|
|
|
|
remove_blank_lines_at_start_or_end_of_block: bool, true, false,
|
|
|
|
"Remove blank lines at start or end of a block";
|
|
|
|
match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \
|
|
|
|
the same line with the pattern of arms";
|
|
|
|
force_multiline_blocks: bool, false, false,
|
|
|
|
"Force multiline closure bodies and match arms to be wrapped in a block";
|
|
|
|
fn_args_density: Density, Density::Tall, false, "Argument density in functions";
|
|
|
|
brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items";
|
|
|
|
control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
|
|
|
|
"Brace style for control flow constructs";
|
|
|
|
trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false,
|
|
|
|
"How to handle trailing commas for lists";
|
|
|
|
trailing_semicolon: bool, true, false,
|
|
|
|
"Add trailing semicolon after break, continue and return";
|
|
|
|
match_block_trailing_comma: bool, false, false,
|
|
|
|
"Put a trailing comma after a block based match arm (non-block arms are not affected)";
|
|
|
|
blank_lines_upper_bound: usize, 1, false,
|
|
|
|
"Maximum number of blank lines which can be put between items.";
|
|
|
|
blank_lines_lower_bound: usize, 0, false,
|
|
|
|
"Minimum number of blank lines which must be put between items.";
|
|
|
|
|
|
|
|
// Options that can change the source code beyond whitespace/blocks (somewhat linty things)
|
|
|
|
merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one";
|
|
|
|
use_try_shorthand: bool, false, false, "Replace uses of the try! macro by the ? shorthand";
|
|
|
|
condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \
|
|
|
|
in tuple patterns";
|
|
|
|
force_explicit_abi: bool, true, true, "Always print the abi for extern items";
|
|
|
|
use_field_init_shorthand: bool, false, false, "Use field initialization shorthand if possible";
|
|
|
|
|
|
|
|
// Control options (changes the operation of rustfmt, rather than the formatting)
|
|
|
|
write_mode: WriteMode, WriteMode::Overwrite, false,
|
|
|
|
"What Write Mode to use when none is supplied: \
|
|
|
|
Replace, Overwrite, Display, Plain, Diff, Coverage";
|
|
|
|
color: Color, Color::Auto, false,
|
|
|
|
"What Color option to use when none is supplied: Always, Never, Auto";
|
|
|
|
required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,
|
|
|
|
"Require a specific version of rustfmt.";
|
|
|
|
unstable_features: bool, false, true,
|
|
|
|
"Enables unstable features. Only available on nightly channel";
|
|
|
|
disable_all_formatting: bool, false, false, "Don't reformat anything";
|
|
|
|
skip_children: bool, false, false, "Don't reformat out of line modules";
|
|
|
|
hide_parse_errors: bool, false, false, "Hide errors from the parser";
|
|
|
|
error_on_line_overflow: bool, true, false, "Error if unable to get all lines within max_width";
|
|
|
|
error_on_unformatted: bool, false, false,
|
|
|
|
"Error if unable to get comments or string literals within max_width, \
|
|
|
|
or they are left with trailing whitespaces";
|
|
|
|
report_todo: ReportTactic, ReportTactic::Never, false,
|
|
|
|
"Report all, none or unnumbered occurrences of TODO in source file comments";
|
|
|
|
report_fixme: ReportTactic, ReportTactic::Never, false,
|
|
|
|
"Report all, none or unnumbered occurrences of FIXME in source file comments";
|
|
|
|
|
|
|
|
// Not user-facing.
|
|
|
|
verbose: bool, false, false, "Use verbose output";
|
|
|
|
file_lines: FileLines, FileLines::all(), false,
|
|
|
|
"Lines to format; this is not supported in rustfmt.toml, and can only be specified \
|
|
|
|
via the --file-lines option";
|
2018-02-11 19:44:47 +01:00
|
|
|
width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false,
|
2018-02-07 22:48:52 +09:00
|
|
|
"'small' heuristic values";
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir`
|
|
|
|
///
|
|
|
|
/// Return the path if a config file exists, empty if no file exists, and Error for IO errors
|
|
|
|
pub fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
|
|
|
|
const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"];
|
|
|
|
for config_file_name in &CONFIG_FILE_NAMES {
|
|
|
|
let config_file = dir.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
|
|
|
|
// `rustfmt.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() != ErrorKind::NotFound {
|
|
|
|
return Err(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::Config;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_config_set() {
|
|
|
|
let mut config = Config::default();
|
|
|
|
config.set().verbose(false);
|
|
|
|
assert_eq!(config.verbose(), false);
|
|
|
|
config.set().verbose(true);
|
|
|
|
assert_eq!(config.verbose(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_config_used_to_toml() {
|
|
|
|
let config = Config::default();
|
|
|
|
|
|
|
|
let merge_derives = config.merge_derives();
|
|
|
|
let skip_children = config.skip_children();
|
|
|
|
|
|
|
|
let used_options = config.used_options();
|
|
|
|
let toml = used_options.to_toml().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
toml,
|
|
|
|
format!(
|
|
|
|
"merge_derives = {}\nskip_children = {}\n",
|
|
|
|
merge_derives, skip_children,
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_was_set() {
|
|
|
|
let config = Config::from_toml("hard_tabs = true").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(config.was_set().hard_tabs(), true);
|
|
|
|
assert_eq!(config.was_set().verbose(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME(#2183) these tests cannot be run in parallel because they use env vars
|
|
|
|
// #[test]
|
|
|
|
// fn test_as_not_nightly_channel() {
|
|
|
|
// let mut config = Config::default();
|
|
|
|
// assert_eq!(config.was_set().unstable_features(), false);
|
|
|
|
// config.set().unstable_features(true);
|
|
|
|
// assert_eq!(config.was_set().unstable_features(), false);
|
|
|
|
// }
|
|
|
|
|
|
|
|
// #[test]
|
|
|
|
// fn test_as_nightly_channel() {
|
|
|
|
// let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
|
|
|
|
// ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
|
|
|
|
// let mut config = Config::default();
|
|
|
|
// config.set().unstable_features(true);
|
|
|
|
// assert_eq!(config.was_set().unstable_features(), false);
|
|
|
|
// config.set().unstable_features(true);
|
|
|
|
// assert_eq!(config.unstable_features(), true);
|
|
|
|
// ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
|
|
|
|
// }
|
|
|
|
|
|
|
|
// #[test]
|
|
|
|
// fn test_unstable_from_toml() {
|
|
|
|
// let mut config = Config::from_toml("unstable_features = true").unwrap();
|
|
|
|
// assert_eq!(config.was_set().unstable_features(), false);
|
|
|
|
// let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
|
|
|
|
// ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
|
|
|
|
// config = Config::from_toml("unstable_features = true").unwrap();
|
|
|
|
// assert_eq!(config.was_set().unstable_features(), true);
|
|
|
|
// assert_eq!(config.unstable_features(), true);
|
|
|
|
// ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
|
|
|
|
// }
|
|
|
|
}
|