Merge pull request #1007 from kamalmarhubi/basic-line-ranges-v2

Add infrastructure for formatting specific line ranges
This commit is contained in:
Nick Cameron 2016-05-31 08:45:45 +01:00
commit 240dba5467
24 changed files with 638 additions and 52 deletions

6
Cargo.lock generated
View File

@ -7,6 +7,7 @@ dependencies = [
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"multimap 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"strings 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -80,6 +81,11 @@ dependencies = [
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "multimap"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "regex"
version = "0.1.71"

View File

@ -26,3 +26,4 @@ log = "0.3"
env_logger = "0.3"
getopts = "0.2"
itertools = "0.4.15"
multimap = "0.3"

View File

@ -73,6 +73,25 @@ the command line. For example `rustfmt --write-mode=display src/filename.rs`
`cargo fmt` uses `--write-mode=replace` by default.
If you want to restrict reformatting to specific sets of lines, you can
use the `--file-lines` option. Its argument is a JSON array of objects
with `file` and `range` properties, where `file` is a file name, and
`range` is an array representing a range of lines like `[7,13]`. Ranges
are 1-based and inclusive of both end points. Specifying an empty array
will result in no files being formatted. For example,
```
rustfmt --file-lines '[
{"file":"src/lib.rs","range":[7,13]},
{"file":"src/lib.rs","range":[21,29]},
{"file":"src/foo.rs","range":[10,11]},
{"file":"src/foo.rs","range":[15,15]}]'
```
would format lines `7-13` and `21-29` of `src/lib.rs`, and lines `10-11`,
and `15` of `src/foo.rs`. No other files would be formatted, even if they
are included as out of line modules from `src/lib.rs`.
If `rustfmt` successfully reformatted the code it will exit with `0` exit
status. Exit status `1` signals some unexpected error, like an unknown option or
a failure to read a file. Exit status `2` is returned if there are syntax errors

View File

@ -18,6 +18,7 @@ extern crate env_logger;
extern crate getopts;
use rustfmt::{run, Input, Summary};
use rustfmt::file_lines::FileLines;
use rustfmt::config::{Config, WriteMode};
use std::{env, error};
@ -57,6 +58,7 @@ struct CliOptions {
skip_children: bool,
verbose: bool,
write_mode: Option<WriteMode>,
file_lines: FileLines, // Default is all lines in all files.
}
impl CliOptions {
@ -73,12 +75,17 @@ impl CliOptions {
}
}
if let Some(ref file_lines) = matches.opt_str("file-lines") {
options.file_lines = try!(file_lines.parse());
}
Ok(options)
}
fn apply_to(&self, config: &mut Config) {
fn apply_to(self, config: &mut Config) {
config.skip_children = self.skip_children;
config.verbose = self.verbose;
config.file_lines = self.file_lines;
if let Some(write_mode) = self.write_mode {
config.write_mode = write_mode;
}
@ -168,6 +175,10 @@ fn make_opts() -> Options {
"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]");
opts.optopt("",
"file-lines",
"Format specified line ranges. See README for more detail on the JSON format.",
"JSON");
opts
}
@ -198,8 +209,12 @@ fn execute(opts: &Options) -> FmtResult<Summary> {
Ok(run(Input::Text(input), &config))
}
Operation::Format { files, config_path } => {
Operation::Format { mut files, config_path } => {
let options = try!(CliOptions::from_matches(&matches));
// Add any additional files that were specified via `--file-lines`.
files.extend(options.file_lines.files().cloned().map(PathBuf::from));
let mut config = Config::default();
let mut path = None;
// Load the config path file if provided
@ -227,7 +242,7 @@ fn execute(opts: &Options) -> FmtResult<Summary> {
config = config_tmp;
}
options.apply_to(&mut config);
options.clone().apply_to(&mut config);
error_summary.add(run(Input::File(file), &config));
}
Ok(error_summary)
@ -306,8 +321,8 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
Some(dir)
});
// if no file argument is supplied, read from stdin
if matches.free.is_empty() {
// if no file argument is supplied and `--file-lines` is not specified, read from stdin
if matches.free.is_empty() && !matches.opt_present("file-lines") {
let mut buffer = String::new();
try!(io::stdin().read_to_string(&mut buffer));
@ -318,6 +333,7 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
});
}
// We append files from `--file-lines` later in `execute()`.
let files: Vec<_> = matches.free.iter().map(PathBuf::from).collect();
Ok(Operation::Format {

94
src/codemap.rs Normal file
View File

@ -0,0 +1,94 @@
// Copyright 2016 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.
//! This module contains utilities that work with the `CodeMap` from libsyntax / syntex_syntax.
//! This includes extension traits and methods for looking up spans and line ranges for AST nodes.
use std::rc::Rc;
use syntax::codemap::{BytePos, CodeMap, FileMap, Span};
use comment::FindUncommented;
/// A range of lines in a file, inclusive of both ends.
pub struct LineRange {
pub file: Rc<FileMap>,
pub lo: usize,
pub hi: usize,
}
impl LineRange {
pub fn file_name(&self) -> &str {
self.file.as_ref().name.as_str()
}
}
pub trait SpanUtils {
fn span_after(&self, original: Span, needle: &str) -> BytePos;
fn span_after_last(&self, original: Span, needle: &str) -> BytePos;
fn span_before(&self, original: Span, needle: &str) -> BytePos;
}
pub trait LineRangeUtils {
/// Returns the `LineRange` that corresponds to `span` in `self`.
///
/// # Panics
///
/// Panics if `span` crosses a file boundary, which shouldn't happen.
fn lookup_line_range(&self, span: Span) -> LineRange;
}
impl SpanUtils for CodeMap {
#[inline]
fn span_after(&self, original: Span, needle: &str) -> BytePos {
let snippet = self.span_to_snippet(original).unwrap();
let offset = snippet.find_uncommented(needle).unwrap() + needle.len();
original.lo + BytePos(offset as u32)
}
#[inline]
fn span_after_last(&self, original: Span, needle: &str) -> BytePos {
let snippet = self.span_to_snippet(original).unwrap();
let mut offset = 0;
while let Some(additional_offset) = snippet[offset..].find_uncommented(needle) {
offset += additional_offset + needle.len();
}
original.lo + BytePos(offset as u32)
}
#[inline]
fn span_before(&self, original: Span, needle: &str) -> BytePos {
let snippet = self.span_to_snippet(original).unwrap();
let offset = snippet.find_uncommented(needle).unwrap();
original.lo + BytePos(offset as u32)
}
}
impl LineRangeUtils for CodeMap {
fn lookup_line_range(&self, span: Span) -> LineRange {
let lo = self.lookup_char_pos(span.lo);
let hi = self.lookup_char_pos(span.hi);
assert!(lo.file.name == hi.file.name,
"span crossed file boundary: lo: {:?}, hi: {:?}",
lo,
hi);
LineRange {
file: lo.file.clone(),
lo: lo.line,
hi: hi.line,
}
}
}

View File

@ -10,6 +10,7 @@
extern crate toml;
use file_lines::FileLines;
use lists::{SeparatorTactic, ListTactic};
use std::io::Write;
@ -200,6 +201,12 @@ impl ConfigType for String {
}
}
impl ConfigType for FileLines {
fn doc_hint() -> String {
String::from("<json>")
}
}
pub struct ConfigHelpItem {
option_name: &'static str,
doc_string: &'static str,
@ -327,6 +334,9 @@ macro_rules! create_config {
create_config! {
verbose: bool, false, "Use verbose output";
skip_children: bool, false, "Don't reformat out of line modules";
file_lines: FileLines, FileLines::all(),
"Lines to format; this is not supported in rustfmt.toml, and can only be specified \
via the --file-lines option";
max_width: usize, 100, "Maximum width of each line";
ideal_width: usize, 80, "Ideal width of each line";
tab_spaces: usize, 4, "Number of spaces per tab";

View File

@ -16,12 +16,13 @@ use std::iter::ExactSizeIterator;
use std::fmt::Write;
use {Indent, Spanned};
use codemap::SpanUtils;
use rewrite::{Rewrite, RewriteContext};
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic,
DefinitiveListTactic, definitive_tactic, ListItem, format_item_list};
use string::{StringFormat, rewrite_string};
use utils::{CodeMapSpanUtils, extra_offset, last_line_width, wrap_str, binary_search,
first_line_width, semicolon_for_stmt, trimmed_last_line_width, left_most_sub_expr};
use utils::{extra_offset, last_line_width, wrap_str, binary_search, first_line_width,
semicolon_for_stmt, trimmed_last_line_width, left_most_sub_expr};
use visitor::FmtVisitor;
use config::{Config, StructLitStyle, MultilineStyle, ElseIfBraceStyle, ControlBraceStyle};
use comment::{FindUncommented, rewrite_comment, contains_comment, recover_comment_removed};

232
src/file_lines.rs Normal file
View File

@ -0,0 +1,232 @@
// Copyright 2016 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.
//! This module contains types and functions to support formatting specific line ranges.
use std::{cmp, iter, str};
use itertools::Itertools;
use multimap::MultiMap;
use rustc_serialize::{self, json};
use codemap::LineRange;
/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcDecodable)]
struct Range {
pub lo: usize,
pub hi: usize,
}
impl<'a> From<&'a LineRange> for Range {
fn from(range: &'a LineRange) -> Range {
Range::new(range.lo, range.hi)
}
}
impl Range {
fn new(lo: usize, hi: usize) -> Range {
Range { lo: lo, hi: hi }
}
fn is_empty(self) -> bool {
self.lo > self.hi
}
fn contains(self, other: Range) -> bool {
if other.is_empty() {
true
} else {
!self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
}
}
fn intersects(self, other: Range) -> bool {
if self.is_empty() || other.is_empty() {
false
} else {
(self.lo <= other.hi && other.hi <= self.hi) ||
(other.lo <= self.hi && self.hi <= other.hi)
}
}
fn adjacent_to(self, other: Range) -> bool {
if self.is_empty() || other.is_empty() {
false
} else {
self.hi + 1 == other.lo || other.hi + 1 == self.lo
}
}
/// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
/// intersect; returns `None` otherwise.
fn merge(self, other: Range) -> Option<Range> {
if self.adjacent_to(other) || self.intersects(other) {
Some(Range::new(cmp::min(self.lo, other.lo), cmp::max(self.hi, other.hi)))
} else {
None
}
}
}
/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default)]
pub struct FileLines(Option<MultiMap<String, Range>>);
/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(map: &mut MultiMap<String, Range>) {
for (_, ranges) in map.iter_all_mut() {
ranges.sort_by_key(|x| x.lo);
let merged = ranges.drain(..).coalesce(|x, y| x.merge(y).ok_or((x, y))).collect();
*ranges = merged;
}
}
impl FileLines {
/// Creates a `FileLines` that contains all lines in all files.
pub fn all() -> FileLines {
FileLines(None)
}
/// Creates a `FileLines` from a `MultiMap`, ensuring that the invariants hold.
fn from_multimap(map: MultiMap<String, Range>) -> FileLines {
let mut map = map;
normalize_ranges(&mut map);
FileLines(Some(map))
}
/// Returns an iterator over the files contained in `self`.
pub fn files(&self) -> Files {
Files(self.0.as_ref().map(MultiMap::keys))
}
/// Returns true if `range` is fully contained in `self`.
pub fn contains(&self, range: &LineRange) -> bool {
let map = match self.0 {
// `None` means "all lines in all files".
None => return true,
Some(ref map) => map,
};
match map.get_vec(range.file_name()) {
None => false,
Some(ranges) => ranges.iter().any(|r| r.contains(Range::from(range))),
}
}
/// Returns true if any lines in `range` are in `self`.
pub fn intersects(&self, range: &LineRange) -> bool {
let map = match self.0 {
// `None` means "all lines in all files".
None => return true,
Some(ref map) => map,
};
match map.get_vec(range.file_name()) {
None => false,
Some(ranges) => ranges.iter().any(|r| r.intersects(Range::from(range))),
}
}
}
/// FileLines files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, String, Vec<Range>>>);
impl<'a> iter::Iterator for Files<'a> {
type Item = &'a String;
fn next(&mut self) -> Option<&'a String> {
self.0.as_mut().and_then(Iterator::next)
}
}
// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
type Err = String;
fn from_str(s: &str) -> Result<FileLines, String> {
let v: Vec<JsonSpan> = try!(json::decode(s).map_err(|e| e.to_string()));
let m = v.into_iter().map(JsonSpan::into_tuple).collect();
Ok(FileLines::from_multimap(m))
}
}
// For JSON decoding.
#[derive(Clone, Debug, RustcDecodable)]
struct JsonSpan {
file: String,
range: (usize, usize),
}
impl JsonSpan {
// To allow `collect()`ing into a `MultiMap`.
fn into_tuple(self) -> (String, Range) {
let (lo, hi) = self.range;
(self.file, Range::new(lo, hi))
}
}
// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl rustc_serialize::Decodable for FileLines {
fn decode<D: rustc_serialize::Decoder>(_: &mut D) -> Result<Self, D::Error> {
panic!("FileLines cannot be deserialized from a project rustfmt.toml file: please \
specify it via the `--file-lines` option instead");
}
}
#[cfg(test)]
mod test {
use super::Range;
#[test]
fn test_range_intersects() {
assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
}
#[test]
fn test_range_adjacent_to() {
assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
}
#[test]
fn test_range_contains() {
assert!(Range::new(1, 2).contains(Range::new(1, 1)));
assert!(Range::new(1, 2).contains(Range::new(2, 2)));
assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
}
#[test]
fn test_range_merge() {
assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
assert_eq!(Some(Range::new(3, 7)),
Range::new(3, 5).merge(Range::new(4, 7)));
assert_eq!(Some(Range::new(3, 7)),
Range::new(3, 5).merge(Range::new(5, 7)));
assert_eq!(Some(Range::new(3, 7)),
Range::new(3, 5).merge(Range::new(6, 7)));
assert_eq!(Some(Range::new(3, 7)),
Range::new(3, 7).merge(Range::new(4, 5)));
}
}

