Updated to latest rustfmt

This commit is contained in:
Connor Brewster 2016-03-12 11:09:27 -07:00
commit 3163071c75
60 changed files with 1503 additions and 624 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
/target/
**/*.rs.bk
/target/

66
Cargo.lock generated
View File

@ -1,26 +1,26 @@
[root]
name = "rustfmt"
version = "0.2.1"
version = "0.3.0"
dependencies = [
"diff 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"diff 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
"strings 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aho-corasick"
version = "0.4.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -30,7 +30,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "diff"
version = "0.1.8"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -38,8 +38,8 @@ name = "env_logger"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -58,43 +58,44 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.2"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.3.4"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "0.1.7"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.1.43"
version = "0.1.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.2.2"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-serialize"
version = "0.3.16"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -102,18 +103,18 @@ name = "strings"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syntex_syntax"
version = "0.23.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -129,10 +130,10 @@ dependencies = [
[[package]]
name = "toml"
version = "0.1.24"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -145,6 +146,11 @@ name = "unicode-xid"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.5"

View File

@ -1,7 +1,7 @@
[package]
name = "rustfmt"
version = "0.2.1"
version = "0.3.0"
authors = ["Nicholas Cameron <ncameron@mozilla.com>", "Marcus Klaas <mail@marcusklaas.nl>", "The Rustfmt contributors"]
description = "Tool to find and fix Rust formatting issues"
repository = "https://github.com/rust-lang-nursery/rustfmt"
@ -21,7 +21,7 @@ regex = "0.1.41"
term = "0.2.11"
strings = "0.0.1"
diff = "0.1.8"
syntex_syntax = "0.23.0"
syntex_syntax = "0.29.1"
log = "0.3.2"
env_logger = "0.3.1"
getopts = "0.2"

View File

@ -134,25 +134,34 @@ not worthwhile due to uniformity being desirable, but it is a useful goal).
### Architecture details
We use the AST from libsyntax. We use libsyntax's visit module to walk the AST
to find starting points for reformatting. Eventually, we should reformat everything
and we shouldn't need the visit module. We keep track of the last formatted
position in the code, and when we reformat the next piece of code we make sure
to output the span for all the code in between (handled by missed_spans.rs).
We use the AST from [syntex_syntax], an export of rustc's libsyntax. We use
syntex_syntax's visit module to walk the AST to find starting points for
reformatting. Eventually, we should reformat everything and we shouldn't need
the visit module. We keep track of the last formatted position in the code, and
when we reformat the next piece of code we make sure to output the span for all
the code in between (handled by missed_spans.rs).
[syntex_syntax]: https://crates.io/crates/syntex_syntax
We read in formatting configuration from a `rustfmt.toml` file if there is one.
The options and their defaults are defined in `config.rs`. A `Config` object is
passed throughout the formatting code, and each formatting routine looks there
for its configuration.
Our visitor keeps track of the desired current indent due to blocks (
`block_indent`). Each `visit_*` method reformats code according to this indent
and `IDEAL_WIDTH` and `MAX_WIDTH` (which should one day be supplied from a
config file). Most reformatting done in the `visit_*` methods is a bit hackey
and is meant to be temporary until it can be done properly.
`block_indent`). Each `visit_*` method reformats code according to this indent,
`config.ideal_width` and `config.max_width`. Most reformatting done in the
`visit_*` methods is a bit hackey and is meant to be temporary until it can be
done properly.
There are a bunch of methods called `rewrite_*`. There do the bulk of the
reformatting. These take the AST node to be reformatted (this may not literally
be an AST node from libsyntax, there might be multiple parameters describing a
logical node), the current indent, and the current width budget. They return a
`String` (or sometimes an `Option<String>`) which formats the code in the box
given by the indent and width budget. If the method fails, it returns `None` and
the calling method then has to fallback in some way to give the callee more space.
be an AST node from syntex_syntax: there might be multiple parameters
describing a logical node), the current indent, and the current width budget.
They return a `String` (or sometimes an `Option<String>`) which formats the
code in the box given by the indent and width budget. If the method fails, it
returns `None` and the calling method then has to fallback in some way to give
the callee more space.
So, in summary to format a node, we calculate the width budget and then walk down
the tree from the node. At a leaf, we generate an actual string and then unwind,

View File

@ -51,14 +51,17 @@ read data from stdin. Alternatively, you can use `cargo fmt` to format all
binary and library targets of your crate.
You'll probably want to specify the write mode. Currently, there are modes for
replace, overwrite, display, and coverage. The replace mode is the default
and overwrites the original files after renaming them. In overwrite mode,
rustfmt does not backup the source files. To print the output to stdout, use the
display mode. The write mode can be set by passing the `--write-mode` flag on
the command line.
diff, replace, overwrite, display, coverage, and checkstyle.
`rustfmt filename --write-mode=display` prints the output of rustfmt to the
screen, for example.
* `replace` Is the default and overwrites the original files after creating backups of the files.
* `overwrite` Overwrites the original files _without_ creating backups.
* `display` Will print the formatted files to stdout.
* `diff` Will print a diff between the original files and formatted files to stdout.
* `checkstyle` Will output the lines that need to be corrected as a checkstyle XML file,
that can be used by tools like Jenkins.
The write mode can be set by passing the `--write-mode` flag on
the command line. For example `rustfmt --write-mode=display src/filename.rs`
You can run `rustfmt --help` for more information.
@ -75,8 +78,6 @@ You can run `rustfmt --help` for more information.
## How to build and test
First make sure you've got Rust **1.4.0** or greater available, then:
`cargo build` to build.
`cargo test` to run all tests.
@ -85,12 +86,12 @@ To run rustfmt after this, use `cargo run --bin rustfmt -- filename`. See the
notes above on running rustfmt.
## What style does Rustfmt use?
## Configuring Rustfmt
Rustfmt is designed to be very configurable. You can create a TOML file called
rustfmt.toml, place it in the project directory and it will apply the options
in that file. See `cargo run -- --config-help` for the options which are available,
or if you prefer to see source code, [src/config.rs].
in that file. See `rustfmt --config-help` for the options which are available,
or if you prefer to see source code, [src/config.rs](src/config.rs).
By default, Rustfmt uses a style which (mostly) conforms to the
[Rust style guidelines](https://github.com/rust-lang/rust/tree/master/src/doc/style).

View File

@ -17,69 +17,120 @@ extern crate toml;
extern crate env_logger;
extern crate getopts;
use rustfmt::{WriteMode, run, run_from_stdin};
use rustfmt::config::Config;
use rustfmt::{run, run_from_stdin};
use rustfmt::config::{Config, WriteMode};
use std::env;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::io::{self, ErrorKind, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use getopts::{Matches, Options};
/// Rustfmt operations.
enum Operation {
/// Format files and their child modules.
Format(Vec<PathBuf>, WriteMode),
Format {
files: Vec<PathBuf>,
config_path: Option<PathBuf>,
},
/// Print the help message.
Help,
// Print version information
Version,
/// Print detailed configuration help.
ConfigHelp,
/// Invalid program input, including reason.
InvalidInput(String),
/// Invalid program input.
InvalidInput {
reason: String,
},
/// No file specified, read from stdin
Stdin(String, WriteMode),
Stdin {
input: String,
config_path: Option<PathBuf>,
},
}
/// Try to find a project file in the input file directory and its parents.
fn lookup_project_file(input_file: &Path) -> io::Result<PathBuf> {
let mut current = if input_file.is_relative() {
try!(env::current_dir()).join(input_file)
/// Try to find a project file in the given directory and its parents. Returns the path of a the
/// nearest project file if one exists, or `None` if no project file was found.
fn lookup_project_file(dir: &Path) -> io::Result<Option<PathBuf>> {
let mut current = if dir.is_relative() {
try!(env::current_dir()).join(dir)
} else {
input_file.to_path_buf()
dir.to_path_buf()
};
// FIXME: We should canonize path to properly handle its parents,
// but `canonicalize` function is unstable now (recently added API)
// current = try!(fs::canonicalize(current));
current = try!(fs::canonicalize(current));
loop {
let config_file = current.join("rustfmt.toml");
if fs::metadata(&config_file).is_ok() {
return Ok(config_file);
match fs::metadata(&config_file) {
Ok(md) => {
// Properly handle unlikely situation of a directory named `rustfmt.toml`.
if md.is_file() {
return Ok(Some(config_file));
}
}
// If it's not found, we continue searching; otherwise something went wrong and we
// return the error.
Err(e) => {
if e.kind() != ErrorKind::NotFound {
return Err(e);
}
}
}
// If the current directory has no parent, we're done searching.
if !current.pop() {
return Err(io::Error::new(io::ErrorKind::NotFound, "Config not found"));
return Ok(None);
}
}
}
/// Try to find a project file. If it's found, read it.
fn lookup_and_read_project_file(input_file: &Path) -> io::Result<(PathBuf, String)> {
let path = try!(lookup_project_file(input_file));
/// Resolve the config for input in `dir`.
///
/// Returns the `Config` to use, and the path of the project file if there was
/// one.
fn resolve_config(dir: &Path) -> io::Result<(Config, Option<PathBuf>)> {
let path = try!(lookup_project_file(dir));
if path.is_none() {
return Ok((Config::default(), None));
}
let path = path.unwrap();
let mut file = try!(File::open(&path));
let mut toml = String::new();
try!(file.read_to_string(&mut toml));
Ok((path, toml))
Ok((Config::from_toml(&toml), Some(path)))
}
fn update_config(config: &mut Config, matches: &Matches) {
/// read the given config file path recursively if present else read the project file path
fn match_cli_path_or_file(config_path: Option<PathBuf>,
input_file: &Path)
-> io::Result<(Config, Option<PathBuf>)> {
if let Some(config_file) = config_path {
let (toml, path) = try!(resolve_config(config_file.as_ref()));
if path.is_some() {
return Ok((toml, path));
}
}
resolve_config(input_file)
}
fn update_config(config: &mut Config, matches: &Matches) -> Result<(), String> {
config.verbose = matches.opt_present("verbose");
config.skip_children = matches.opt_present("skip-children");
let write_mode = matches.opt_str("write-mode");
match matches.opt_str("write-mode").map(|wm| WriteMode::from_str(&wm)) {
None => Ok(()),
Some(Ok(write_mode)) => {
config.write_mode = write_mode;
Ok(())
}
Some(Err(_)) => Err(format!("Invalid write-mode: {}", write_mode.expect("cannot happen"))),
}
}
fn execute() -> i32 {
@ -90,12 +141,17 @@ fn execute() -> i32 {
opts.optopt("",
"write-mode",
"mode to write in (not usable when piping from stdin)",
"[replace|overwrite|display|diff|coverage]");
"[replace|overwrite|display|diff|coverage|checkstyle]");
opts.optflag("", "skip-children", "don't reformat child modules");
opts.optflag("",
"config-help",
"show details of rustfmt configuration options");
opts.optopt("",
"config-path",
"Recursively searches the given path for the rustfmt.toml config file. If not \
found reverts to the input file path",
"[Path for the configuration file]");
let matches = match opts.parse(env::args().skip(1)) {
Ok(m) => m,
@ -108,7 +164,7 @@ fn execute() -> i32 {
let operation = determine_operation(&matches);
match operation {
Operation::InvalidInput(reason) => {
Operation::InvalidInput { reason } => {
print_usage(&opts, &reason);
1
}
@ -124,30 +180,51 @@ fn execute() -> i32 {
Config::print_docs();
0
}
Operation::Stdin(input, write_mode) => {
Operation::Stdin { input, config_path } => {
// try to read config from local directory
let config = match lookup_and_read_project_file(&Path::new(".")) {
Ok((_, toml)) => Config::from_toml(&toml),
Err(_) => Default::default(),
};
let (mut config, _) = match_cli_path_or_file(config_path, &env::current_dir().unwrap())
.expect("Error resolving config");
run_from_stdin(input, write_mode, &config);
// write_mode is always Plain for Stdin.
config.write_mode = WriteMode::Plain;
run_from_stdin(input, &config);
0
}
Operation::Format(files, write_mode) => {
Operation::Format { files, config_path } => {
let mut config = Config::default();
let mut path = None;
// Load the config path file if provided
if let Some(config_file) = config_path {
let (cfg_tmp, path_tmp) = resolve_config(config_file.as_ref())
.expect(&format!("Error resolving config for {:?}",
config_file));
config = cfg_tmp;
path = path_tmp;
};
if let Some(path) = path.as_ref() {
println!("Using rustfmt config file {}", path.display());
}
for file in files {
let mut config = match lookup_and_read_project_file(&file) {
Ok((path, toml)) => {
// Check the file directory if the config-path could not be read or not provided
if path.is_none() {
let (config_tmp, path_tmp) = resolve_config(file.parent().unwrap())
.expect(&format!("Error resolving config \
for {}",
file.display()));
if let Some(path) = path_tmp.as_ref() {
println!("Using rustfmt config file {} for {}",
path.display(),
file.display());
Config::from_toml(&toml)
}
Err(_) => Default::default(),
};
config = config_tmp;
}
update_config(&mut config, &matches);
run(&file, write_mode, &config);
if let Err(e) = update_config(&mut config, &matches) {
print_usage(&opts, &e);
return 1;
}
run(&file, &config);
}
0
}
@ -196,30 +273,35 @@ fn determine_operation(matches: &Matches) -> Operation {
return Operation::Version;
}
// Read the config_path and convert to parent dir if a file is provided.
let config_path: Option<PathBuf> = matches.opt_str("config-path")
.map(PathBuf::from)
.and_then(|dir| {
if dir.is_file() {
return dir.parent().map(|v| v.into());
}
Some(dir)
});
// if no file argument is supplied, read from stdin
if matches.free.is_empty() {
let mut buffer = String::new();
match io::stdin().read_to_string(&mut buffer) {
Ok(..) => (),
Err(e) => return Operation::InvalidInput(e.to_string()),
Err(e) => return Operation::InvalidInput { reason: e.to_string() },
}
// WriteMode is always plain for Stdin
return Operation::Stdin(buffer, WriteMode::Plain);
return Operation::Stdin {
input: buffer,
config_path: config_path,
};
}
let write_mode = match matches.opt_str("write-mode") {
Some(mode) => {
match mode.parse() {
Ok(mode) => mode,
Err(..) => return Operation::InvalidInput("Unrecognized write mode".into()),
}
}
None => WriteMode::Replace,
};
let files: Vec<_> = matches.free.iter().map(PathBuf::from).collect();
Operation::Format(files, write_mode)
Operation::Format {
files: files,
config_path: config_path,
}
}

View File

@ -94,7 +94,7 @@ pub fn rewrite_chain(mut expr: &ast::Expr,
let fits_single_line = !veto_single_line &&
match subexpr_list[0].node {
ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions)
ast::ExprKind::MethodCall(ref method_name, ref types, ref expressions)
if context.config.chains_overflow_last => {
let len = rewrites.len();
let (init, last) = rewrites.split_at_mut(len - 1);
@ -150,28 +150,28 @@ pub fn rewrite_chain(mut expr: &ast::Expr,
// parens, braces and brackets in its idiomatic formatting.
fn is_block_expr(expr: &ast::Expr, repr: &str) -> bool {
match expr.node {
ast::Expr_::ExprStruct(..) |
ast::Expr_::ExprWhile(..) |
ast::Expr_::ExprWhileLet(..) |
ast::Expr_::ExprIf(..) |
ast::Expr_::ExprIfLet(..) |
ast::Expr_::ExprBlock(..) |
ast::Expr_::ExprLoop(..) |
ast::Expr_::ExprForLoop(..) |
ast::Expr_::ExprMatch(..) => repr.contains('\n'),
ast::Expr_::ExprParen(ref expr) |
ast::Expr_::ExprBinary(_, _, ref expr) |
ast::Expr_::ExprIndex(_, ref expr) |
ast::Expr_::ExprUnary(_, ref expr) => is_block_expr(expr, repr),
ast::ExprKind::Struct(..) |
ast::ExprKind::While(..) |
ast::ExprKind::WhileLet(..) |
ast::ExprKind::If(..) |
ast::ExprKind::IfLet(..) |
ast::ExprKind::Block(..) |
ast::ExprKind::Loop(..) |
ast::ExprKind::ForLoop(..) |
ast::ExprKind::Match(..) => repr.contains('\n'),
ast::ExprKind::Paren(ref expr) |
ast::ExprKind::Binary(_, _, ref expr) |
ast::ExprKind::Index(_, ref expr) |
ast::ExprKind::Unary(_, ref expr) => is_block_expr(expr, repr),
_ => false,
}
}
fn pop_expr_chain(expr: &ast::Expr) -> Option<&ast::Expr> {
match expr.node {
ast::Expr_::ExprMethodCall(_, _, ref expressions) => Some(&expressions[0]),
ast::Expr_::ExprTupField(ref subexpr, _) |
ast::Expr_::ExprField(ref subexpr, _) => Some(subexpr),
ast::ExprKind::MethodCall(_, _, ref expressions) => Some(&expressions[0]),
ast::ExprKind::TupField(ref subexpr, _) |
ast::ExprKind::Field(ref subexpr, _) => Some(subexpr),
_ => None,
}
}
@ -183,7 +183,7 @@ fn rewrite_chain_expr(expr: &ast::Expr,
offset: Indent)
-> Option<String> {
match expr.node {
ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions) => {
ast::ExprKind::MethodCall(ref method_name, ref types, ref expressions) => {
let inner = &RewriteContext { block_indent: offset, ..*context };
rewrite_method_call(method_name.node,
types,
@ -193,7 +193,7 @@ fn rewrite_chain_expr(expr: &ast::Expr,
width,
offset)
}
ast::Expr_::ExprField(_, ref field) => {
ast::ExprKind::Field(_, ref field) => {
let s = format!(".{}", field.node);
if s.len() <= width {
Some(s)
@ -201,7 +201,7 @@ fn rewrite_chain_expr(expr: &ast::Expr,
None
}
}
ast::Expr_::ExprTupField(_, ref field) => {
ast::ExprKind::TupField(_, ref field) => {
let s = format!(".{}", field.node);
if s.len() <= width {
Some(s)
@ -216,7 +216,7 @@ fn rewrite_chain_expr(expr: &ast::Expr,
// Determines we can continue formatting a given expression on the same line.
fn is_continuable(expr: &ast::Expr) -> bool {
match expr.node {
ast::Expr_::ExprPath(..) => true,
ast::ExprKind::Path(..) => true,
_ => false,
}
}

82
src/checkstyle.rs Normal file
View File

@ -0,0 +1,82 @@
// 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 rustfmt_diff::{Mismatch, DiffLine};
use std::io::{self, Write, Read};
use config::WriteMode;
pub fn output_header<T>(out: &mut T, mode: WriteMode) -> Result<(), io::Error>
where T: Write
{
if mode == WriteMode::Checkstyle {
let mut xml_heading = String::new();
xml_heading.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
xml_heading.push_str("\n");
xml_heading.push_str("<checkstyle version=\"4.3\">");
try!(write!(out, "{}", xml_heading));
}
Ok(())
}
pub fn output_footer<T>(out: &mut T, mode: WriteMode) -> Result<(), io::Error>
where T: Write
{
if mode == WriteMode::Checkstyle {
let mut xml_tail = String::new();
xml_tail.push_str("</checkstyle>");
try!(write!(out, "{}", xml_tail));
}
Ok(())
}
pub fn output_checkstyle_file<T>(mut writer: T,
filename: &str,
diff: Vec<Mismatch>)
-> Result<(), io::Error>
where T: Write
{
try!(write!(writer, "<file name=\"{}\">", filename));
for mismatch in diff {
for line in mismatch.lines {
match line {
DiffLine::Expected(ref str) => {
let message = xml_escape_str(&str);
try!(write!(writer,
"<error line=\"{}\" severity=\"warning\" message=\"Should be \
`{}`\" />",
mismatch.line_number,
message));
}
_ => {
// Do nothing with context and expected.
}
}
}
}
try!(write!(writer, "</file>"));
Ok(())
}
// Convert special characters into XML entities.
// This is needed for checkstyle output.
fn xml_escape_str(string: &str) -> String {
let mut out = String::new();
for c in string.chars() {
match c {
'<' => out.push_str("&lt;"),
'>' => out.push_str("&gt;"),
'"' => out.push_str("&quot;"),
'\'' => out.push_str("&apos;"),
'&' => out.push_str("&amp;"),
_ => out.push(c),
}
}
out
}

View File

@ -73,6 +73,13 @@ configuration_option_enum! { Density:
CompressedIfEmpty,
}
configuration_option_enum! { TypeDensity:
// No spaces around "=" and "+"
Compressed,
// Spaces around " = " and " + "
Wide,
}
impl Density {
pub fn to_list_tactic(self) -> ListTactic {
match self {
@ -113,6 +120,23 @@ configuration_option_enum! { ReportTactic:
Never,
}
configuration_option_enum! { WriteMode:
// Backsup the original file and overwrites the orignal.
Replace,
// Overwrites original file without backup.
Overwrite,
// Write the output to stdout.
Display,
// Write the diff to stdout.
Diff,
// Display how much of the input file was processed
Coverage,
// Unfancy stdout
Plain,
// Output a checkstyle XML file.
Checkstyle,
}
// This trait and the following impl blocks are there so that we an use
// UCFS inside the get_docs() function on types for configs.
pub trait ConfigType {
@ -191,12 +215,12 @@ macro_rules! create_config {
}
pub fn from_toml(toml: &str) -> Config {
let parsed = toml.parse().unwrap();
let parsed = toml.parse().expect("Could not parse TOML");
let parsed_config:ParsedConfig = match toml::decode(parsed) {
Some(decoded) => decoded,
None => {
println!("Decoding config file failed. Config:\n{}", toml);
let parsed: toml::Value = toml.parse().unwrap();
let parsed: toml::Value = toml.parse().expect("Could not parse TOML");
println!("\n\nParsed:\n{:?}", parsed);
panic!();
}
@ -208,10 +232,14 @@ macro_rules! create_config {
match key {
$(
stringify!($i) => {
self.$i = val.parse::<$ty>().unwrap();
self.$i = val.parse::<$ty>()
.expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
stringify!($i),
val,
stringify!($ty)));
}
)+
_ => panic!("Bad config key!")
_ => panic!("Unknown config key in override: {}", key)
}
}
@ -270,6 +298,7 @@ create_config! {
newline_style: NewlineStyle, NewlineStyle::Unix, "Unix or Windows line endings";
fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for functions";
item_brace_style: BraceStyle, BraceStyle::SameLineWhere, "Brace style for structs and enums";
impl_empty_single_line: bool, true, "Put empty-body implementations on a single line";
fn_empty_single_line: bool, true, "Put empty-body functions on a single line";
fn_single_line: bool, false, "Put single-expression functions on a single line";
fn_return_indent: ReturnIndent, ReturnIndent::WithArgs,
@ -278,6 +307,8 @@ create_config! {
fn_args_density: Density, Density::Tall, "Argument density in functions";
fn_args_layout: StructLitStyle, StructLitStyle::Visual, "Layout of function arguments";
fn_arg_indent: BlockIndentStyle, BlockIndentStyle::Visual, "Indent on function arguments";
type_punctuation_density: TypeDensity, TypeDensity::Wide,
"Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
// Should we at least try to put the where clause on the same line as the rest of the
// function decl?
where_density: Density, Density::CompressedIfEmpty, "Density of a where clause";
@ -286,6 +317,7 @@ create_config! {
where_layout: ListTactic, ListTactic::Vertical, "Element layout inside a where clause";
where_pred_indent: BlockIndentStyle, BlockIndentStyle::Visual,
"Indentation style of a where predicate";
where_trailing_comma: bool, false, "Put a trailing comma on where clauses";
generics_indent: BlockIndentStyle, BlockIndentStyle::Visual, "Indentation of generics";
struct_trailing_comma: SeparatorTactic, SeparatorTactic::Vertical,
"If there is a trailing comma on structs";
@ -295,7 +327,7 @@ create_config! {
struct_lit_multiline_style: MultilineStyle, MultilineStyle::PreferSingle,
"Multiline style on literal structs";
enum_trailing_comma: bool, true, "Put a trailing comma on enum declarations";
report_todo: ReportTactic, ReportTactic::Always,
report_todo: ReportTactic, ReportTactic::Never,
"Report all, none or unnumbered occurrences of TODO in source file comments";
report_fixme: ReportTactic, ReportTactic::Never,
"Report all, none or unnumbered occurrences of FIXME in source file comments";
@ -303,7 +335,8 @@ create_config! {
chain_indent: BlockIndentStyle, BlockIndentStyle::Visual, "Indentation of chain";
reorder_imports: bool, false, "Reorder import statements alphabetically";
single_line_if_else: bool, false, "Put else on same line as closing brace for if statements";
format_strings: bool, true, "Format string literals, or leave as is";
format_strings: bool, true, "Format string literals where necessary";
force_format_strings: bool, false, "Always format string literals";
chains_overflow_last: bool, true, "Allow last call in method chain to break the line";
take_source_hints: bool, true, "Retain some formatting characteristics from the source code";
hard_tabs: bool, false, "Use tab characters for indentation, spaces for alignment";
@ -313,4 +346,6 @@ create_config! {
match_block_trailing_comma: bool, false,
"Put a trailing comma after a block based match arm (non-block arms are not affected)";
match_wildcard_trailing_comma: bool, true, "Put a trailing comma after a wildcard arm";
write_mode: WriteMode, WriteMode::Replace,
"What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage";
}

View File

@ -13,14 +13,15 @@ use std::borrow::Borrow;
use std::mem::swap;
use std::ops::Deref;
use std::iter::ExactSizeIterator;
use std::fmt::Write;
use {Indent, Spanned};
use rewrite::{Rewrite, RewriteContext};
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic,
DefinitiveListTactic, definitive_tactic, ListItem, format_fn_args};
use string::{StringFormat, rewrite_string};
use utils::{span_after, extra_offset, last_line_width, wrap_str, binary_search, first_line_width,
semicolon_for_stmt};
use utils::{span_after, span_before, extra_offset, last_line_width, wrap_str, binary_search,
first_line_width, semicolon_for_stmt};
use visitor::FmtVisitor;
use config::{Config, StructLitStyle, MultilineStyle};
use comment::{FindUncommented, rewrite_comment, contains_comment, recover_comment_removed};
@ -36,16 +37,16 @@ use syntax::visit::Visitor;
impl Rewrite for ast::Expr {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
let result = match self.node {
ast::Expr_::ExprVec(ref expr_vec) => {
ast::ExprKind::Vec(ref expr_vec) => {
rewrite_array(expr_vec.iter().map(|e| &**e),
mk_sp(span_after(self.span, "[", context.codemap), self.span.hi),
context,
width,
offset)
}
ast::Expr_::ExprLit(ref l) => {
ast::ExprKind::Lit(ref l) => {
match l.node {
ast::Lit_::LitStr(_, ast::StrStyle::CookedStr) => {
ast::LitKind::Str(_, ast::StrStyle::Cooked) => {
rewrite_string_lit(context, l.span, width, offset)
}
_ => {
@ -56,18 +57,18 @@ impl Rewrite for ast::Expr {
}
}
}
ast::Expr_::ExprCall(ref callee, ref args) => {
ast::ExprKind::Call(ref callee, ref args) => {
let inner_span = mk_sp(callee.span.hi, self.span.hi);
rewrite_call(context, &**callee, args, inner_span, width, offset)
}
ast::Expr_::ExprParen(ref subexpr) => rewrite_paren(context, subexpr, width, offset),
ast::Expr_::ExprBinary(ref op, ref lhs, ref rhs) => {
ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, width, offset),
ast::ExprKind::Binary(ref op, ref lhs, ref rhs) => {
rewrite_binary_op(context, op, lhs, rhs, width, offset)
}
ast::Expr_::ExprUnary(ref op, ref subexpr) => {
ast::ExprKind::Unary(ref op, ref subexpr) => {
rewrite_unary_op(context, op, subexpr, width, offset)
}
ast::Expr_::ExprStruct(ref path, ref fields, ref base) => {
ast::ExprKind::Struct(ref path, ref fields, ref base) => {
rewrite_struct_lit(context,
path,
fields,
@ -76,79 +77,81 @@ impl Rewrite for ast::Expr {
width,
offset)
}
ast::Expr_::ExprTup(ref items) => {
ast::ExprKind::Tup(ref items) => {
rewrite_tuple(context,
items.iter().map(|x| &**x),
self.span,
width,
offset)
}
ast::Expr_::ExprWhile(ref cond, ref block, label) => {
ast::ExprKind::While(ref cond, ref block, label) => {
Loop::new_while(None, cond, block, label).rewrite(context, width, offset)
}
ast::Expr_::ExprWhileLet(ref pat, ref cond, ref block, label) => {
ast::ExprKind::WhileLet(ref pat, ref cond, ref block, label) => {
Loop::new_while(Some(pat), cond, block, label).rewrite(context, width, offset)
}
ast::Expr_::ExprForLoop(ref pat, ref cond, ref block, label) => {
ast::ExprKind::ForLoop(ref pat, ref cond, ref block, label) => {
Loop::new_for(pat, cond, block, label).rewrite(context, width, offset)
}
ast::Expr_::ExprLoop(ref block, label) => {
ast::ExprKind::Loop(ref block, label) => {
Loop::new_loop(block, label).rewrite(context, width, offset)
}
ast::Expr_::ExprBlock(ref block) => block.rewrite(context, width, offset),
ast::Expr_::ExprIf(ref cond, ref if_block, ref else_block) => {
ast::ExprKind::Block(ref block) => block.rewrite(context, width, offset),
ast::ExprKind::If(ref cond, ref if_block, ref else_block) => {
rewrite_if_else(context,
cond,
if_block,
else_block.as_ref().map(|e| &**e),
self.span,
None,
width,
offset,
true)
}
ast::Expr_::ExprIfLet(ref pat, ref cond, ref if_block, ref else_block) => {
ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref else_block) => {
rewrite_if_else(context,
cond,
if_block,
else_block.as_ref().map(|e| &**e),
self.span,
Some(pat),
width,
offset,
true)
}
ast::Expr_::ExprMatch(ref cond, ref arms) => {
ast::ExprKind::Match(ref cond, ref arms) => {
rewrite_match(context, cond, arms, width, offset, self.span)
}
ast::Expr_::ExprPath(ref qself, ref path) => {
ast::ExprKind::Path(ref qself, ref path) => {
rewrite_path(context, true, qself.as_ref(), path, width, offset)
}
ast::Expr_::ExprAssign(ref lhs, ref rhs) => {
ast::ExprKind::Assign(ref lhs, ref rhs) => {
rewrite_assignment(context, lhs, rhs, None, width, offset)
}
ast::Expr_::ExprAssignOp(ref op, ref lhs, ref rhs) => {
ast::ExprKind::AssignOp(ref op, ref lhs, ref rhs) => {
rewrite_assignment(context, lhs, rhs, Some(op), width, offset)
}
ast::Expr_::ExprAgain(ref opt_ident) => {
ast::ExprKind::Again(ref opt_ident) => {
let id_str = match *opt_ident {
Some(ident) => format!(" {}", ident.node),
None => String::new(),
};
Some(format!("continue{}", id_str))
}
ast::Expr_::ExprBreak(ref opt_ident) => {
ast::ExprKind::Break(ref opt_ident) => {
let id_str = match *opt_ident {
Some(ident) => format!(" {}", ident.node),
None => String::new(),
};
Some(format!("break{}", id_str))
}
ast::Expr_::ExprClosure(capture, ref fn_decl, ref body) => {
ast::ExprKind::Closure(capture, ref fn_decl, ref body) => {
rewrite_closure(capture, fn_decl, body, self.span, context, width, offset)
}
ast::Expr_::ExprField(..) |
ast::Expr_::ExprTupField(..) |
ast::Expr_::ExprMethodCall(..) => rewrite_chain(self, context, width, offset),
ast::Expr_::ExprMac(ref mac) => {
ast::ExprKind::Field(..) |
ast::ExprKind::TupField(..) |
ast::ExprKind::MethodCall(..) => rewrite_chain(self, context, width, offset),
ast::ExprKind::Mac(ref mac) => {
// Failure to rewrite a marco should not imply failure to
// rewrite the expression.
rewrite_macro(mac, context, width, offset).or_else(|| {
@ -158,40 +161,43 @@ impl Rewrite for ast::Expr {
offset)
})
}
ast::Expr_::ExprRet(None) => {
ast::ExprKind::Ret(None) => {
wrap_str("return".to_owned(), context.config.max_width, width, offset)
}
ast::Expr_::ExprRet(Some(ref expr)) => {
ast::ExprKind::Ret(Some(ref expr)) => {
rewrite_unary_prefix(context, "return ", &**expr, width, offset)
}
ast::Expr_::ExprBox(ref expr) => {
ast::ExprKind::Box(ref expr) => {
rewrite_unary_prefix(context, "box ", &**expr, width, offset)
}
ast::Expr_::ExprAddrOf(mutability, ref expr) => {
ast::ExprKind::AddrOf(mutability, ref expr) => {
rewrite_expr_addrof(context, mutability, expr, width, offset)
}
ast::Expr_::ExprCast(ref expr, ref ty) => {
ast::ExprKind::Cast(ref expr, ref ty) => {
rewrite_pair(&**expr, &**ty, "", " as ", "", context, width, offset)
}
ast::Expr_::ExprIndex(ref expr, ref index) => {
// TODO(#848): Handle type ascription; rust tracking issue
// https://github.com/rust-lang/rust/issues/23416
ast::ExprKind::Type(_, _) => unimplemented!(),
ast::ExprKind::Index(ref expr, ref index) => {
rewrite_pair(&**expr, &**index, "", "[", "]", context, width, offset)
}
ast::Expr_::ExprRepeat(ref expr, ref repeats) => {
ast::ExprKind::Repeat(ref expr, ref repeats) => {
rewrite_pair(&**expr, &**repeats, "[", "; ", "]", context, width, offset)
}
ast::Expr_::ExprRange(Some(ref lhs), Some(ref rhs)) => {
ast::ExprKind::Range(Some(ref lhs), Some(ref rhs)) => {
rewrite_pair(&**lhs, &**rhs, "", "..", "", context, width, offset)
}
ast::Expr_::ExprRange(None, Some(ref rhs)) => {
ast::ExprKind::Range(None, Some(ref rhs)) => {
rewrite_unary_prefix(context, "..", &**rhs, width, offset)
}
ast::Expr_::ExprRange(Some(ref lhs), None) => {
ast::ExprKind::Range(Some(ref lhs), None) => {
Some(format!("{}..",
try_opt!(lhs.rewrite(context,
try_opt!(width.checked_sub(2)),
offset))))
}
ast::Expr_::ExprRange(None, None) => {
ast::ExprKind::Range(None, None) => {
if width >= 2 {
Some("..".into())
} else {
@ -200,8 +206,8 @@ impl Rewrite for ast::Expr {
}
// We do not format these expressions yet, but they should still
// satisfy our width restrictions.
ast::Expr_::ExprInPlace(..) |
ast::Expr_::ExprInlineAsm(..) => {
ast::ExprKind::InPlace(..) |
ast::ExprKind::InlineAsm(..) => {
wrap_str(context.snippet(self.span),
context.config.max_width,
width,
@ -299,7 +305,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I,
// This functions is pretty messy because of the wrapping and unwrapping of
// expressions into and from blocks. See rust issue #27872.
fn rewrite_closure(capture: ast::CaptureClause,
fn rewrite_closure(capture: ast::CaptureBy,
fn_decl: &ast::FnDecl,
body: &ast::Block,
span: Span,
@ -307,7 +313,7 @@ fn rewrite_closure(capture: ast::CaptureClause,
width: usize,
offset: Indent)
-> Option<String> {
let mover = if capture == ast::CaptureClause::CaptureByValue {
let mover = if capture == ast::CaptureBy::Value {
"move "
} else {
""
@ -371,9 +377,8 @@ fn rewrite_closure(capture: ast::CaptureClause,
// All closure bodies are blocks in the eyes of the AST, but we may not
// want to unwrap them when they only contain a single expression.
let inner_expr = match expr.node {
ast::Expr_::ExprBlock(ref inner) if inner.stmts.is_empty() && inner.expr.is_some() &&
inner.rules ==
ast::BlockCheckMode::DefaultBlock => {
ast::ExprKind::Block(ref inner) if inner.stmts.is_empty() && inner.expr.is_some() &&
inner.rules == ast::BlockCheckMode::Default => {
inner.expr.as_ref().unwrap()
}
_ => expr,
@ -395,7 +400,7 @@ fn rewrite_closure(capture: ast::CaptureClause,
let body_rewrite = body.expr
.as_ref()
.and_then(|body_expr| {
if let ast::Expr_::ExprBlock(ref inner) = body_expr.node {
if let ast::ExprKind::Block(ref inner) = body_expr.node {
Some(inner.rewrite(&context, 2, Indent::empty()))
} else {
None
@ -424,11 +429,11 @@ impl Rewrite for ast::Block {
return Some(user_str);
}
let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config, None);
let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
visitor.block_indent = context.block_indent;
let prefix = match self.rules {
ast::BlockCheckMode::UnsafeBlock(..) => {
ast::BlockCheckMode::Unsafe(..) => {
let snippet = context.snippet(self.span);
let open_pos = try_opt!(snippet.find_uncommented("{"));
visitor.last_pos = self.span.lo + BytePos(open_pos as u32);
@ -464,7 +469,7 @@ impl Rewrite for ast::Block {
prefix
}
ast::BlockCheckMode::DefaultBlock => {
ast::BlockCheckMode::Default => {
visitor.last_pos = self.span.lo;
String::new()
@ -480,14 +485,14 @@ impl Rewrite for ast::Block {
impl Rewrite for ast::Stmt {
fn rewrite(&self, context: &RewriteContext, _width: usize, offset: Indent) -> Option<String> {
let result = match self.node {
ast::Stmt_::StmtDecl(ref decl, _) => {
if let ast::Decl_::DeclLocal(ref local) = decl.node {
ast::StmtKind::Decl(ref decl, _) => {
if let ast::DeclKind::Local(ref local) = decl.node {
local.rewrite(context, context.config.max_width, offset)
} else {
None
}
}
ast::Stmt_::StmtExpr(ref ex, _) | ast::Stmt_::StmtSemi(ref ex, _) => {
ast::StmtKind::Expr(ref ex, _) | ast::StmtKind::Semi(ref ex, _) => {
let suffix = if semicolon_for_stmt(self) {
";"
} else {
@ -499,7 +504,7 @@ impl Rewrite for ast::Stmt {
offset)
.map(|s| s + suffix)
}
ast::Stmt_::StmtMac(..) => None,
ast::StmtKind::Mac(..) => None,
};
result.and_then(|res| recover_comment_removed(res, self.span, context, _width, offset))
}
@ -605,12 +610,33 @@ fn rewrite_label(label: Option<ast::Ident>) -> String {
}
}
fn extract_comment(span: Span,
context: &RewriteContext,
offset: Indent,
width: usize)
-> Option<String> {
let comment_str = context.snippet(span);
if contains_comment(&comment_str) {
let comment = try_opt!(rewrite_comment(comment_str.trim(),
false,
width,
offset,
context.config));
Some(format!("\n{indent}{}\n{indent}",
comment,
indent = offset.to_string(context.config)))
} else {
None
}
}
// Rewrites if-else blocks. If let Some(_) = pat, the expression is
// treated as an if-let-else expression.
fn rewrite_if_else(context: &RewriteContext,
cond: &ast::Expr,
if_block: &ast::Block,
else_block_opt: Option<&ast::Expr>,
span: Span,
pat: Option<&ast::Pat>,
width: usize,
offset: Indent,
@ -635,27 +661,45 @@ fn rewrite_if_else(context: &RewriteContext,
}
let if_block_string = try_opt!(if_block.rewrite(context, width, offset));
let mut result = format!("if {} {}", pat_expr_string, if_block_string);
let between_if_cond = mk_sp(span_after(span, "if", context.codemap),
pat.map_or(cond.span.lo,
|_| span_before(span, "let", context.codemap)));
let between_if_cond_comment = extract_comment(between_if_cond, &context, offset, width);
let after_cond_comment = extract_comment(mk_sp(cond.span.hi, if_block.span.lo),
context,
offset,
width);
let mut result = format!("if{}{}{}{}",
between_if_cond_comment.as_ref().map_or(" ", |str| &**str),
pat_expr_string,
after_cond_comment.as_ref().map_or(" ", |str| &**str),
if_block_string);
if let Some(else_block) = else_block_opt {
let rewrite = match else_block.node {
// If the else expression is another if-else expression, prevent it
// from being formatted on a single line.
ast::Expr_::ExprIfLet(ref pat, ref cond, ref if_block, ref else_block) => {
ast::ExprKind::IfLet(ref pat, ref cond, ref if_block, ref next_else_block) => {
rewrite_if_else(context,
cond,
if_block,
else_block.as_ref().map(|e| &**e),
next_else_block.as_ref().map(|e| &**e),
mk_sp(else_block.span.lo, span.hi),
Some(pat),
width,
offset,
false)
}
ast::Expr_::ExprIf(ref cond, ref if_block, ref else_block) => {
ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => {
rewrite_if_else(context,
cond,
if_block,
else_block.as_ref().map(|e| &**e),
next_else_block.as_ref().map(|e| &**e),
mk_sp(else_block.span.lo, span.hi),
None,
width,
offset,
@ -664,7 +708,26 @@ fn rewrite_if_else(context: &RewriteContext,
_ => else_block.rewrite(context, width, offset),
};
result.push_str(" else ");
let between_if_else_block = mk_sp(if_block.span.hi,
span_before(mk_sp(if_block.span.hi, else_block.span.lo),
"else",
context.codemap));
let between_if_else_block_comment = extract_comment(between_if_else_block,
&context,
offset,
width);
let after_else = mk_sp(span_after(mk_sp(if_block.span.hi, else_block.span.lo),
"else",
context.codemap),
else_block.span.lo);
let after_else_comment = extract_comment(after_else, &context, offset, width);
try_opt!(write!(&mut result,
"{}else{}",
between_if_else_block_comment.as_ref().map_or(" ", |str| &**str),
after_else_comment.as_ref().map_or(" ", |str| &**str))
.ok());
result.push_str(&&try_opt!(rewrite));
}
@ -680,7 +743,7 @@ fn single_line_if_else(context: &RewriteContext,
let else_block = try_opt!(else_block_opt);
let fixed_cost = "if { } else { }".len();
if let ast::ExprBlock(ref else_node) = else_block.node {
if let ast::ExprKind::Block(ref else_node) = else_block.node {
if !is_simple_block(if_node, context.codemap) ||
!is_simple_block(else_node, context.codemap) || pat_expr_str.contains('\n') {
return None;
@ -733,7 +796,7 @@ pub fn is_empty_block(block: &ast::Block, codemap: &CodeMap) -> bool {
}
fn is_unsafe_block(block: &ast::Block) -> bool {
if let ast::BlockCheckMode::UnsafeBlock(..) = block.rules {
if let ast::BlockCheckMode::Unsafe(..) = block.rules {
true
} else {
false
@ -864,15 +927,15 @@ fn arm_end_pos(arm: &ast::Arm) -> BytePos {
fn arm_comma(config: &Config, arm: &ast::Arm, body: &ast::Expr) -> &'static str {
if !config.match_wildcard_trailing_comma {
if arm.pats.len() == 1 && arm.pats[0].node == ast::PatWild && arm.guard.is_none() {
if arm.pats.len() == 1 && arm.pats[0].node == ast::PatKind::Wild && arm.guard.is_none() {
return "";
}
}
if config.match_block_trailing_comma {
","
} else if let ast::ExprBlock(ref block) = body.node {
if let ast::DefaultBlock = block.rules {
} else if let ast::ExprKind::Block(ref block) = body.node {
if let ast::BlockCheckMode::Default = block.rules {
""
} else {
","
@ -893,9 +956,7 @@ impl Rewrite for ast::Arm {
let attr_str = if !attrs.is_empty() {
// We only use this visitor for the attributes, should we use it for
// more?
let mut attr_visitor = FmtVisitor::from_codemap(context.parse_session,
context.config,
None);
let mut attr_visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
attr_visitor.block_indent = context.block_indent;
attr_visitor.last_pos = attrs[0].span.lo;
if attr_visitor.visit_attrs(attrs) {
@ -956,12 +1017,9 @@ impl Rewrite for ast::Arm {
}
let body = match **body {
ast::Expr { node: ast::ExprBlock(ref block), .. } if !is_unsafe_block(block) &&
is_simple_block(block,
context.codemap) &&
context.config.wrap_match_arms => {
block.expr.as_ref().map(|e| &**e).unwrap()
}
ast::Expr { node: ast::ExprKind::Block(ref block), .. }
if !is_unsafe_block(block) && is_simple_block(block, context.codemap) &&
context.config.wrap_match_arms => block.expr.as_ref().map(|e| &**e).unwrap(),
ref x => x,
};
@ -973,7 +1031,7 @@ impl Rewrite for ast::Arm {
let budget = context.config.max_width - line_start - comma.len() - 4;
let offset = Indent::new(offset.block_indent, line_start + 4 - offset.block_indent);
let rewrite = nop_block_collapse(body.rewrite(context, budget, offset), budget);
let is_block = if let ast::ExprBlock(..) = body.node {
let is_block = if let ast::ExprKind::Block(..) = body.node {
true
} else {
false
@ -1121,8 +1179,15 @@ fn rewrite_string_lit(context: &RewriteContext,
width: usize,
offset: Indent)
-> Option<String> {
if !context.config.format_strings {
return Some(context.snippet(span));
let string_lit = context.snippet(span);
if !context.config.format_strings && !context.config.force_format_strings {
return Some(string_lit);
}
if !context.config.force_format_strings &&
!string_requires_rewrite(context, span, &string_lit, width, offset) {
return Some(string_lit);
}
let fmt = StringFormat {
@ -1136,12 +1201,37 @@ fn rewrite_string_lit(context: &RewriteContext,
config: context.config,
};
let string_lit = context.snippet(span);
let str_lit = &string_lit[1..string_lit.len() - 1]; // Remove the quote characters.
// Remove the quote characters.
let str_lit = &string_lit[1..string_lit.len() - 1];
rewrite_string(str_lit, &fmt)
}
fn string_requires_rewrite(context: &RewriteContext,
span: Span,
string: &str,
width: usize,
offset: Indent)
-> bool {
if context.codemap.lookup_char_pos(span.lo).col.0 != offset.width() {
return true;
}
for (i, line) in string.lines().enumerate() {
if i == 0 {
if line.len() > width {
return true;
}
} else {
if line.len() > width + offset.width() {
return true;
}
}
}
false
}
pub fn rewrite_call<R>(context: &RewriteContext,
callee: &R,
args: &[ptr::P<ast::Expr>],
@ -1216,8 +1306,8 @@ fn rewrite_call_inner<R>(context: &RewriteContext,
// indentation. If its first line fits on one line with the other arguments,
// we format the function arguments horizontally.
let overflow_last = match args.last().map(|x| &x.node) {
Some(&ast::Expr_::ExprClosure(..)) |
Some(&ast::Expr_::ExprBlock(..)) if arg_count > 1 => true,
Some(&ast::ExprKind::Closure(..)) |
Some(&ast::ExprKind::Block(..)) if arg_count > 1 => true,
_ => false,
} && context.config.chains_overflow_last;
@ -1311,6 +1401,7 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
// Foo { a: Foo } - indent is +3, width is -5.
let h_budget = width.checked_sub(path_str.len() + 5).unwrap_or(0);
// The 1 taken from the v_budget is for the comma.
let (indent, v_budget) = match context.config.struct_lit_style {
StructLitStyle::Visual => (offset + path_str.len() + 3, h_budget),
StructLitStyle::Block => {
@ -1355,7 +1446,10 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext,
|item| {
match *item {
StructLitField::Regular(ref field) => {
rewrite_field(inner_context, &field, v_budget, indent)
rewrite_field(inner_context,
&field,
v_budget.checked_sub(1).unwrap_or(0),
indent)
}
StructLitField::Base(ref expr) => {
// 2 = ..
@ -1441,7 +1535,19 @@ fn rewrite_field(context: &RewriteContext,
let expr = field.expr.rewrite(context,
try_opt!(width.checked_sub(overhead)),
offset + overhead);
expr.map(|s| format!("{}: {}", name, s))
match expr {
Some(e) => Some(format!("{}: {}", name, e)),
None => {
let expr_offset = offset.block_indent(&context.config);
let expr = field.expr.rewrite(context,
try_opt!(context.config
.max_width
.checked_sub(expr_offset.width())),
expr_offset);
expr.map(|s| format!("{}:\n{}{}", name, expr_offset.to_string(&context.config), s))
}
}
}
pub fn rewrite_tuple<'a, I>(context: &RewriteContext,
@ -1559,9 +1665,9 @@ fn rewrite_unary_op(context: &RewriteContext,
-> Option<String> {
// For some reason, an UnOp is not spanned like BinOp!
let operator_str = match *op {
ast::UnOp::UnDeref => "*",
ast::UnOp::UnNot => "!",
ast::UnOp::UnNeg => "-",
ast::UnOp::Deref => "*",
ast::UnOp::Not => "!",
ast::UnOp::Neg => "-",
};
rewrite_unary_prefix(context, operator_str, expr, width, offset)
}
@ -1637,8 +1743,8 @@ fn rewrite_expr_addrof(context: &RewriteContext,
offset: Indent)
-> Option<String> {
let operator_str = match mutability {
ast::Mutability::MutImmutable => "&",
ast::Mutability::MutMutable => "&mut ",
ast::Mutability::Immutable => "&",
ast::Mutability::Mutable => "&mut ",
};
rewrite_unary_prefix(context, operator_str, expr, width, offset)
}

View File

@ -15,11 +15,11 @@ use strings::string_buffer::StringBuffer;
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{self, Write, Read, stdout};
use std::io::{self, Write, Read, stdout, BufWriter};
use WriteMode;
use config::{NewlineStyle, Config};
use rustfmt_diff::{make_diff, print_diff};
use config::{NewlineStyle, Config, WriteMode};
use rustfmt_diff::{make_diff, print_diff, Mismatch};
use checkstyle::{output_header, output_footer, output_checkstyle_file};
// A map of the files of a crate, with their new content
pub type FileMap = HashMap<String, StringBuffer>;
@ -31,59 +31,62 @@ pub fn append_newlines(file_map: &mut FileMap) {
}
}
pub fn write_all_files(file_map: &FileMap,
mode: WriteMode,
config: &Config)
-> Result<(HashMap<String, String>), io::Error> {
let mut result = HashMap::new();
pub fn write_all_files<T>(file_map: &FileMap, mut out: T, config: &Config) -> Result<(), io::Error>
where T: Write
{
output_header(&mut out, config.write_mode).ok();
for filename in file_map.keys() {
let one_result = try!(write_file(&file_map[filename], filename, mode, config));
if let Some(r) = one_result {
result.insert(filename.clone(), r);
}
try!(write_file(&file_map[filename], filename, &mut out, config));
}
output_footer(&mut out, config.write_mode).ok();
Ok(result)
Ok(())
}
pub fn write_file(text: &StringBuffer,
filename: &str,
mode: WriteMode,
config: &Config)
-> Result<Option<String>, io::Error> {
// prints all newlines either as `\n` or as `\r\n`
fn write_system_newlines<T>(mut writer: T,
// Prints all newlines either as `\n` or as `\r\n`.
pub fn write_system_newlines<T>(writer: T,
text: &StringBuffer,
config: &Config)
-> Result<(), io::Error>
where T: Write
{
let style = if config.newline_style == NewlineStyle::Native {
if cfg!(windows) {
NewlineStyle::Windows
} else {
NewlineStyle::Unix
}
} else {
config.newline_style
};
where T: Write
{
// Buffer output, since we're writing a since char at a time.
let mut writer = BufWriter::new(writer);
match style {
NewlineStyle::Unix => write!(writer, "{}", text),
NewlineStyle::Windows => {
for (c, _) in text.chars() {
match c {
'\n' => try!(write!(writer, "\r\n")),
'\r' => continue,
c => try!(write!(writer, "{}", c)),
}
}
Ok(())
}
NewlineStyle::Native => unreachable!(),
let style = if config.newline_style == NewlineStyle::Native {
if cfg!(windows) {
NewlineStyle::Windows
} else {
NewlineStyle::Unix
}
} else {
config.newline_style
};
match style {
NewlineStyle::Unix => write!(writer, "{}", text),
NewlineStyle::Windows => {
for (c, _) in text.chars() {
match c {
'\n' => try!(write!(writer, "\r\n")),
'\r' => continue,
c => try!(write!(writer, "{}", c)),
}
}
Ok(())
}
NewlineStyle::Native => unreachable!(),
}
}
pub fn write_file<T>(text: &StringBuffer,
filename: &str,
out: &mut T,
config: &Config)
-> Result<Option<String>, io::Error>
where T: Write
{
fn source_and_formatted_text(text: &StringBuffer,
filename: &str,
@ -98,7 +101,15 @@ pub fn write_file(text: &StringBuffer,
Ok((ori_text, fmt_text))
}
match mode {
fn create_diff(filename: &str,
text: &StringBuffer,
config: &Config)
-> Result<Vec<Mismatch>, io::Error> {
let (ori, fmt) = try!(source_and_formatted_text(text, filename, config));
Ok(make_diff(&ori, &fmt, 3))
}
match config.write_mode {
WriteMode::Replace => {
if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
if fmt != ori {
@ -123,11 +134,6 @@ pub fn write_file(text: &StringBuffer,
let file = try!(File::create(filename));
try!(write_system_newlines(file, text, config));
}
WriteMode::NewFile(extn) => {
let filename = filename.to_owned() + "." + extn;
let file = try!(File::create(&filename));
try!(write_system_newlines(file, text, config));
}
WriteMode::Plain => {
let stdout = stdout();
let stdout = stdout.lock();
@ -146,13 +152,9 @@ pub fn write_file(text: &StringBuffer,
|line_num| format!("\nDiff at line {}:", line_num));
}
}
WriteMode::Return => {
// io::Write is not implemented for String, working around with
// Vec<u8>
let mut v = Vec::new();
try!(write_system_newlines(&mut v, text, config));
// won't panic, we are writing correct utf8
return Ok(Some(String::from_utf8(v).unwrap()));
WriteMode::Checkstyle => {
let diff = try!(create_diff(filename, text, config));
try!(output_checkstyle_file(out, filename, diff));
}
}

View File

@ -51,7 +51,7 @@ impl Rewrite for ast::ViewPath {
}
fn rewrite_single_use_list(path_str: String, vpi: &ast::PathListItem) -> String {
let path_item_str = if let ast::PathListItem_::PathListIdent{ name, .. } = vpi.node {
let path_item_str = if let ast::PathListItemKind::Ident { name, .. } = vpi.node {
// A name.
if path_str.is_empty() {
name.to_string()
@ -74,8 +74,8 @@ fn rewrite_single_use_list(path_str: String, vpi: &ast::PathListItem) -> String
fn rewrite_path_item(vpi: &&ast::PathListItem) -> Option<String> {
let path_item_str = match vpi.node {
ast::PathListItem_::PathListIdent{ name, .. } => name.to_string(),
ast::PathListItem_::PathListMod{ .. } => "self".to_owned(),
ast::PathListItemKind::Ident { name, .. } => name.to_string(),
ast::PathListItemKind::Mod { .. } => "self".to_owned(),
};
Some(append_alias(path_item_str, vpi))
@ -83,8 +83,8 @@ fn rewrite_path_item(vpi: &&ast::PathListItem) -> Option<String> {
fn append_alias(path_item_str: String, vpi: &ast::PathListItem) -> String {
match vpi.node {
ast::PathListItem_::PathListIdent{ rename: Some(rename), .. } |
ast::PathListItem_::PathListMod{ rename: Some(rename), .. } => {
ast::PathListItemKind::Ident { rename: Some(rename), .. } |
ast::PathListItemKind::Mod { rename: Some(rename), .. } => {
format!("{} as {}", path_item_str, rename)
}
_ => path_item_str,

View File

@ -15,8 +15,8 @@
use std::fmt;
pub use config::ReportTactic;
static TO_DO_CHARS: &'static [char] = &['T', 'O', 'D', 'O'];
static FIX_ME_CHARS: &'static [char] = &['F', 'I', 'X', 'M', 'E'];
const TO_DO_CHARS: &'static [char] = &['T', 'O', 'D', 'O'];
const FIX_ME_CHARS: &'static [char] = &['F', 'I', 'X', 'M', 'E'];
// Enabled implementation detail is here because it is
// irrelevant outside the issues module

View File

@ -21,10 +21,12 @@ use comment::{FindUncommented, contains_comment};
use visitor::FmtVisitor;
use rewrite::{Rewrite, RewriteContext};
use config::{Config, BlockIndentStyle, Density, ReturnIndent, BraceStyle, StructLitStyle};
use syntax::codemap;
use syntax::{ast, abi};
use syntax::codemap::{Span, BytePos, mk_sp};
use syntax::parse::token;
use syntax::ast::ImplItem;
// Statements of the form
// let pat: ty = init;
@ -107,7 +109,7 @@ impl<'a> FmtVisitor<'a> {
let span = mk_sp(item.span.lo, item.span.hi - BytePos(1));
match item.node {
ast::ForeignItem_::ForeignItemFn(ref fn_decl, ref generics) => {
ast::ForeignItemKind::Fn(ref fn_decl, ref generics) => {
let indent = self.block_indent;
let rewrite = rewrite_fn_base(&self.get_context(),
indent,
@ -133,7 +135,7 @@ impl<'a> FmtVisitor<'a> {
None => self.format_missing(item.span.hi),
}
}
ast::ForeignItem_::ForeignItemStatic(ref ty, is_mutable) => {
ast::ForeignItemKind::Static(ref ty, is_mutable) => {
// FIXME(#21): we're dropping potential comments in between the
// function keywords here.
let mut_str = if is_mutable {
@ -180,6 +182,10 @@ impl<'a> FmtVisitor<'a> {
let mut newline_brace = newline_for_brace(self.config, &generics.where_clause);
let context = self.get_context();
let block_snippet = self.snippet(codemap::mk_sp(block.span.lo, block.span.hi));
let has_body = !block_snippet[1..block_snippet.len() - 1].trim().is_empty() ||
!context.config.fn_empty_single_line;
let (mut result, force_newline_brace) = try_opt!(rewrite_fn_base(&context,
indent,
ident,
@ -192,7 +198,7 @@ impl<'a> FmtVisitor<'a> {
vis,
span,
newline_brace,
true));
has_body));
if self.config.fn_brace_style != BraceStyle::AlwaysNextLine && !result.contains('\n') {
newline_brace = false;
@ -434,12 +440,12 @@ impl<'a> FmtVisitor<'a> {
}
pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -> Option<String> {
if let ast::Item_::ItemImpl(unsafety,
polarity,
ref generics,
ref trait_ref,
ref self_ty,
ref items) = item.node {
if let ast::ItemKind::Impl(unsafety,
polarity,
ref generics,
ref trait_ref,
ref self_ty,
ref items) = item.node {
let mut result = String::new();
result.push_str(format_visibility(item.vis));
result.push_str(format_unsafety(unsafety));
@ -484,9 +490,20 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
where_budget,
context.config.where_density,
"{",
true,
None));
if !where_clause_str.contains('\n') &&
result.len() + where_clause_str.len() + offset.width() > context.config.max_width {
if try_opt!(is_impl_single_line(context, &items, &result, &where_clause_str, &item)) {
result.push_str(&where_clause_str);
if where_clause_str.contains('\n') {
result.push_str("\n{\n}");
} else {
result.push_str(" {}");
}
return Some(result);
}
if !where_clause_str.is_empty() && !where_clause_str.contains('\n') {
result.push('\n');
let width = context.block_indent.width() + context.config.tab_spaces - 1;
let where_indent = Indent::new(0, width);
@ -505,13 +522,14 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
}
}
}
result.push('{');
let snippet = context.snippet(item.span);
let open_pos = try_opt!(snippet.find_uncommented("{")) + 1;
if !items.is_empty() || contains_comment(&snippet[open_pos..]) {
let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config, None);
let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
visitor.block_indent = context.block_indent.block_indent(context.config);
visitor.last_pos = item.span.lo + BytePos(open_pos as u32);
@ -531,13 +549,31 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) -
result.push_str(&outer_indent_str);
}
if result.chars().last().unwrap() == '{' {
result.push('\n');
}
result.push('}');
Some(result)
} else {
unreachable!();
}
}
fn is_impl_single_line(context: &RewriteContext,
items: &Vec<ImplItem>,
result: &str,
where_clause_str: &str,
item: &ast::Item)
-> Option<bool> {
let snippet = context.snippet(item.span);
let open_pos = try_opt!(snippet.find_uncommented("{")) + 1;
Some(context.config.impl_empty_single_line && items.is_empty() &&
result.len() + where_clause_str.len() <= context.config.max_width &&
!contains_comment(&snippet[open_pos..]))
}
pub fn format_struct(context: &RewriteContext,
item_name: &str,
ident: ast::Ident,
@ -573,7 +609,7 @@ pub fn format_struct(context: &RewriteContext,
}
pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent) -> Option<String> {
if let ast::Item_::ItemTrait(unsafety, ref generics, ref type_param_bounds, ref trait_items) =
if let ast::ItemKind::Trait(unsafety, ref generics, ref type_param_bounds, ref trait_items) =
item.node {
let mut result = String::new();
let header = format!("{}{}trait {}",
@ -619,6 +655,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
where_budget,
context.config.where_density,
"{",
context.config.where_trailing_comma,
None));
if !where_clause_str.contains('\n') &&
result.len() + where_clause_str.len() + offset.width() > context.config.max_width {
@ -650,7 +687,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
let open_pos = try_opt!(snippet.find_uncommented("{")) + 1;
if !trait_items.is_empty() || contains_comment(&snippet[open_pos..]) {
let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config, None);
let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config);
visitor.block_indent = context.block_indent.block_indent(context.config);
visitor.last_pos = item.span.lo + BytePos(open_pos as u32);
@ -732,7 +769,7 @@ fn format_struct_struct(context: &RewriteContext,
}
let item_indent = offset.block_indent(context.config);
// 2 = ","
// 1 = ","
let item_budget = try_opt!(context.config.max_width.checked_sub(item_indent.width() + 1));
let items = itemize_list(context.codemap,
@ -806,6 +843,7 @@ fn format_tuple_struct(context: &RewriteContext,
where_budget,
Density::Compressed,
";",
false,
None))
}
None => "".to_owned(),
@ -887,6 +925,7 @@ pub fn rewrite_type_alias(context: &RewriteContext,
where_budget,
context.config.where_density,
"=",
false,
Some(span.hi)));
result.push_str(&where_clause_str);
result.push_str(" = ");
@ -985,15 +1024,15 @@ pub fn rewrite_static(prefix: &str,
impl Rewrite for ast::FunctionRetTy {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
match *self {
ast::FunctionRetTy::DefaultReturn(_) => Some(String::new()),
ast::FunctionRetTy::NoReturn(_) => {
ast::FunctionRetTy::Default(_) => Some(String::new()),
ast::FunctionRetTy::None(_) => {
if width >= 4 {
Some("-> !".to_owned())
} else {
None
}
}
ast::FunctionRetTy::Return(ref ty) => {
ast::FunctionRetTy::Ty(ref ty) => {
let inner_width = try_opt!(width.checked_sub(3));
ty.rewrite(context, inner_width, offset + 3).map(|r| format!("-> {}", r))
}
@ -1006,7 +1045,7 @@ impl Rewrite for ast::Arg {
if is_named_arg(self) {
let mut result = try_opt!(self.pat.rewrite(context, width, offset));
if self.ty.node != ast::Ty_::TyInfer {
if self.ty.node != ast::TyKind::Infer {
result.push_str(": ");
let max_width = try_opt!(width.checked_sub(result.len()));
let ty_str = try_opt!(self.ty.rewrite(context, max_width, offset + result.len()));
@ -1025,7 +1064,7 @@ fn rewrite_explicit_self(explicit_self: &ast::ExplicitSelf,
context: &RewriteContext)
-> Option<String> {
match explicit_self.node {
ast::ExplicitSelf_::SelfRegion(lt, m, _) => {
ast::SelfKind::Region(lt, m, _) => {
let mut_str = format_mutability(m);
match lt {
Some(ref l) => {
@ -1037,7 +1076,7 @@ fn rewrite_explicit_self(explicit_self: &ast::ExplicitSelf,
None => Some(format!("&{}self", mut_str)),
}
}
ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
ast::SelfKind::Explicit(ref ty, _) => {
assert!(!args.is_empty(), "&[ast::Arg] shouldn't be empty.");
let mutability = explicit_self_mutability(&args[0]);
@ -1045,7 +1084,7 @@ fn rewrite_explicit_self(explicit_self: &ast::ExplicitSelf,
Some(format!("{}self: {}", format_mutability(mutability), type_str))
}
ast::ExplicitSelf_::SelfValue(_) => {
ast::SelfKind::Value(_) => {
assert!(!args.is_empty(), "&[ast::Arg] shouldn't be empty.");
let mutability = explicit_self_mutability(&args[0]);
@ -1059,7 +1098,7 @@ fn rewrite_explicit_self(explicit_self: &ast::ExplicitSelf,
// Hacky solution caused by absence of `Mutability` in `SelfValue` and
// `SelfExplicit` variants of `ast::ExplicitSelf_`.
fn explicit_self_mutability(arg: &ast::Arg) -> ast::Mutability {
if let ast::Pat_::PatIdent(ast::BindingMode::BindByValue(mutability), _, _) = arg.pat.node {
if let ast::PatKind::Ident(ast::BindingMode::ByValue(mutability), _, _) = arg.pat.node {
mutability
} else {
unreachable!()
@ -1076,13 +1115,13 @@ pub fn span_lo_for_arg(arg: &ast::Arg) -> BytePos {
pub fn span_hi_for_arg(arg: &ast::Arg) -> BytePos {
match arg.ty.node {
ast::Ty_::TyInfer if is_named_arg(arg) => arg.pat.span.hi,
ast::TyKind::Infer if is_named_arg(arg) => arg.pat.span.hi,
_ => arg.ty.span.hi,
}
}
pub fn is_named_arg(arg: &ast::Arg) -> bool {
if let ast::Pat_::PatIdent(_, ident, _) = arg.pat.node {
if let ast::PatKind::Ident(_, ident, _) = arg.pat.node {
ident.node != token::special_idents::invalid
} else {
true
@ -1091,9 +1130,9 @@ pub fn is_named_arg(arg: &ast::Arg) -> bool {
fn span_for_return(ret: &ast::FunctionRetTy) -> Span {
match *ret {
ast::FunctionRetTy::NoReturn(ref span) |
ast::FunctionRetTy::DefaultReturn(ref span) => span.clone(),
ast::FunctionRetTy::Return(ref ty) => ty.span,
ast::FunctionRetTy::None(ref span) |
ast::FunctionRetTy::Default(ref span) => span.clone(),
ast::FunctionRetTy::Ty(ref ty) => ty.span,
}
}
@ -1145,13 +1184,14 @@ fn rewrite_fn_base(context: &RewriteContext,
let mut result = String::with_capacity(1024);
// Vis unsafety abi.
result.push_str(format_visibility(vis));
result.push_str(::utils::format_unsafety(unsafety));
if let ast::Constness::Const = constness {
result.push_str("const ");
}
if abi != abi::Rust {
result.push_str(::utils::format_unsafety(unsafety));
if abi != abi::Abi::Rust {
result.push_str(&::utils::format_abi(abi));
}
@ -1301,7 +1341,7 @@ fn rewrite_fn_base(context: &RewriteContext,
(context.config.fn_args_layout == StructLitStyle::Block &&
ret_str.is_empty()) ||
(context.config.where_density == Density::CompressedIfEmpty &&
!has_body) {
!has_body && !result.contains('\n')) {
Density::Compressed
} else {
Density::Tall
@ -1317,7 +1357,14 @@ fn rewrite_fn_base(context: &RewriteContext,
where_budget,
where_density,
"{",
has_body,
Some(span.hi)));
if last_line_width(&result) + where_clause_str.len() > context.config.max_width &&
!where_clause_str.contains('\n') {
result.push('\n');
}
result.push_str(&where_clause_str);
Some((result, force_new_line_for_brace))
@ -1559,7 +1606,7 @@ fn rewrite_trait_bounds(context: &RewriteContext,
.join(" + ");
let mut result = String::new();
result.push_str(" : ");
result.push_str(": ");
result.push_str(&bound_str);
Some(result)
}
@ -1572,6 +1619,7 @@ fn rewrite_where_clause(context: &RewriteContext,
width: usize,
density: Density,
terminator: &str,
allow_trailing_comma: bool,
span_end: Option<BytePos>)
-> Option<String> {
if where_clause.predicates.is_empty() {
@ -1611,11 +1659,12 @@ fn rewrite_where_clause(context: &RewriteContext,
// FIXME: we don't need to collect here if the where_layout isn't
// HorizontalVertical.
let tactic = definitive_tactic(&item_vec, context.config.where_layout, budget);
let use_trailing_comma = allow_trailing_comma && context.config.where_trailing_comma;
let fmt = ListFormatting {
tactic: tactic,
separator: ",",
trailing_separator: SeparatorTactic::Never,
trailing_separator: SeparatorTactic::from_bool(use_trailing_comma),
indent: offset,
width: budget,
ends_with_newline: true,
@ -1677,6 +1726,7 @@ fn format_generics(context: &RewriteContext,
budget,
Density::Tall,
terminator,
true,
Some(span.hi)));
result.push_str(&where_clause_str);
if !force_same_line_brace &&

View File

@ -26,15 +26,17 @@ extern crate diff;
extern crate term;
use syntax::ast;
use syntax::codemap::{mk_sp, Span};
use syntax::diagnostic::{EmitterWriter, Handler};
use syntax::codemap::{mk_sp, CodeMap, Span};
use syntax::errors::Handler;
use syntax::errors::emitter::{ColorConfig, EmitterWriter};
use syntax::parse::{self, ParseSess};
use std::io::stdout;
use std::ops::{Add, Sub};
use std::path::Path;
use std::rc::Rc;
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
use issues::{BadIssueSeeker, Issue};
use filemap::FileMap;
@ -46,6 +48,7 @@ mod utils;
pub mod config;
pub mod filemap;
mod visitor;
mod checkstyle;
mod items;
mod missed_spans;
mod lists;
@ -187,42 +190,6 @@ impl Sub<usize> for Indent {
}
}
#[derive(Copy, Clone)]
pub enum WriteMode {
// Backsup the original file and overwrites the orignal.
Replace,
// Overwrites original file without backup.
Overwrite,
// str is the extension of the new file.
NewFile(&'static str),
// Write the output to stdout.
Display,
// Write the diff to stdout.
Diff,
// Return the result as a mapping from filenames to Strings.
Return,
// Display how much of the input file was processed
Coverage,
// Unfancy stdout
Plain,
}
impl FromStr for WriteMode {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"replace" => Ok(WriteMode::Replace),
"display" => Ok(WriteMode::Display),
"overwrite" => Ok(WriteMode::Overwrite),
"diff" => Ok(WriteMode::Diff),
"coverage" => Ok(WriteMode::Coverage),
"plain" => Ok(WriteMode::Plain),
_ => Err(()),
}
}
}
pub enum ErrorKind {
// Line has exceeded character limit
LineOverflow,
@ -299,8 +266,7 @@ impl fmt::Display for FormatReport {
fn fmt_ast(krate: &ast::Crate,
parse_session: &ParseSess,
main_file: &Path,
config: &Config,
mode: WriteMode)
config: &Config)
-> FileMap {
let mut file_map = FileMap::new();
for (path, module) in modules::list_files(krate, parse_session.codemap()) {
@ -311,7 +277,7 @@ fn fmt_ast(krate: &ast::Crate,
if config.verbose {
println!("Formatting {}", path);
}
let mut visitor = FmtVisitor::from_codemap(parse_session, config, Some(mode));
let mut visitor = FmtVisitor::from_codemap(parse_session, config);
visitor.format_separate_mod(module);
file_map.insert(path.to_owned(), visitor.buffer);
}
@ -401,24 +367,32 @@ pub fn fmt_lines(file_map: &mut FileMap, config: &Config) -> FormatReport {
report
}
pub fn format_string(input: String, config: &Config, mode: WriteMode) -> FileMap {
pub fn format_string(input: String, config: &Config) -> FileMap {
let path = "stdin";
let mut parse_session = ParseSess::new();
let codemap = Rc::new(CodeMap::new());
let tty_handler = Handler::with_tty_emitter(ColorConfig::Auto,
None,
true,
false,
codemap.clone());
let mut parse_session = ParseSess::with_span_handler(tty_handler, codemap.clone());
let krate = parse::parse_crate_from_source_str(path.to_owned(),
input,
Vec::new(),
&parse_session);
// Suppress error output after parsing.
let emitter = Box::new(EmitterWriter::new(Box::new(Vec::new()), None));
parse_session.span_diagnostic.handler = Handler::with_emitter(false, emitter);
let silent_emitter = Box::new(EmitterWriter::new(Box::new(Vec::new()), None, codemap.clone()));
parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter);
// FIXME: we still use a FileMap even though we only have
// one file, because fmt_lines requires a FileMap
let mut file_map = FileMap::new();
// do the actual formatting
let mut visitor = FmtVisitor::from_codemap(&parse_session, config, Some(mode));
let mut visitor = FmtVisitor::from_codemap(&parse_session, config);
visitor.format_separate_mod(&krate.module);
// append final newline
@ -428,15 +402,23 @@ pub fn format_string(input: String, config: &Config, mode: WriteMode) -> FileMap
file_map
}
pub fn format(file: &Path, config: &Config, mode: WriteMode) -> FileMap {
let mut parse_session = ParseSess::new();
pub fn format(file: &Path, config: &Config) -> FileMap {
let codemap = Rc::new(CodeMap::new());
let tty_handler = Handler::with_tty_emitter(ColorConfig::Auto,
None,
true,
false,
codemap.clone());
let mut parse_session = ParseSess::with_span_handler(tty_handler, codemap.clone());
let krate = parse::parse_crate_from_file(file, Vec::new(), &parse_session);
// Suppress error output after parsing.
let emitter = Box::new(EmitterWriter::new(Box::new(Vec::new()), None));
parse_session.span_diagnostic.handler = Handler::with_emitter(false, emitter);
let silent_emitter = Box::new(EmitterWriter::new(Box::new(Vec::new()), None, codemap.clone()));
parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter);
let mut file_map = fmt_ast(&krate, &parse_session, file, config, mode);
let mut file_map = fmt_ast(&krate, &parse_session, file, config);
// For some reason, the codemap does not include terminating
// newlines so we must add one on for each file. This is sad.
@ -445,16 +427,12 @@ pub fn format(file: &Path, config: &Config, mode: WriteMode) -> FileMap {
file_map
}
// args are the arguments passed on the command line, generally passed through
// to the compiler.
// write_mode determines what happens to the result of running rustfmt, see
// WriteMode.
pub fn run(file: &Path, write_mode: WriteMode, config: &Config) {
let mut result = format(file, config, write_mode);
pub fn run(file: &Path, config: &Config) {
let mut result = format(file, config);
print!("{}", fmt_lines(&mut result, config));
let write_result = filemap::write_all_files(&result, write_mode, config);
let out = stdout();
let write_result = filemap::write_all_files(&result, out, config);
if let Err(msg) = write_result {
println!("Error writing files: {}", msg);
@ -462,11 +440,12 @@ pub fn run(file: &Path, write_mode: WriteMode, config: &Config) {
}
// Similar to run, but takes an input String instead of a file to format
pub fn run_from_stdin(input: String, mode: WriteMode, config: &Config) {
let mut result = format_string(input, config, mode);
pub fn run_from_stdin(input: String, config: &Config) {
let mut result = format_string(input, config);
fmt_lines(&mut result, config);
let write_result = filemap::write_file(&result["stdin"], "stdin", mode, config);
let mut out = stdout();
let write_result = filemap::write_file(&result["stdin"], "stdin", &mut out, config);
if let Err(msg) = write_result {
panic!("Error writing to stdout: {}", msg);

View File

@ -30,10 +30,10 @@ use expr::{rewrite_call, rewrite_array};
use comment::FindUncommented;
use utils::{wrap_str, span_after};
static FORCED_BRACKET_MACROS: &'static [&'static str] = &["vec!"];
const FORCED_BRACKET_MACROS: &'static [&'static str] = &["vec!"];
// FIXME: use the enum from libsyntax?
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
enum MacroStyle {
Parens,
Brackets,
@ -63,9 +63,7 @@ pub fn rewrite_macro(mac: &ast::Mac,
original_style
};
if let MacroStyle::Braces = style {
return None;
} else if mac.node.tts.is_empty() {
if mac.node.tts.is_empty() {
return if let MacroStyle::Parens = style {
Some(format!("{}()", macro_name))
} else {
@ -76,22 +74,24 @@ pub fn rewrite_macro(mac: &ast::Mac,
let mut parser = tts_to_parser(context.parse_session, mac.node.tts.clone(), Vec::new());
let mut expr_vec = Vec::new();
loop {
expr_vec.push(match parser.parse_expr() {
Ok(expr) => expr,
Err(..) => return None,
});
if MacroStyle::Braces != style {
loop {
expr_vec.push(match parser.parse_expr() {
Ok(expr) => expr,
Err(..) => return None,
});
match parser.token {
Token::Eof => break,
Token::Comma => (),
_ => return None,
}
match parser.token {
Token::Eof => break,
Token::Comma => (),
_ => return None,
}
let _ = parser.bump();
let _ = parser.bump();
if parser.token == Token::Eof {
return None;
if parser.token == Token::Eof {
return None;
}
}
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use WriteMode;
use config::WriteMode;
use visitor::FmtVisitor;
use syntax::codemap::{self, BytePos, Span, Pos};
use comment::{CodeCharKind, CommentCodeSlices, rewrite_comment};
@ -103,14 +103,9 @@ impl<'a> FmtVisitor<'a> {
.collect()
}
let replaced = match self.write_mode {
Some(mode) => {
match mode {
WriteMode::Coverage => replace_chars(old_snippet),
_ => old_snippet.to_owned(),
}
}
None => old_snippet.to_owned(),
let replaced = match self.config.write_mode {
WriteMode::Coverage => replace_chars(old_snippet),
_ => old_snippet.to_owned(),
};
let snippet = &*replaced;
@ -147,11 +142,11 @@ impl<'a> FmtVisitor<'a> {
if let Some('/') = subslice.chars().skip(1).next() {
// Add a newline after line comments
self.buffer.push_str("\n");
} else if line_start < snippet.len() {
} else if line_start <= snippet.len() {
// For other comments add a newline if there isn't one at the end already
let c = snippet[line_start..].chars().next().unwrap();
if c != '\n' && c != '\r' {
self.buffer.push_str("\n");
match snippet[line_start..].chars().next() {
Some('\n') | Some('\r') => (),
_ => self.buffer.push_str("\n"),
}
}

View File

@ -40,7 +40,7 @@ fn list_submodules<'a>(module: &'a ast::Mod,
result: &mut HashMap<PathBuf, &'a ast::Mod>) {
debug!("list_submodules: search_dir: {:?}", search_dir);
for item in &module.items {
if let ast::ItemMod(ref sub_mod) = item.node {
if let ast::ItemKind::Mod(ref sub_mod) = item.node {
if !utils::contains_skip(&item.attrs) {
let is_internal = codemap.span_to_filename(item.span) ==
codemap.span_to_filename(sub_mod.inner);

View File

@ -15,80 +15,160 @@ use lists::{format_item_list, itemize_list};
use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple};
use types::rewrite_path;
use syntax::ast::{BindingMode, Pat, Pat_};
use syntax::ast::{BindingMode, Pat, PatKind, FieldPat};
// FIXME(#18): implement pattern formatting.
impl Rewrite for Pat {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
match self.node {
Pat_::PatBox(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, width, offset),
Pat_::PatIdent(binding_mode, ident, None) => {
PatKind::Box(ref pat) => rewrite_unary_prefix(context, "box ", &**pat, width, offset),
PatKind::Ident(binding_mode, ident, ref sub_pat) => {
let (prefix, mutability) = match binding_mode {
BindingMode::BindByRef(mutability) => ("ref ", mutability),
BindingMode::BindByValue(mutability) => ("", mutability),
BindingMode::ByRef(mutability) => ("ref ", mutability),
BindingMode::ByValue(mutability) => ("", mutability),
};
let mut_infix = format_mutability(mutability);
let result = format!("{}{}{}", prefix, mut_infix, ident.node);
let id_str = ident.node.to_string();
let sub_pat = match *sub_pat {
Some(ref p) => {
// 3 - ` @ `.
let width = try_opt!(width.checked_sub(prefix.len() + mut_infix.len() +
id_str.len() +
3));
format!(" @ {}", try_opt!(p.rewrite(context, width, offset)))
}
None => "".to_owned(),
};
let result = format!("{}{}{}{}", prefix, mut_infix, id_str, sub_pat);
wrap_str(result, context.config.max_width, width, offset)
}
Pat_::PatWild => {
PatKind::Wild => {
if 1 <= width {
Some("_".to_owned())
} else {
None
}
}
Pat_::PatQPath(ref q_self, ref path) => {
PatKind::QPath(ref q_self, ref path) => {
rewrite_path(context, true, Some(q_self), path, width, offset)
}
Pat_::PatRange(ref lhs, ref rhs) => {
PatKind::Range(ref lhs, ref rhs) => {
rewrite_pair(&**lhs, &**rhs, "", "...", "", context, width, offset)
}
Pat_::PatRegion(ref pat, mutability) => {
PatKind::Ref(ref pat, mutability) => {
let prefix = format!("&{}", format_mutability(mutability));
rewrite_unary_prefix(context, &prefix, &**pat, width, offset)
}
Pat_::PatTup(ref items) => {
PatKind::Tup(ref items) => {
rewrite_tuple(context,
items.iter().map(|x| &**x),
self.span,
width,
offset)
}
Pat_::PatEnum(ref path, Some(ref pat_vec)) => {
let path_str = try_opt!(::types::rewrite_path(context,
true,
None,
path,
width,
offset));
PatKind::Path(ref path) => rewrite_path(context, true, None, path, width, offset),
PatKind::TupleStruct(ref path, ref pat_vec) => {
let path_str = try_opt!(rewrite_path(context, true, None, path, width, offset));
if pat_vec.is_empty() {
Some(path_str)
} else {
// 1 = (
let width = try_opt!(width.checked_sub(path_str.len() + 1));
let offset = offset + path_str.len() + 1;
let items = itemize_list(context.codemap,
pat_vec.iter(),
")",
|item| item.span.lo,
|item| item.span.hi,
|item| item.rewrite(context, width, offset),
span_after(self.span, "(", context.codemap),
self.span.hi);
Some(format!("{}({})",
path_str,
try_opt!(format_item_list(items, width, offset, context.config))))
match *pat_vec {
Some(ref pat_vec) => {
if pat_vec.is_empty() {
Some(path_str)
} else {
// 1 = (
let width = try_opt!(width.checked_sub(path_str.len() + 1));
let offset = offset + path_str.len() + 1;
let items = itemize_list(context.codemap,
pat_vec.iter(),
")",
|item| item.span.lo,
|item| item.span.hi,
|item| item.rewrite(context, width, offset),
span_after(self.span, "(", context.codemap),
self.span.hi);
Some(format!("{}({})",
path_str,
try_opt!(format_item_list(items,
width,
offset,
context.config))))
}
}
None => Some(format!("{}(..)", path_str)),
}
}
Pat_::PatLit(ref expr) => expr.rewrite(context, width, offset),
// FIXME(#8): format remaining pattern variants.
Pat_::PatIdent(_, _, Some(..)) |
Pat_::PatEnum(_, None) |
Pat_::PatStruct(..) |
Pat_::PatVec(..) |
Pat_::PatMac(..) => {
PatKind::Lit(ref expr) => expr.rewrite(context, width, offset),
PatKind::Vec(ref prefix, ref slice_pat, ref suffix) => {
// Rewrite all the sub-patterns.
let prefix = prefix.iter().map(|p| p.rewrite(context, width, offset));
let slice_pat = slice_pat.as_ref().map(|p| {
Some(format!("{}..", try_opt!(p.rewrite(context, width, offset))))
});
let suffix = suffix.iter().map(|p| p.rewrite(context, width, offset));
// Munge them together.
let pats: Option<Vec<String>> = prefix.chain(slice_pat.into_iter())
.chain(suffix)
.collect();
// Check that all the rewrites succeeded, and if not return None.
let pats = try_opt!(pats);
// Unwrap all the sub-strings and join them with commas.
let result = format!("[{}]", pats.join(", "));
wrap_str(result, context.config.max_width, width, offset)
}
PatKind::Struct(ref path, ref fields, elipses) => {
let path = try_opt!(rewrite_path(context, true, None, path, width, offset));
let (elipses_str, terminator) = if elipses {
(", ..", "..")
} else {
("", "}")
};
// 5 = `{` plus space before and after plus `}` plus space before.
let budget = try_opt!(width.checked_sub(path.len() + 5 + elipses_str.len()));
// FIXME Using visual indenting, should use block or visual to match
// struct lit preference (however, in practice I think it is rare
// for struct patterns to be multi-line).
// 3 = `{` plus space before and after.
let offset = offset + path.len() + 3;
let items = itemize_list(context.codemap,
fields.iter(),
terminator,
|f| f.span.lo,
|f| f.span.hi,
|f| f.node.rewrite(context, budget, offset),
span_after(self.span, "{", context.codemap),
self.span.hi);
let mut field_string = try_opt!(format_item_list(items,
budget,
offset,
context.config));
if elipses {
if field_string.contains('\n') {
field_string.push_str(",\n");
field_string.push_str(&offset.to_string(context.config));
field_string.push_str("..");
} else {
if field_string.len() > 0 {
field_string.push_str(", ");
}
field_string.push_str("..");
}
}
if field_string.is_empty() {
Some(format!("{} {{}}", path))
} else {
Some(format!("{} {{ {} }}", path, field_string))
}
}
// FIXME(#819) format pattern macros.
PatKind::Mac(..) => {
wrap_str(context.snippet(self.span),
context.config.max_width,
width,
@ -97,3 +177,17 @@ impl Rewrite for Pat {
}
}
}
impl Rewrite for FieldPat {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
let pat = self.pat.rewrite(context, width, offset);
if self.is_shorthand {
pat
} else {
wrap_str(format!("{}: {}", self.ident.to_string(), try_opt!(pat)),
context.config.max_width,
width,
offset)
}
}
}

View File

@ -21,6 +21,7 @@ use lists::{format_item_list, itemize_list, format_fn_args};
use rewrite::{Rewrite, RewriteContext};
use utils::{extra_offset, span_after, format_mutability, wrap_str};
use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple};
use config::TypeDensity;
// Does not wrap on simple segments.
pub fn rewrite_path(context: &RewriteContext,
@ -52,7 +53,7 @@ pub fn rewrite_path(context: &RewriteContext,
// 3 = ">::".len()
let budget = try_opt!(width.checked_sub(extra_offset + 3));
result = try_opt!(rewrite_path_segments(expr_context,
result = try_opt!(rewrite_path_segments(false,
result,
path.segments.iter().take(skip_count),
span_lo,
@ -171,9 +172,9 @@ fn rewrite_segment(expr_context: bool,
let offset = offset + ident_len;
let params = match segment.parameters {
ast::PathParameters::AngleBracketedParameters(ref data) if !data.lifetimes.is_empty() ||
!data.types.is_empty() ||
!data.bindings.is_empty() => {
ast::PathParameters::AngleBracketed(ref data) if !data.lifetimes.is_empty() ||
!data.types.is_empty() ||
!data.bindings.is_empty() => {
let param_list = data.lifetimes
.iter()
.map(SegmentParam::LifeTime)
@ -212,10 +213,10 @@ fn rewrite_segment(expr_context: bool,
format!("{}<{}>", separator, list_str)
}
ast::PathParameters::ParenthesizedParameters(ref data) => {
ast::PathParameters::Parenthesized(ref data) => {
let output = match data.output {
Some(ref ty) => FunctionRetTy::Return(ty.clone()),
None => FunctionRetTy::DefaultReturn(codemap::DUMMY_SP),
Some(ref ty) => FunctionRetTy::Ty(ty.clone()),
None => FunctionRetTy::Default(codemap::DUMMY_SP),
};
try_opt!(format_function_type(data.inputs.iter().map(|x| &**x),
&output,
@ -258,13 +259,13 @@ fn format_function_type<'a, I>(inputs: I,
let list_str = try_opt!(format_fn_args(items, budget, offset, context.config));
let output = match *output {
FunctionRetTy::Return(ref ty) => {
FunctionRetTy::Ty(ref ty) => {
let budget = try_opt!(width.checked_sub(4));
let type_str = try_opt!(ty.rewrite(context, budget, offset + 4));
format!(" -> {}", type_str)
}
FunctionRetTy::NoReturn(..) => " -> !".to_owned(),
FunctionRetTy::DefaultReturn(..) => String::new(),
FunctionRetTy::None(..) => " -> !".to_owned(),
FunctionRetTy::Default(..) => String::new(),
};
let infix = if output.len() + list_str.len() > width {
@ -424,7 +425,12 @@ impl Rewrite for ast::TyParam {
result.push_str(&bounds);
}
if let Some(ref def) = self.default {
result.push_str(" = ");
let eq_str = match context.config.type_punctuation_density {
TypeDensity::Compressed => "=",
TypeDensity::Wide => " = ",
};
result.push_str(eq_str);
let budget = try_opt!(width.checked_sub(result.len()));
let rewrite = try_opt!(def.rewrite(context, budget, offset + result.len()));
result.push_str(&rewrite);
@ -464,24 +470,29 @@ impl Rewrite for ast::TraitRef {
impl Rewrite for ast::Ty {
fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option<String> {
match self.node {
ast::TyObjectSum(ref ty, ref bounds) => {
ast::TyKind::ObjectSum(ref ty, ref bounds) => {
let ty_str = try_opt!(ty.rewrite(context, width, offset));
let overhead = ty_str.len() + 3;
Some(format!("{} + {}",
let plus_str = match context.config.type_punctuation_density {
TypeDensity::Compressed => "+",
TypeDensity::Wide => " + ",
};
Some(format!("{}{}{}",
ty_str,
plus_str,
try_opt!(bounds.rewrite(context,
try_opt!(width.checked_sub(overhead)),
offset + overhead))))
}
ast::TyPtr(ref mt) => {
ast::TyKind::Ptr(ref mt) => {
let prefix = match mt.mutbl {
Mutability::MutMutable => "*mut ",
Mutability::MutImmutable => "*const ",
Mutability::Mutable => "*mut ",
Mutability::Immutable => "*const ",
};
rewrite_unary_prefix(context, prefix, &*mt.ty, width, offset)
}
ast::TyRptr(ref lifetime, ref mt) => {
ast::TyKind::Rptr(ref lifetime, ref mt) => {
let mut_str = format_mutability(mt.mutbl);
let mut_len = mut_str.len();
Some(match *lifetime {
@ -509,39 +520,39 @@ impl Rewrite for ast::Ty {
}
// FIXME: we drop any comments here, even though it's a silly place to put
// comments.
ast::TyParen(ref ty) => {
ast::TyKind::Paren(ref ty) => {
let budget = try_opt!(width.checked_sub(2));
ty.rewrite(context, budget, offset + 1).map(|ty_str| format!("({})", ty_str))
}
ast::TyVec(ref ty) => {
ast::TyKind::Vec(ref ty) => {
let budget = try_opt!(width.checked_sub(2));
ty.rewrite(context, budget, offset + 1).map(|ty_str| format!("[{}]", ty_str))
}
ast::TyTup(ref items) => {
ast::TyKind::Tup(ref items) => {
rewrite_tuple(context,
items.iter().map(|x| &**x),
self.span,
width,
offset)
}
ast::TyPolyTraitRef(ref trait_ref) => trait_ref.rewrite(context, width, offset),
ast::TyPath(ref q_self, ref path) => {
ast::TyKind::PolyTraitRef(ref trait_ref) => trait_ref.rewrite(context, width, offset),
ast::TyKind::Path(ref q_self, ref path) => {
rewrite_path(context, false, q_self.as_ref(), path, width, offset)
}
ast::TyFixedLengthVec(ref ty, ref repeats) => {
ast::TyKind::FixedLengthVec(ref ty, ref repeats) => {
rewrite_pair(&**ty, &**repeats, "[", "; ", "]", context, width, offset)
}
ast::TyInfer => {
ast::TyKind::Infer => {
if width >= 1 {
Some("_".to_owned())
} else {
None
}
}
ast::TyBareFn(ref bare_fn) => {
ast::TyKind::BareFn(ref bare_fn) => {
rewrite_bare_fn(bare_fn, self.span, context, width, offset)
}
ast::TyMac(..) | ast::TyTypeof(..) => unreachable!(),
ast::TyKind::Mac(..) | ast::TyKind::Typeof(..) => unreachable!(),
}
}
}
@ -556,7 +567,7 @@ fn rewrite_bare_fn(bare_fn: &ast::BareFnTy,
result.push_str(&::utils::format_unsafety(bare_fn.unsafety));
if bare_fn.abi != abi::Rust {
if bare_fn.abi != abi::Abi::Rust {
result.push_str(&::utils::format_abi(bare_fn.abi));
}

View File

@ -10,7 +10,7 @@
use std::cmp::Ordering;
use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItem_};
use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItemKind};
use syntax::codemap::{CodeMap, Span, BytePos};
use syntax::abi;
@ -38,6 +38,14 @@ pub fn span_after(original: Span, needle: &str, codemap: &CodeMap) -> BytePos {
original.lo + BytePos(offset as u32)
}
#[inline]
pub fn span_before(original: Span, needle: &str, codemap: &CodeMap) -> BytePos {
let snippet = codemap.span_to_snippet(original).unwrap();
let offset = snippet.find_uncommented(needle).unwrap();
original.lo + BytePos(offset as u32)
}
#[inline]
pub fn span_after_last(original: Span, needle: &str, codemap: &CodeMap) -> BytePos {
let snippet = codemap.span_to_snippet(original).unwrap();
@ -69,8 +77,8 @@ pub fn format_unsafety(unsafety: ast::Unsafety) -> &'static str {
#[inline]
pub fn format_mutability(mutability: ast::Mutability) -> &'static str {
match mutability {
ast::Mutability::MutMutable => "mut ",
ast::Mutability::MutImmutable => "",
ast::Mutability::Mutable => "mut ",
ast::Mutability::Immutable => "",
}
}
@ -101,8 +109,8 @@ pub fn last_line_width(s: &str) -> usize {
#[inline]
fn is_skip(meta_item: &MetaItem) -> bool {
match meta_item.node {
MetaItem_::MetaWord(ref s) => *s == SKIP_ANNOTATION,
MetaItem_::MetaList(ref s, ref l) => *s == "cfg_attr" && l.len() == 2 && is_skip(&l[1]),
MetaItemKind::Word(ref s) => *s == SKIP_ANNOTATION,
MetaItemKind::List(ref s, ref l) => *s == "cfg_attr" && l.len() == 2 && is_skip(&l[1]),
_ => false,
}
}
@ -129,9 +137,9 @@ pub fn end_typaram(typaram: &ast::TyParam) -> BytePos {
#[inline]
pub fn semicolon_for_expr(expr: &ast::Expr) -> bool {
match expr.node {
ast::Expr_::ExprRet(..) |
ast::Expr_::ExprAgain(..) |
ast::Expr_::ExprBreak(..) => true,
ast::ExprKind::Ret(..) |
ast::ExprKind::Again(..) |
ast::ExprKind::Break(..) => true,
_ => false,
}
}
@ -139,16 +147,16 @@ pub fn semicolon_for_expr(expr: &ast::Expr) -> bool {
#[inline]
pub fn semicolon_for_stmt(stmt: &ast::Stmt) -> bool {
match stmt.node {
ast::Stmt_::StmtSemi(ref expr, _) => {
ast::StmtKind::Semi(ref expr, _) => {
match expr.node {
ast::Expr_::ExprWhile(..) |
ast::Expr_::ExprWhileLet(..) |
ast::Expr_::ExprLoop(..) |
ast::Expr_::ExprForLoop(..) => false,
ast::ExprKind::While(..) |
ast::ExprKind::WhileLet(..) |
ast::ExprKind::Loop(..) |
ast::ExprKind::ForLoop(..) => false,
_ => true,
}
}
ast::Stmt_::StmtExpr(..) => false,
ast::StmtKind::Expr(..) => false,
_ => true,
}
}

View File

@ -15,7 +15,7 @@ use syntax::visit;
use strings::string_buffer::StringBuffer;
use {Indent, WriteMode};
use Indent;
use utils;
use config::Config;
use rewrite::{Rewrite, RewriteContext};
@ -31,14 +31,13 @@ pub struct FmtVisitor<'a> {
// FIXME: use an RAII util or closure for indenting
pub block_indent: Indent,
pub config: &'a Config,
pub write_mode: Option<WriteMode>,
}
impl<'a> FmtVisitor<'a> {
fn visit_stmt(&mut self, stmt: &ast::Stmt) {
match stmt.node {
ast::Stmt_::StmtDecl(ref decl, _) => {
if let ast::Decl_::DeclItem(ref item) = decl.node {
ast::StmtKind::Decl(ref decl, _) => {
if let ast::DeclKind::Item(ref item) = decl.node {
self.visit_item(item);
} else {
let rewrite = stmt.rewrite(&self.get_context(),
@ -48,14 +47,14 @@ impl<'a> FmtVisitor<'a> {
self.push_rewrite(stmt.span, rewrite);
}
}
ast::Stmt_::StmtExpr(..) | ast::Stmt_::StmtSemi(..) => {
ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => {
let rewrite = stmt.rewrite(&self.get_context(),
self.config.max_width - self.block_indent.width(),
self.block_indent);
self.push_rewrite(stmt.span, rewrite);
}
ast::Stmt_::StmtMac(ref mac, _macro_style, _) => {
ast::StmtKind::Mac(ref mac, _macro_style, _) => {
self.format_missing_with_indent(stmt.span.lo);
self.visit_mac(mac);
}
@ -184,7 +183,7 @@ impl<'a> FmtVisitor<'a> {
// FIXME This is overly conservative and means we miss attributes on
// inline modules.
match item.node {
ast::Item_::ItemMod(_) => {
ast::ItemKind::Mod(_) => {
if utils::contains_skip(&item.attrs) {
return;
}
@ -198,10 +197,10 @@ impl<'a> FmtVisitor<'a> {
}
match item.node {
ast::Item_::ItemUse(ref vp) => {
ast::ItemKind::Use(ref vp) => {
self.format_import(item.vis, vp, item.span);
}
ast::Item_::ItemImpl(..) => {
ast::ItemKind::Impl(..) => {
self.format_missing_with_indent(item.span.lo);
if let Some(impl_str) = format_impl(&self.get_context(), item, self.block_indent) {
self.buffer.push_str(&impl_str);
@ -209,7 +208,7 @@ impl<'a> FmtVisitor<'a> {
}
}
// FIXME(#78): format traits.
ast::Item_::ItemTrait(..) => {
ast::ItemKind::Trait(..) => {
self.format_missing_with_indent(item.span.lo);
if let Some(trait_str) = format_trait(&self.get_context(),
item,
@ -223,13 +222,13 @@ impl<'a> FmtVisitor<'a> {
// }
// self.block_indent = self.block_indent.block_unindent(self.config);
}
ast::Item_::ItemExternCrate(_) => {
ast::ItemKind::ExternCrate(_) => {
self.format_missing_with_indent(item.span.lo);
let new_str = self.snippet(item.span);
self.buffer.push_str(&new_str);
self.last_pos = item.span.hi;
}
ast::Item_::ItemStruct(ref def, ref generics) => {
ast::ItemKind::Struct(ref def, ref generics) => {
let rewrite = {
let indent = self.block_indent;
let context = self.get_context();
@ -250,28 +249,24 @@ impl<'a> FmtVisitor<'a> {
};
self.push_rewrite(item.span, rewrite);
}
ast::Item_::ItemEnum(ref def, ref generics) => {
ast::ItemKind::Enum(ref def, ref generics) => {
self.format_missing_with_indent(item.span.lo);
self.visit_enum(item.ident, item.vis, def, generics, item.span);
self.last_pos = item.span.hi;
}
ast::Item_::ItemMod(ref module) => {
ast::ItemKind::Mod(ref module) => {
self.format_missing_with_indent(item.span.lo);
self.format_mod(module, item.vis, item.span, item.ident);
}
ast::Item_::ItemMac(..) => {
ast::ItemKind::Mac(ref mac) => {
self.format_missing_with_indent(item.span.lo);
let snippet = self.snippet(item.span);
self.buffer.push_str(&snippet);
self.last_pos = item.span.hi;
// FIXME: we cannot format these yet, because of a bad span.
// See rust lang issue #28424.
self.visit_mac(mac);
}
ast::Item_::ItemForeignMod(ref foreign_mod) => {
ast::ItemKind::ForeignMod(ref foreign_mod) => {
self.format_missing_with_indent(item.span.lo);
self.format_foreign_mod(foreign_mod, item.span);
}
ast::Item_::ItemStatic(ref ty, mutability, ref expr) => {
ast::ItemKind::Static(ref ty, mutability, ref expr) => {
let rewrite = rewrite_static("static",
item.vis,
item.ident,
@ -281,32 +276,32 @@ impl<'a> FmtVisitor<'a> {
&self.get_context());
self.push_rewrite(item.span, rewrite);
}
ast::Item_::ItemConst(ref ty, ref expr) => {
ast::ItemKind::Const(ref ty, ref expr) => {
let rewrite = rewrite_static("const",
item.vis,
item.ident,
ty,
ast::Mutability::MutImmutable,
ast::Mutability::Immutable,
expr,
&self.get_context());
self.push_rewrite(item.span, rewrite);
}
ast::Item_::ItemDefaultImpl(..) => {
ast::ItemKind::DefaultImpl(..) => {
// FIXME(#78): format impl definitions.
}
ast::ItemFn(ref declaration, unsafety, constness, abi, ref generics, ref body) => {
ast::ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
self.visit_fn(visit::FnKind::ItemFn(item.ident,
generics,
unsafety,
constness,
abi,
item.vis),
declaration,
decl,
body,
item.span,
item.id)
}
ast::Item_::ItemTy(ref ty, ref generics) => {
ast::ItemKind::Ty(ref ty, ref generics) => {
let rewrite = rewrite_type_alias(&self.get_context(),
self.block_indent,
item.ident,
@ -325,22 +320,22 @@ impl<'a> FmtVisitor<'a> {
}
match ti.node {
ast::ConstTraitItem(..) => {
ast::TraitItemKind::Const(..) => {
// FIXME: Implement
}
ast::MethodTraitItem(ref sig, None) => {
ast::TraitItemKind::Method(ref sig, None) => {
let indent = self.block_indent;
let rewrite = self.rewrite_required_fn(indent, ti.ident, sig, ti.span);
self.push_rewrite(ti.span, rewrite);
}
ast::MethodTraitItem(ref sig, Some(ref body)) => {
ast::TraitItemKind::Method(ref sig, Some(ref body)) => {
self.visit_fn(visit::FnKind::Method(ti.ident, sig, None),
&sig.decl,
&body,
ti.span,
ti.id);
}
ast::TypeTraitItem(ref type_param_bounds, _) => {
ast::TraitItemKind::Type(ref type_param_bounds, _) => {
let indent = self.block_indent;
let mut result = String::new();
result.push_str(&format!("type {}", ti.ident));
@ -408,10 +403,7 @@ impl<'a> FmtVisitor<'a> {
self.last_pos = span.hi;
}
pub fn from_codemap(parse_session: &'a ParseSess,
config: &'a Config,
mode: Option<WriteMode>)
-> FmtVisitor<'a> {
pub fn from_codemap(parse_session: &'a ParseSess, config: &'a Config) -> FmtVisitor<'a> {
FmtVisitor {
parse_session: parse_session,
codemap: parse_session.codemap(),
@ -422,7 +414,6 @@ impl<'a> FmtVisitor<'a> {
alignment: 0,
},
config: config,
write_mode: mode,
}
}
@ -482,11 +473,19 @@ impl<'a> FmtVisitor<'a> {
if is_internal {
self.buffer.push_str(" {");
self.last_pos = ::utils::span_after(s, "{", self.codemap);
self.block_indent = self.block_indent.block_indent(self.config);
self.walk_mod_items(m);
self.format_missing_with_indent(m.inner.hi - BytePos(1));
self.close_block();
// Hackery to account for the closing }.
let mod_lo = ::utils::span_after(s, "{", self.codemap);
let body_snippet = self.snippet(codemap::mk_sp(mod_lo, m.inner.hi - BytePos(1)));
let body_snippet = body_snippet.trim();
if body_snippet.is_empty() {
self.buffer.push_str("}");
} else {
self.last_pos = mod_lo;
self.block_indent = self.block_indent.block_indent(self.config);
self.walk_mod_items(m);
self.format_missing_with_indent(m.inner.hi - BytePos(1));
self.close_block();
}
self.last_pos = m.inner.hi;
} else {
self.buffer.push_str(";");

View File

@ -12,6 +12,7 @@ where_density = "Tall"
where_indent = "Tabbed"
where_layout = "Vertical"
where_pred_indent = "Visual"
where_trailing_comma = false
generics_indent = "Visual"
struct_trailing_comma = "Vertical"
struct_lit_trailing_comma = "Vertical"

View File

@ -45,3 +45,5 @@ fn chains() {
/*
* random comment */
fn main() {/* Test */}

