diff --git a/src/items.rs b/src/items.rs index 2817b23b33e..29c950f1464 100644 --- a/src/items.rs +++ b/src/items.rs @@ -11,7 +11,7 @@ // Formatting top-level items - functions, structs, enums, traits, impls. use {ReturnIndent, BraceStyle}; -use utils::{format_visibility, make_indent}; +use utils::{format_visibility, make_indent, FindUncommented}; use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic}; use visitor::FmtVisitor; use syntax::{ast, abi}; @@ -336,12 +336,11 @@ impl<'a> FmtVisitor<'a> { // FIXME If you thought the crap with the commas was ugly, just wait. // This is awful. We're going to look from the last item span to the // start of the return type span, then we drop everything after the - // first closing paren. Obviously, this will break if there is a - // closing paren in the comment. + // first closing paren. // The fix is comments in the AST or a span for the closing paren. let snippet = self.snippet(codemap::mk_sp(prev_end, next_span_start)); let snippet = snippet.trim(); - let snippet = &snippet[..snippet.find(terminator).unwrap_or(snippet.len())]; + let snippet = &snippet[..snippet.find_uncommented(terminator).unwrap_or(snippet.len())]; let snippet = snippet.trim(); result.push(snippet.to_owned()); @@ -417,8 +416,7 @@ impl<'a> FmtVisitor<'a> { self.changes.push_str_span(span, &header_str); let enum_snippet = self.snippet(span); - // FIXME this will give incorrect results if there is a { in a comment. - let body_start = span.lo + BytePos(enum_snippet.find('{').unwrap() as u32 + 1); + let body_start = span.lo + BytePos(enum_snippet.find_uncommented("{").unwrap() as u32 + 1); let generics_str = self.format_generics(generics, body_start); self.changes.push_str_span(span, &generics_str); @@ -542,8 +540,8 @@ impl<'a> FmtVisitor<'a> { self.changes.push_str_span(span, &generics_str); let struct_snippet = self.snippet(span); - // FIXME this will give incorrect results if there is a { in a comment. - self.last_pos = span.lo + BytePos(struct_snippet.find('{').unwrap() as u32 + 1); + // This will drop the comment in between the header and body. + self.last_pos = span.lo + BytePos(struct_snippet.find_uncommented("{").unwrap() as u32 + 1); self.block_indent += config!(tab_spaces); for (i, f) in struct_def.fields.iter().enumerate() { @@ -632,8 +630,7 @@ impl<'a> FmtVisitor<'a> { // This hack makes sure we only add comments etc. after the comma, and // makes sure we don't repeat any commas. let hi = field.span.hi; - // FIXME a comma in a comment will break this hack. - let comma_pos = match struct_snippet[(hi.0 - struct_start.0) as usize..].find(',') { + let comma_pos = match struct_snippet[(hi.0 - struct_start.0) as usize..].find_uncommented(",") { Some(i) => i, None => 0, }; diff --git a/src/utils.rs b/src/utils.rs index c32a9293326..5e7f8baf1dc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -10,6 +10,72 @@ use syntax::ast::Visibility; +pub trait FindUncommented { + fn find_uncommented(&self, pat: &str) -> Option; +} + +impl FindUncommented for str { + fn find_uncommented(&self, pat: &str) -> Option { + let mut needle_iter = pat.chars(); + let mut possible_comment = false; + + for (i, b) in self.char_indices() { + match needle_iter.next() { + Some(c) => { + if b != c { + needle_iter = pat.chars(); + } + }, + None => return Some(i - pat.len()) + } + + if possible_comment { + if b == '/' { + return self[(i+1)..].find('\n') + .and_then(|end| { + self[(end + i + 2)..].find_uncommented(pat) + .map(|idx| idx + end + i + 2) + }); + } else if b == '*' { + return self[(i+1)..].find("*/") + .and_then(|end| { + self[(end + i + 3)..].find_uncommented(pat) + .map(|idx| idx + end + i + 3) + }); + } else { + possible_comment = false; + } + } else { + possible_comment = b == '/'; + } + } + + // Handle case where the pattern is a suffix of the search string + match needle_iter.next() { + Some(_) => None, + None => Some(self.len() - pat.len()) + } + } +} + +#[test] +fn test_find_uncommented() { + fn check(haystack: &str, needle: &str, expected: Option) { + assert_eq!(expected, haystack.find_uncommented(needle)); + } + + check("/*//*/test", "test", Some(6)); + check("//test\ntest", "test", Some(7)); + check("/* comment only */", "whatever", None); + check("/* comment */ some text /* more commentary */ result", "result", Some(46)); + check("sup // sup", "p", Some(2)); + check("sup", "x", None); + check("π? /**/ π is nice!", "π is nice", Some(9)); + check("/*sup yo? \n sup*/ sup", "p", Some(20)); + check("hel/*lohello*/lo", "hello", None); + check("acb", "ab", None); +} + #[inline] pub fn prev_char(s: &str, mut i: usize) -> usize { if i == 0 { return 0; } @@ -63,6 +129,7 @@ pub fn round_up_to_power_of_two(mut x: usize) -> usize { x + 1 } +#[inline] #[cfg(target_pointer_width="32")] pub fn round_up_to_power_of_two(mut x: usize) -> usize { x -= 1; diff --git a/tests/target/fn.rs b/tests/target/fn.rs index b78f6d9ffc3..dff8085136e 100644 --- a/tests/target/fn.rs +++ b/tests/target/fn.rs @@ -4,13 +4,13 @@ fn foo(a: AAAA, b: BBB, c: CCC) -> RetType { } -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: AAA) +fn foo(a: AAA /* (comment) */) where T: Blah {