View File

@ -9,9 +9,9 @@
// except according to those terms.
use Indent;
use codemap::SpanUtils;
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, definitive_tactic};
use types::rewrite_path;
use utils::CodeMapSpanUtils;
use rewrite::{Rewrite, RewriteContext};
use syntax::ast;

View File

@ -11,8 +11,9 @@
// Formatting top-level items - functions, structs, enums, traits, impls.
use Indent;
use utils::{CodeMapSpanUtils, format_mutability, format_visibility, contains_skip, end_typaram,
wrap_str, last_line_width, semicolon_for_expr, format_unsafety, trim_newlines};
use codemap::SpanUtils;
use utils::{format_mutability, format_visibility, contains_skip, end_typaram, wrap_str,
last_line_width, semicolon_for_expr, format_unsafety, trim_newlines};
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic,
DefinitiveListTactic, ListTactic, definitive_tactic, format_item_list};
use expr::{is_empty_block, is_simple_block_stmt, rewrite_assign_rhs};

View File

@ -25,6 +25,7 @@ extern crate regex;
extern crate diff;
extern crate term;
extern crate itertools;
extern crate multimap;
use syntax::ast;
use syntax::codemap::{mk_sp, CodeMap, Span};
@ -52,7 +53,9 @@ pub use self::summary::Summary;
#[macro_use]
mod utils;
pub mod config;
pub mod codemap;
pub mod filemap;
pub mod file_lines;
pub mod visitor;
mod checkstyle;
mod items;
@ -470,6 +473,7 @@ pub fn format_input<T: Write>(input: Input,
}
}
#[derive(Debug)]
pub enum Input {
File(PathBuf),
Text(String),

View File

@ -25,10 +25,11 @@ use syntax::parse::tts_to_parser;
use syntax::codemap::{mk_sp, BytePos};
use Indent;
use codemap::SpanUtils;
use rewrite::{Rewrite, RewriteContext};
use expr::{rewrite_call, rewrite_array};
use comment::{FindUncommented, contains_comment};
use utils::{CodeMapSpanUtils, wrap_str};
use utils::wrap_str;
const FORCED_BRACKET_MACROS: &'static [&'static str] = &["vec!"];