View File

@ -233,3 +233,14 @@ fn blocks() {
println!("yay arithmetix!");
};
}
fn issue767() {
if false {
if false {
} else {
// A let binding here seems necessary to trigger it.
let _ = ();
}
} else if let false = false {
}
}

View File

@ -74,3 +74,6 @@ trait CoolTypes {
trait CoolerTypes { fn dummy(&self) {
}
}
fn Foo<T>() where T: Bar {
}

View File

@ -22,6 +22,12 @@ impl<'a, 'b, X, Y: Foo<Bar>> Foo<'a, X> for Bar<'b, Y> where X: Fooooooooooooooo
fn foo() { "hi" }
}
impl<T> Foo for Bar<T> where T: Baz
{
}
impl<T> Foo for Bar<T> where T: Baz { /* Comment */ }
impl Foo {
fn foo() {}
}
@ -64,3 +70,10 @@ impl X { fn do_parse( mut self : X ) {} }
impl Y5000 {
fn bar(self: X< 'a , 'b >, y: Y) {}
}
pub impl<T> Foo for Bar<T> where T: Foo
{
fn foo() { "hi" }
}
pub impl<T, Z> Foo for Bar<T, Z> where T: Foo, Z: Baz {}

37
tests/source/issue-447.rs Normal file
View File

@ -0,0 +1,37 @@
fn main() {
if /* shouldn't be dropped
shouldn't be dropped */
cond /* shouldn't be dropped
shouldn't be dropped */
{
} /* shouldn't be dropped
shouldn't be dropped */
else /* shouldn't be dropped
shouldn't be dropped */
if /* shouldn't be dropped
shouldn't be dropped */
cond /* shouldn't be dropped
shouldn't be dropped */
{
} /* shouldn't be dropped
shouldn't be dropped */
else /* shouldn't be dropped
shouldn't be dropped */
{
}
if /* shouldn't be dropped
shouldn't be dropped */
let Some(x) = y/* shouldn't be dropped
shouldn't be dropped */
{
}
}

19
tests/source/issue-811.rs Normal file
View File

@ -0,0 +1,19 @@
trait FooTrait<T>: Sized {
type Bar: BarTrait<T>;
}
trait BarTrait<T>: Sized {
type Baz;
fn foo();
}
type Foo<T: FooTrait> = <<T as FooTrait<U>>::Bar as BarTrait<U>>::Baz;
type Bar<T: BarTrait> = <T as BarTrait<U>>::Baz;
fn some_func<T: FooTrait<U>, U>() {
<<T as FooTrait<U>>::Bar as BarTrait<U>>::foo();
}
fn some_func<T: BarTrait<U>>() {
<T as BarTrait<U>>::foo();
}