View File

@ -9,8 +9,9 @@
// except according to those terms.
use Indent;
use codemap::SpanUtils;
use rewrite::{Rewrite, RewriteContext};
use utils::{CodeMapSpanUtils, wrap_str, format_mutability};
use utils::{wrap_str, format_mutability};
use lists::{format_item_list, itemize_list};
use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple};
use types::rewrite_path;

View File

@ -17,9 +17,10 @@ use syntax::codemap::{self, Span, BytePos};
use syntax::abi;
use {Indent, Spanned};
use codemap::SpanUtils;
use lists::{format_item_list, itemize_list, format_fn_args};
use rewrite::{Rewrite, RewriteContext};
use utils::{CodeMapSpanUtils, extra_offset, format_mutability, wrap_str};
use utils::{extra_offset, format_mutability, wrap_str};
use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple};
use config::TypeDensity;

View File

@ -14,51 +14,14 @@ use std::cmp::Ordering;
use itertools::Itertools;
use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItemKind, Path};
use syntax::codemap::{CodeMap, Span, BytePos};
use syntax::codemap::BytePos;
use syntax::abi;
use Indent;
use comment::FindUncommented;
use rewrite::{Rewrite, RewriteContext};
use SKIP_ANNOTATION;
pub trait CodeMapSpanUtils {
fn span_after(&self, original: Span, needle: &str) -> BytePos;
fn span_after_last(&self, original: Span, needle: &str) -> BytePos;
fn span_before(&self, original: Span, needle: &str) -> BytePos;
}
impl CodeMapSpanUtils for CodeMap {
#[inline]
fn span_after(&self, original: Span, needle: &str) -> BytePos {
let snippet = self.span_to_snippet(original).unwrap();
let offset = snippet.find_uncommented(needle).unwrap() + needle.len();
original.lo + BytePos(offset as u32)
}
#[inline]
fn span_after_last(&self, original: Span, needle: &str) -> BytePos {
let snippet = self.span_to_snippet(original).unwrap();
let mut offset = 0;
while let Some(additional_offset) = snippet[offset..].find_uncommented(needle) {
offset += additional_offset + needle.len();
}
original.lo + BytePos(offset as u32)
}
#[inline]
fn span_before(&self, original: Span, needle: &str) -> BytePos {
let snippet = self.span_to_snippet(original).unwrap();
let offset = snippet.find_uncommented(needle).unwrap();
original.lo + BytePos(offset as u32)
}
}
// Computes the length of a string's last line, minus offset.
#[inline]
pub fn extra_offset(text: &str, offset: Indent) -> usize {

View File

@ -15,7 +15,8 @@ use syntax::parse::ParseSess;
use strings::string_buffer::StringBuffer;
use Indent;
use utils::{self, CodeMapSpanUtils};
use utils;
use codemap::{LineRangeUtils, SpanUtils};
use config::Config;
use rewrite::{Rewrite, RewriteContext};
use comment::rewrite_comment;
@ -42,6 +43,15 @@ pub struct FmtVisitor<'a> {
impl<'a> FmtVisitor<'a> {
fn visit_stmt(&mut self, stmt: &ast::Stmt) {
debug!("visit_stmt: {:?} {:?}",
self.codemap.lookup_char_pos(stmt.span.lo),
self.codemap.lookup_char_pos(stmt.span.hi));
// FIXME(#434): Move this check to somewhere more central, eg Rewrite.
if !self.config.file_lines.contains(&self.codemap.lookup_line_range(stmt.span)) {
return;
}
match stmt.node {
ast::StmtKind::Decl(ref decl, _) => {
if let ast::DeclKind::Item(ref item) = decl.node {

View File

@ -0,0 +1,29 @@
// rustfmt-file_lines: [{"file":"tests/source/file-lines-1.rs","range":[4,8]}]
fn floaters() {
let x = Foo {
field1: val1,
field2: val2,
}
.method_call().method_call();
let y = if cond {
val1
} else {
val2
}
.method_call();
{
match x {
PushParam => {
// params are 1-indexed
stack.push(mparams[match cur.to_digit(10) {
Some(d) => d as usize - 1,
None => return Err("bad param number".to_owned()),
}]
.clone());
}
}
}
}

View File

@ -0,0 +1,29 @@
// rustfmt-file_lines: [{"file":"tests/source/file-lines-2.rs","range":[10,15]}]
fn floaters() {
let x = Foo {
field1: val1,
field2: val2,
}
.method_call().method_call();
let y = if cond {
val1
} else {
val2
}
.method_call();
{
match x {
PushParam => {
// params are 1-indexed
stack.push(mparams[match cur.to_digit(10) {
Some(d) => d as usize - 1,
None => return Err("bad param number".to_owned()),
}]
.clone());
}
}
}
}

View File

@ -0,0 +1,29 @@
// rustfmt-file_lines: [{"file":"tests/source/file-lines-3.rs","range":[4,8]},{"file":"tests/source/file-lines-3.rs","range":[10,15]}]
fn floaters() {
let x = Foo {
field1: val1,
field2: val2,
}
.method_call().method_call();
let y = if cond {
val1
} else {
val2
}
.method_call();
{
match x {
PushParam => {
// params are 1-indexed
stack.push(mparams[match cur.to_digit(10) {
Some(d) => d as usize - 1,
None => return Err("bad param number".to_owned()),
}]
.clone());
}
}
}
}

View File

@ -0,0 +1,30 @@
// rustfmt-file_lines: []
// (Test that nothing is formatted if an empty array is specified.)
fn floaters() {
let x = Foo {
field1: val1,
field2: val2,
}
.method_call().method_call();
let y = if cond {
val1
} else {
val2
}
.method_call();
{
match x {
PushParam => {
// params are 1-indexed
stack.push(mparams[match cur.to_digit(10) {
Some(d) => d as usize - 1,
None => return Err("bad param number".to_owned()),
}]
.clone());
}
}
}
}

View File

@ -0,0 +1,30 @@
// rustfmt-file_lines: [{"file":"tests/source/file-lines-1.rs","range":[4,8]}]
fn floaters() {
let x = Foo {
field1: val1,
field2: val2,
}
.method_call()
.method_call();
let y = if cond {
val1
} else {
val2
}
.method_call();
{
match x {
PushParam => {
// params are 1-indexed
stack.push(mparams[match cur.to_digit(10) {
Some(d) => d as usize - 1,
None => return Err("bad param number".to_owned()),
}]
.clone());
}
}
}
}

View File

@ -0,0 +1,24 @@
// rustfmt-file_lines: [{"file":"tests/source/file-lines-2.rs","range":[10,15]}]
fn floaters() {
let x = Foo {
field1: val1,
field2: val2,
}
.method_call().method_call();
let y = if cond { val1 } else { val2 }.method_call();
{
match x {
PushParam => {
// params are 1-indexed
stack.push(mparams[match cur.to_digit(10) {
Some(d) => d as usize - 1,
None => return Err("bad param number".to_owned()),
}]
.clone());
}
}
}
}

View File

@ -0,0 +1,25 @@
// rustfmt-file_lines: [{"file":"tests/source/file-lines-3.rs","range":[4,8]},{"file":"tests/source/file-lines-3.rs","range":[10,15]}]
fn floaters() {
let x = Foo {
field1: val1,
field2: val2,
}
.method_call()
.method_call();
let y = if cond { val1 } else { val2 }.method_call();
{
match x {
PushParam => {
// params are 1-indexed
stack.push(mparams[match cur.to_digit(10) {
Some(d) => d as usize - 1,
None => return Err("bad param number".to_owned()),
}]
.clone());
}
}
}
}

View File

@ -0,0 +1,30 @@
// rustfmt-file_lines: []
// (Test that nothing is formatted if an empty array is specified.)
fn floaters() {
let x = Foo {
field1: val1,
field2: val2,
}
.method_call().method_call();
let y = if cond {
val1
} else {
val2
}
.method_call();
{
match x {
PushParam => {
// params are 1-indexed
stack.push(mparams[match cur.to_digit(10) {
Some(d) => d as usize - 1,
None => return Err("bad param number".to_owned()),
}]
.clone());
}
}
}
}