View File

@ -0,0 +1 @@
const unsafe fn x() {}

View File

@ -1,4 +1,8 @@
itemmacro!(this, is.not() .formatted(yet));
itemmacro!(this, is.now() .formatted(yay));
itemmacro!(really, long.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbb() .is.formatted());
itemmacro!{this, is.bracket().formatted()}
fn main() {
foo! ( );

View File

@ -11,6 +11,20 @@ fn main() {
let Some ( ref xyz /* comment! */) = opt;
if let None = opt2 { panic!("oh noes"); }
let foo@bar (f) = 42;
let a::foo ( ..) = 42;
let [ ] = 42;
let [a.., b,c ] = 42;
let [ a,b,c.. ] = 42;
let [a, b, c, d..,e,f, g] = 42;
let foo { } = 42;
let foo {..} = 42;
let foo { x, y: ref foo, .. } = 42;
let foo { x, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, .. } = 42;
let foo { x, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, } = 42;
let foo { x, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, .. };
let foo { x, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, };
}
impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> {

View File

@ -0,0 +1,14 @@
fn main() -> &'static str {
let too_many_lines = "H\
e\
l\
l\
o";
let leave_me = "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\
s
jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj";
// Crappy formatting :-(
let change_me = "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\
s
jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj";
}

View File

@ -1,3 +1,4 @@
// rustfmt-force_format_strings: true
// Long string literals
fn main() -> &'static str {

View File

@ -114,3 +114,12 @@ fn issue491() {
Foo { a: aaaaaaaaaa, b: bbbbbbbb, c: cccccccccc, d: dddddddddd, /* a comment */
e: eeeeeeeee };
}
fn issue698() {
Record {
ffffffffffffffffffffffffffields: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
};
Record {
ffffffffffffffffffffffffffields: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
}
}

View File

@ -0,0 +1,5 @@
// rustfmt-type_punctuation_density: Compressed
fn Foo<T = Foo, Output = Expr<'tcx> + Foo>() {
let i = 6;
}

View File

@ -0,0 +1,37 @@
// rustfmt-where_trailing_comma: true
fn f<S, T>(x: T, y: S) -> T where T: P, S: Q
{
x
}
impl Trait for T where T: P
{
fn f(x: T) -> T where T: Q + R
{
x
}
}
struct Pair<S, T> where T: P, S: P + Q {
a: T,
b: S
}
struct TupPair<S, T> (S, T) where T: P, S: P + Q;
enum E<S, T> where S: P, T: P {
A {a: T},
}
type Double<T> where T: P, T: Q = Pair<T, T>;
extern "C" {
fn f<S, T>(x: T, y: S) -> T where T: P, S: Q;
}
// Note: trait declarations are not fully formatted (issue #78)
trait Q<S, T> where T: P, S: R
{
fn f<U, V>(self, x: T, y: S, z: U) -> Self where U: P, V: P;
}

View File

@ -19,15 +19,16 @@ use std::io::{self, Read, BufRead, BufReader};
use std::path::Path;
use rustfmt::*;
use rustfmt::config::{Config, ReportTactic};
use rustfmt::filemap::{write_system_newlines, FileMap};
use rustfmt::config::{Config, ReportTactic, WriteMode};
use rustfmt::rustfmt_diff::*;
static DIFF_CONTEXT_SIZE: usize = 3;
const DIFF_CONTEXT_SIZE: usize = 3;
fn get_path_string(dir_entry: io::Result<fs::DirEntry>) -> String {
let path = dir_entry.ok().expect("Couldn't get DirEntry.").path();
let path = dir_entry.expect("Couldn't get DirEntry").path();
path.to_str().expect("Couldn't stringify path.").to_owned()
path.to_str().expect("Couldn't stringify path").to_owned()
}
// Integration tests. The files in the tests/source are formatted and compared
@ -39,11 +40,11 @@ fn get_path_string(dir_entry: io::Result<fs::DirEntry>) -> String {
#[test]
fn system_tests() {
// Get all files in the tests/source directory.
let files = fs::read_dir("tests/source").ok().expect("Couldn't read source dir.");
let files = fs::read_dir("tests/source").expect("Couldn't read source dir");
// Turn a DirEntry into a String that represents the relative path to the
// file.
let files = files.map(get_path_string);
let (_reports, count, fails) = check_files(files, WriteMode::Return);
let (_reports, count, fails) = check_files(files, None);
// Display results.
println!("Ran {} system tests.", count);
@ -54,24 +55,60 @@ fn system_tests() {
// the only difference is the coverage mode
#[test]
fn coverage_tests() {
let files = fs::read_dir("tests/coverage-source").ok().expect("Couldn't read source dir.");
let files = fs::read_dir("tests/coverage-source").expect("Couldn't read source dir");
let files = files.map(get_path_string);
let (_reports, count, fails) = check_files(files, WriteMode::Coverage);
let (_reports, count, fails) = check_files(files, Some(WriteMode::Coverage));
println!("Ran {} tests in coverage mode.", count);
assert!(fails == 0, "{} tests failed", fails);
}
#[test]
fn checkstyle_test() {
let filename = "tests/source/fn-single-line.rs";
let expected_filename = "tests/writemode/checkstyle.xml";
assert_output(filename, expected_filename, Some(WriteMode::Checkstyle));
}
// Helper function for comparing the results of rustfmt
// to a known output file generated by one of the write modes.
fn assert_output(source: &str, expected_filename: &str, write_mode: Option<WriteMode>) {
let file_map = run_rustfmt(source.to_string(), write_mode);
let mut config = read_config(&source);
if let Some(write_mode) = write_mode {
config.write_mode = write_mode;
}
// Populate output by writing to a vec.
let mut out = vec![];
let _ = filemap::write_all_files(&file_map, &mut out, &config);
let output = String::from_utf8(out).unwrap();
let mut expected_file = fs::File::open(&expected_filename).expect("Couldn't open target");
let mut expected_text = String::new();
expected_file.read_to_string(&mut expected_text)
.expect("Failed reading target");
let compare = make_diff(&expected_text, &output, DIFF_CONTEXT_SIZE);
if compare.len() > 0 {
let mut failures = HashMap::new();
failures.insert(source.to_string(), compare);
print_mismatches(failures);
assert!(false, "Text does not match expected output");
}
}
// Idempotence tests. Files in tests/target are checked to be unaltered by
// rustfmt.
#[test]
fn idempotence_tests() {
// Get all files in the tests/target directory.
let files = fs::read_dir("tests/target")
.ok()
.expect("Couldn't read target dir.")
.expect("Couldn't read target dir")
.map(get_path_string);
let (_reports, count, fails) = check_files(files, WriteMode::Return);
let (_reports, count, fails) = check_files(files, None);
// Display results.
println!("Ran {} idempotent tests.", count);
@ -83,14 +120,13 @@ fn idempotence_tests() {
#[test]
fn self_tests() {
let files = fs::read_dir("src/bin")
.ok()
.expect("Couldn't read src dir.")
.chain(fs::read_dir("tests").ok().expect("Couldn't read tests dir."))
.expect("Couldn't read src dir")
.chain(fs::read_dir("tests").expect("Couldn't read tests dir"))
.map(get_path_string);
// Hack because there's no `IntoIterator` impl for `[T; N]`.
let files = files.chain(Some("src/lib.rs".to_owned()).into_iter());
let (reports, count, fails) = check_files(files, WriteMode::Return);
let (reports, count, fails) = check_files(files, None);
let mut warnings = 0;
// Display results.
@ -109,7 +145,7 @@ fn self_tests() {
// For each file, run rustfmt and collect the output.
// Returns the number of files checked and the number of failures.
fn check_files<I>(files: I, write_mode: WriteMode) -> (Vec<FormatReport>, u32, u32)
fn check_files<I>(files: I, write_mode: Option<WriteMode>) -> (Vec<FormatReport>, u32, u32)
where I: Iterator<Item = String>
{
let mut count = 0;
@ -144,9 +180,7 @@ fn print_mismatches(result: HashMap<String, Vec<Mismatch>>) {
assert!(t.reset().unwrap());
}
pub fn idempotent_check(filename: String,
write_mode: WriteMode)
-> Result<FormatReport, HashMap<String, Vec<Mismatch>>> {
fn read_config(filename: &str) -> Config {
let sig_comments = read_significant_comments(&filename);
let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..]));
@ -158,12 +192,36 @@ pub fn idempotent_check(filename: String,
// Don't generate warnings for to-do items.
config.report_todo = ReportTactic::Never;
config
}
let mut file_map = format(Path::new(&filename), &config, write_mode);
// Simulate run()
fn run_rustfmt(filename: String, write_mode: Option<WriteMode>) -> FileMap {
let mut config = read_config(&filename);
if let Some(write_mode) = write_mode {
config.write_mode = write_mode;
}
format(Path::new(&filename), &config)
}
pub fn idempotent_check(filename: String,
write_mode: Option<WriteMode>)
-> Result<FormatReport, HashMap<String, Vec<Mismatch>>> {
let sig_comments = read_significant_comments(&filename);
let config = read_config(&filename);
let mut file_map = run_rustfmt(filename, write_mode);
let format_report = fmt_lines(&mut file_map, &config);
// Won't panic, as we're not doing any IO.
let write_result = filemap::write_all_files(&file_map, WriteMode::Return, &config).unwrap();
let mut write_result = HashMap::new();
for (filename, text) in file_map.iter() {
let mut v = Vec::new();
// Won't panic, as we're not doing any IO.
write_system_newlines(&mut v, text, &config).unwrap();
// Won't panic, we are writing correct utf8.
let one_result = String::from_utf8(v).unwrap();
write_result.insert(filename.clone(), one_result);
}
let target = sig_comments.get("target").map(|x| &(*x)[..]);
handle_result(write_result, target, write_mode).map(|_| format_report)
@ -180,11 +238,9 @@ fn get_config(config_file: Option<&str>) -> Config {
}
};
let mut def_config_file = fs::File::open(config_file_name)
.ok()
.expect("Couldn't open config.");
let mut def_config_file = fs::File::open(config_file_name).expect("Couldn't open config");
let mut def_config = String::new();
def_config_file.read_to_string(&mut def_config).ok().expect("Couldn't read config.");
def_config_file.read_to_string(&mut def_config).expect("Couldn't read config");
Config::from_toml(&def_config)
}
@ -192,25 +248,22 @@ fn get_config(config_file: Option<&str>) -> Config {
// Reads significant comments of the form: // rustfmt-key: value
// into a hash map.
fn read_significant_comments(file_name: &str) -> HashMap<String, String> {
let file = fs::File::open(file_name)
.ok()
.expect(&format!("Couldn't read file {}.", file_name));
let file = fs::File::open(file_name).expect(&format!("Couldn't read file {}", file_name));
let reader = BufReader::new(file);
let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)";
let regex = regex::Regex::new(&pattern).ok().expect("Failed creating pattern 1.");
let regex = regex::Regex::new(&pattern).expect("Failed creating pattern 1");
// Matches lines containing significant comments or whitespace.
let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
.ok()
.expect("Failed creating pattern 2.");
.expect("Failed creating pattern 2");
reader.lines()
.map(|line| line.ok().expect("Failed getting line."))
.map(|line| line.expect("Failed getting line"))
.take_while(|line| line_regex.is_match(&line))
.filter_map(|line| {
regex.captures_iter(&line).next().map(|capture| {
(capture.at(1).expect("Couldn't unwrap capture.").to_owned(),
capture.at(2).expect("Couldn't unwrap capture.").to_owned())
(capture.at(1).expect("Couldn't unwrap capture").to_owned(),
capture.at(2).expect("Couldn't unwrap capture").to_owned())
})
})
.collect()
@ -220,20 +273,22 @@ fn read_significant_comments(file_name: &str) -> HashMap<String, String> {
// TODO: needs a better name, more explanation.
fn handle_result(result: HashMap<String, String>,
target: Option<&str>,
write_mode: WriteMode)
write_mode: Option<WriteMode>)
-> Result<(), HashMap<String, Vec<Mismatch>>> {
let mut failures = HashMap::new();
for (file_name, fmt_text) in result {
// If file is in tests/source, compare to file with same name in tests/target.
let target = get_target(&file_name, target, write_mode);
let mut f = fs::File::open(&target).ok().expect("Couldn't open target.");
let mut f = fs::File::open(&target).expect("Couldn't open target");
let mut text = String::new();
f.read_to_string(&mut text).ok().expect("Failed reading target.");
f.read_to_string(&mut text).expect("Failed reading target");
if fmt_text != text {
let diff = make_diff(&text, &fmt_text, DIFF_CONTEXT_SIZE);
assert!(!diff.is_empty(),
"Empty diff? Maybe due to a missing a newline at the end of a file?");
failures.insert(file_name, diff);
}
}
@ -246,10 +301,10 @@ fn handle_result(result: HashMap<String, String>,
}
// Map source file paths to their target paths.
fn get_target(file_name: &str, target: Option<&str>, write_mode: WriteMode) -> String {
fn get_target(file_name: &str, target: Option<&str>, write_mode: Option<WriteMode>) -> String {
let file_path = Path::new(file_name);
let (source_path_prefix, target_path_prefix) = match write_mode {
WriteMode::Coverage => {
Some(WriteMode::Coverage) => {
(Path::new("tests/coverage-source/"),
"tests/coverage-target/")
}

View File

@ -2,14 +2,14 @@
fn main() {
reader.lines()
.map(|line| line.ok().expect("Failed getting line."))
.map(|line| line.expect("Failed getting line"))
.take_while(|line| line_regex.is_match(&line))
.filter_map(|line| {
regex.captures_iter(&line)
.next()
.map(|capture| {
(capture.at(1).expect("Couldn\'t unwrap capture.").to_owned(),
capture.at(2).expect("Couldn\'t unwrap capture.").to_owned())
(capture.at(1).expect("Couldn\'t unwrap capture").to_owned(),
capture.at(2).expect("Couldn\'t unwrap capture").to_owned())
})
})
.collect();

View File

@ -28,12 +28,6 @@ fn foo() -> Vec<i32> {
.collect()
}
fn d() {
if true /* and ... */ {
a();
}
}
fn calc_page_len(prefix_len: usize, sofar: usize) -> usize {
2 // page type and flags
+ 1 // stored depth

View File

@ -46,3 +46,7 @@ fn chains() {
}
// random comment
fn main() {
// Test
}

View File

@ -2,5 +2,4 @@
/// This is a long line that angers rustfmt. Rustfmt shall deal with it swiftly
/// and justly.
pub mod foo {
}
pub mod foo {}

View File

@ -3,5 +3,4 @@
//! This is a long line that angers rustfmt. Rustfmt shall deal with it swiftly
//! and justly.
pub mod foo {
}
pub mod foo {}

View File

@ -256,3 +256,14 @@ fn blocks() {
println!("yay arithmetix!");
};
}
fn issue767() {
if false {
if false {
} else {
// A let binding here seems necessary to trigger it.
let _ = ();
}
} else if let false = false {
}
}

View File

@ -61,3 +61,5 @@ trait CoolTypes {
trait CoolerTypes {
fn dummy(&self) {}
}
fn Foo<T>() where T: Bar {}

View File

@ -2,17 +2,9 @@
fn foo(a: AAAA, b: BBB, c: CCC) -> RetType {}
fn foo(a: AAAA, b: BBB /* some, weird, inline comment */, c: CCC) -> RetType
where T: Blah
{
fn foo(a: AAAA, b: BBB /* some, weird, inline comment */, c: CCC) -> RetType where T: Blah {}
}
fn foo(a: AAA /* (comment) */)
where T: Blah
{
}
fn foo(a: AAA /* (comment) */) where T: Blah {}
fn foo(a: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
b: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB)
@ -35,10 +27,7 @@ fn foo<U, T>(a: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
fn foo<U: Fn(A) -> B /* paren inside generics */>() {}
impl Foo {
fn with_no_errors<T, F>(&mut self, f: F) -> T
where F: FnOnce(&mut Resolver) -> T
{
}
fn with_no_errors<T, F>(&mut self, f: F) -> T where F: FnOnce(&mut Resolver) -> T {}
fn foo(mut self, mut bar: u32) {}

View File

@ -16,7 +16,8 @@ pub impl Foo for Bar {
// Comment 3
}
pub unsafe impl<'a, 'b, X, Y: Foo<Bar>> !Foo<'a, X> for Bar<'b, Y> where X: Foo<'a, Z>
pub unsafe impl<'a, 'b, X, Y: Foo<Bar>> !Foo<'a, X> for Bar<'b, Y>
where X: Foo<'a, Z>
{
fn foo() {
"hi"
@ -31,13 +32,22 @@ impl<'a, 'b, X, Y: Foo<Bar>> Foo<'a, X> for Bar<'b, Y>
}
}
impl<'a, 'b, X, Y: Foo<Bar>> Foo<'a, X> for Bar<'b, Y> where X: Foooooooooooooooooooooooooooo<'a, Z>
impl<'a, 'b, X, Y: Foo<Bar>> Foo<'a, X> for Bar<'b, Y>
where X: Foooooooooooooooooooooooooooo<'a, Z>
{
fn foo() {
"hi"
}
}
impl<T> Foo for Bar<T> where T: Baz {}
impl<T> Foo for Bar<T>
where T: Baz
{
// Comment
}
impl Foo {
fn foo() {}
}
@ -80,3 +90,17 @@ impl X {
impl Y5000 {
fn bar(self: X<'a, 'b>, y: Y) {}
}
pub impl<T> Foo for Bar<T>
where T: Foo
{
fn foo() {
"hi"
}
}
pub impl<T, Z> Foo for Bar<T, Z>
where T: Foo,
Z: Baz
{
}

39
tests/target/issue-447.rs Normal file
View File

@ -0,0 +1,39 @@
fn main() {
if
// shouldn't be dropped
// shouldn't be dropped
cond
// shouldn't be dropped
// shouldn't be dropped
{
}
// shouldn't be dropped
// shouldn't be dropped
else
// shouldn't be dropped
// shouldn't be dropped
if
// shouldn't be dropped
// shouldn't be dropped
cond
// shouldn't be dropped
// shouldn't be dropped
{
}
// shouldn't be dropped
// shouldn't be dropped
else
// shouldn't be dropped
// shouldn't be dropped
{
}
if
// shouldn't be dropped
// shouldn't be dropped
let Some(x) = y
// shouldn't be dropped
// shouldn't be dropped
{
}
}

20
tests/target/issue-811.rs Normal file
View File

@ -0,0 +1,20 @@
trait FooTrait<T>: Sized {
type Bar: BarTrait<T>;
}
trait BarTrait<T>: Sized {
type Baz;
fn foo();
}
type Foo<T: FooTrait> =
<<T as FooTrait<U>>::Bar as BarTrait<U>>::Baz;
type Bar<T: BarTrait> = <T as BarTrait<U>>::Baz;
fn some_func<T: FooTrait<U>, U>() {
<<T as FooTrait<U>>::Bar as BarTrait<U>>::foo();
}
fn some_func<T: BarTrait<U>>() {
<T as BarTrait<U>>::foo();
}

View File

@ -0,0 +1 @@
const unsafe fn x() {}

View File

@ -1,4 +1,11 @@
itemmacro!(this, is.not() .formatted(yet));
itemmacro!(this, is.now().formatted(yay));
itemmacro!(really,
long.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbb()
.is
.formatted());
itemmacro!{this, is.bracket().formatted()}
fn main() {
foo!();

View File

@ -251,7 +251,7 @@ fn issue280() {
fn issue383() {
match resolution.last_private {
LastImport{..} => false,
LastImport { .. } => false,
_ => true,
};
}

View File

@ -2,8 +2,7 @@
mod foo {
mod bar {
mod baz {
}
mod baz {}
}
}
@ -16,9 +15,7 @@ mod foo {
}
}
mod qux {
}
mod qux {}
}
mod boxed {

View File

@ -13,6 +13,23 @@ fn main() {
if let None = opt2 {
panic!("oh noes");
}
let foo @ bar(f) = 42;
let a::foo(..) = 42;
let [] = 42;
let [a.., b, c] = 42;
let [a, b, c..] = 42;
let [a, b, c, d.., e, f, g] = 42;
let foo {} = 42;
let foo { .. } = 42;
let foo { x, y: ref foo, .. } = 42;
let foo { x, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo, .. } = 42;
let foo { x, yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo } = 42;
let foo { x,
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo,
.. };
let foo { x,
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy: ref foo };
}
impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> {

View File

@ -0,0 +1,15 @@
fn main() -> &'static str {
let too_many_lines = "H\
e\
l\
l\
o";
let leave_me = "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\
s
jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj";
// Crappy formatting :-(
let change_me = "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
\
jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj\
j";
}

View File

@ -1,3 +1,4 @@
// rustfmt-force_format_strings: true
// Long string literals
fn main() -> &'static str {

View File

@ -149,3 +149,13 @@ fn issue491() {
e: eeeeeeeee,
};
}
fn issue698() {
Record {
ffffffffffffffffffffffffffields: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
};
Record {
ffffffffffffffffffffffffffields:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
}
}

View File

@ -0,0 +1,5 @@
// rustfmt-type_punctuation_density: Compressed
fn Foo<T=Foo, Output=Expr<'tcx>+Foo>() {
let i = 6;
}

View File

@ -0,0 +1,57 @@
// rustfmt-where_trailing_comma: true
fn f<S, T>(x: T, y: S) -> T
where T: P,
S: Q,
{
x
}
impl Trait for T
where T: P,
{
fn f(x: T) -> T
where T: Q + R,
{
x
}
}
struct Pair<S, T>
where T: P,
S: P + Q,
{
a: T,
b: S,
}
struct TupPair<S, T>(S, T)
where T: P,
S: P + Q;
enum E<S, T>
where S: P,
T: P,
{
A {
a: T,
},
}
type Double<T>
where T: P,
T: Q = Pair<T, T>;
extern "C" {
fn f<S, T>(x: T, y: S) -> T
where T: P,
S: Q;
}
// Note: trait declarations are not fully formatted (issue #78)
trait Q<S, T> where T: P, S: R
{
fn f<U, V>(self, x: T, y: S, z: U) -> Self
where U: P,
V: P;
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<checkstyle version="4.3"><file name="tests/source/fn-single-line.rs"><error line="1" severity="warning" message="Should be `fn foo_expr() { 1 }`" /><error line="1" severity="warning" message="Should be `fn foo_stmt() { foo(); }`" /><error line="1" severity="warning" message="Should be `fn foo_decl_local() { let z = 5; }`" /><error line="1" severity="warning" message="Should be `fn foo_decl_item(x: &amp;mut i32) { x = 3; }`" /><error line="1" severity="warning" message="Should be `fn empty() {}`" /><error line="1" severity="warning" message="Should be `fn foo_return() -&gt; String { &quot;yay&quot; }`" /><error line="1" severity="warning" message="Should be `fn foo_where() -&gt; T`" /><error line="1" severity="warning" message="Should be ` where T: Sync`" /><error line="1" severity="warning" message="Should be `{`" /><error line="50" severity="warning" message="Should be `fn lots_of_space() { 1 }`" /><error line="57" severity="warning" message="Should be ` fn dummy(&amp;self) {}`" /><error line="57" severity="warning" message="Should be `trait CoolerTypes {`" /><error line="57" severity="warning" message="Should be ` fn dummy(&amp;self) {}`" /><error line="57" severity="warning" message="Should be `fn Foo&lt;T&gt;() where T: Bar {}`" /><error line="57" severity="warning" message="Should be ``" /></file></checkstyle>