about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2018-07-24 16:01:38 -0700
committerEsteban Küber <esteban@kuber.com.ar>2018-07-24 16:01:38 -0700
commit4d8aa5989c3ecbcee9da63fdcf4564f71ac328fc (patch)
treee28251b7ff5a9e6e241448130b35381f47168f15 /src
parentf9e37625e6e9d90ee8b7200313de3915e2fc15a0 (diff)
downloadrust-4d8aa5989c3ecbcee9da63fdcf4564f71ac328fc.tar.gz
rust-4d8aa5989c3ecbcee9da63fdcf4564f71ac328fc.zip
Use suggestions for `printf` format
Diffstat (limited to 'src')
-rw-r--r--src/libsyntax_ext/format.rs17
-rw-r--r--src/libsyntax_ext/format_foreign.rs54
-rw-r--r--src/test/ui/ifmt-bad-arg.stderr5
-rw-r--r--src/test/ui/macros/format-foreign.rs5
-rw-r--r--src/test/ui/macros/format-foreign.stderr35
5 files changed, 102 insertions, 14 deletions
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index 023fe77cb3c..ad05db91770 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -948,6 +948,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
                 ($kind:ident) => {{
                     let mut show_doc_note = false;
 
+                    let mut suggestions = vec![];
                     for sub in foreign::$kind::iter_subs(fmt_str) {
                         let trn = match sub.translate() {
                             Some(trn) => trn,
@@ -956,6 +957,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
                             None => continue,
                         };
 
+                        let pos = sub.position();
                         let sub = String::from(sub.as_str());
                         if explained.contains(&sub) {
                             continue;
@@ -967,7 +969,14 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
                             show_doc_note = true;
                         }
 
-                        diag.help(&format!("`{}` should be written as `{}`", sub, trn));
+                        if let Some((start, end)) = pos {
+                            // account for `"` and account for raw strings `r#`
+                            let padding = str_style.map(|i| i + 2).unwrap_or(1);
+                            let sp = fmt_sp.from_inner_byte_pos(start + padding, end + padding);
+                            suggestions.push((sp, trn));
+                        } else {
+                            diag.help(&format!("`{}` should be written as `{}`", sub, trn));
+                        }
                     }
 
                     if show_doc_note {
@@ -976,6 +985,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
                             " formatting not supported; see the documentation for `std::fmt`",
                         ));
                     }
+                    if suggestions.len() > 0 {
+                        diag.multipart_suggestion(
+                            "format specifiers in Rust are written using `{}`",
+                            suggestions,
+                        );
+                    }
                 }};
             }
 
diff --git a/src/libsyntax_ext/format_foreign.rs b/src/libsyntax_ext/format_foreign.rs
index ff9663cdd3c..115f51c5e81 100644
--- a/src/libsyntax_ext/format_foreign.rs
+++ b/src/libsyntax_ext/format_foreign.rs
@@ -14,7 +14,7 @@ pub mod printf {
     /// Represents a single `printf`-style substitution.
     #[derive(Clone, PartialEq, Debug)]
     pub enum Substitution<'a> {
-        /// A formatted output substitution.
+        /// A formatted output substitution with its internal byte offset.
         Format(Format<'a>),
         /// A literal `%%` escape.
         Escape,
@@ -28,6 +28,23 @@ pub mod printf {
             }
         }
 
+        pub fn position(&self) -> Option<(usize, usize)> {
+            match *self {
+                Substitution::Format(ref fmt) => Some(fmt.position),
+                _ => None,
+            }
+        }
+
+        pub fn set_position(&mut self, start: usize, end: usize) {
+            match self {
+                Substitution::Format(ref mut fmt) => {
+                    fmt.position = (start, end);
+                }
+                _ => {}
+            }
+        }
+
+
         /// Translate this substitution into an equivalent Rust formatting directive.
         ///
         /// This ignores cases where the substitution does not have an exact equivalent, or where
@@ -57,6 +74,8 @@ pub mod printf {
         pub length: Option<&'a str>,
         /// Type of parameter being converted.
         pub type_: &'a str,
+        /// Byte offset for the start and end of this formatting directive.
+        pub position: (usize, usize),
     }
 
     impl<'a> Format<'a> {
@@ -257,19 +276,28 @@ pub mod printf {
     pub fn iter_subs(s: &str) -> Substitutions {
         Substitutions {
             s,
+            pos: 0,
         }
     }
 
     /// Iterator over substitutions in a string.
     pub struct Substitutions<'a> {
         s: &'a str,
+        pos: usize,
     }
 
     impl<'a> Iterator for Substitutions<'a> {
         type Item = Substitution<'a>;
         fn next(&mut self) -> Option<Self::Item> {
-            let (sub, tail) = parse_next_substitution(self.s)?;
+            let (mut sub, tail) = parse_next_substitution(self.s)?;
             self.s = tail;
+            match sub {
+                Substitution::Format(_) => if let Some((start, end)) = sub.position() {
+                    sub.set_position(start + self.pos, end + self.pos);
+                    self.pos += end;
+                }
+                Substitution::Escape => self.pos += 2,
+            }
             Some(sub)
         }
 
@@ -301,7 +329,9 @@ pub mod printf {
                 _ => {/* fall-through */},
             }
 
-            Cur::new_at_start(&s[start..])
+            //let _ = Cur::new_at_start_with_pos(&s[..], start);
+            //Cur::new_at_start(&s[start..])
+            Cur::new_at_start_with_pos(&s[..], start)
         };
 
         // This is meant to be a translation of the following regex:
@@ -355,6 +385,7 @@ pub mod printf {
                     precision: None,
                     length: None,
                     type_: at.slice_between(next).unwrap(),
+                    position: (start.at, next.at),
                 }),
                 next.slice_after()
             ));
@@ -541,6 +572,7 @@ pub mod printf {
         drop(next);
 
         end = at;
+        let position = (start.at, end.at);
 
         let f = Format {
             span: start.slice_between(end).unwrap(),
@@ -550,6 +582,7 @@ pub mod printf {
             precision,
             length,
             type_,
+            position,
         };
         Some((Substitution::Format(f), end.slice_after()))
     }
@@ -755,6 +788,12 @@ pub mod shell {
             }
         }
 
+        pub fn position(&self) -> Option<(usize, usize)> {
+            match *self {
+                _ => None,
+            }
+        }
+
         pub fn translate(&self) -> Option<String> {
             match *self {
                 Substitution::Ordinal(n) => Some(format!("{{{}}}", n)),
@@ -918,7 +957,7 @@ mod strcursor {
 
     pub struct StrCursor<'a> {
         s: &'a str,
-        at: usize,
+        pub at: usize,
     }
 
     impl<'a> StrCursor<'a> {
@@ -929,6 +968,13 @@ mod strcursor {
             }
         }
 
+        pub fn new_at_start_with_pos(s: &'a str, at: usize) -> StrCursor<'a> {
+            StrCursor {
+                s,
+                at,
+            }
+        }
+
         pub fn at_next_cp(mut self) -> Option<StrCursor<'a>> {
             match self.try_seek_right_cp() {
                 true => Some(self),
diff --git a/src/test/ui/ifmt-bad-arg.stderr b/src/test/ui/ifmt-bad-arg.stderr
index 92c44cf406b..a126998355e 100644
--- a/src/test/ui/ifmt-bad-arg.stderr
+++ b/src/test/ui/ifmt-bad-arg.stderr
@@ -178,9 +178,10 @@ error: argument never used
   --> $DIR/ifmt-bad-arg.rs:66:27
    |
 LL |     format!("foo %s baz", "bar"); //~ ERROR: argument never used
-   |                           ^^^^^
+   |                  --       ^^^^^
+   |                  |
+   |                  help: format specifiers in Rust are written using `{}`: `{}`
    |
-   = help: `%s` should be written as `{}`
    = note: printf formatting not supported; see the documentation for `std::fmt`
 
 error: there is no argument named `foo`
diff --git a/src/test/ui/macros/format-foreign.rs b/src/test/ui/macros/format-foreign.rs
index ec0eaed43ae..33401424c9a 100644
--- a/src/test/ui/macros/format-foreign.rs
+++ b/src/test/ui/macros/format-foreign.rs
@@ -11,6 +11,11 @@
 fn main() {
     println!("%.*3$s %s!\n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments
     println!("%1$*2$.*3$f", 123.456); //~ ERROR never used
+    println!(r###"%.*3$s
+        %s!\n
+"###, "Hello,", "World", 4);
+    //~^ ERROR multiple unused formatting arguments
+    // correctly account for raw strings in inline suggestions
 
     // This should *not* produce hints, on the basis that there's equally as
     // many "correct" format specifiers.  It's *probably* just an actual typo.
diff --git a/src/test/ui/macros/format-foreign.stderr b/src/test/ui/macros/format-foreign.stderr
index 83ff301dc19..93e68183b14 100644
--- a/src/test/ui/macros/format-foreign.stderr
+++ b/src/test/ui/macros/format-foreign.stderr
@@ -6,27 +6,48 @@ LL |     println!("%.*3$s %s!/n", "Hello,", "World", 4); //~ ERROR multiple unus
    |              |
    |              multiple missing formatting specifiers
    |
-   = help: `%.*3$s` should be written as `{:.2$}`
-   = help: `%s` should be written as `{}`
    = note: printf formatting not supported; see the documentation for `std::fmt`
+help: format specifiers in Rust are written using `{}`
+   |
+LL |     println!("{:.2$} {}!/n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments
+   |               ^^^^^^ ^^
 
 error: argument never used
   --> $DIR/format-foreign.rs:13:29
    |
 LL |     println!("%1$*2$.*3$f", 123.456); //~ ERROR never used
-   |                             ^^^^^^^
+   |               -----------   ^^^^^^^
+   |               |
+   |               help: format specifiers in Rust are written using `{}`: `{0:1$.2$}`
+   |
+   = note: printf formatting not supported; see the documentation for `std::fmt`
+
+error: multiple unused formatting arguments
+  --> $DIR/format-foreign.rs:16:7
+   |
+LL |       println!(r###"%.*3$s
+   |  ______________-
+LL | |         %s!/n
+LL | | "###, "Hello,", "World", 4);
+   | |    -  ^^^^^^^^  ^^^^^^^  ^
+   | |____|
+   |      multiple missing formatting specifiers
    |
-   = help: `%1$*2$.*3$f` should be written as `{0:1$.2$}`
    = note: printf formatting not supported; see the documentation for `std::fmt`
+help: format specifiers in Rust are written using `{}`
+   |
+LL |     println!(r###"{:.2$}
+LL |         {}!/n
+   |
 
 error: argument never used
-  --> $DIR/format-foreign.rs:17:30
+  --> $DIR/format-foreign.rs:22:30
    |
 LL |     println!("{} %f", "one", 2.0); //~ ERROR never used
    |                              ^^^
 
 error: named argument never used
-  --> $DIR/format-foreign.rs:19:39
+  --> $DIR/format-foreign.rs:24:39
    |
 LL |     println!("Hi there, $NAME.", NAME="Tim"); //~ ERROR never used
    |                                       ^^^^^
@@ -34,5 +55,5 @@ LL |     println!("Hi there, $NAME.", NAME="Tim"); //~ ERROR never used
    = help: `$NAME` should be written as `{NAME}`
    = note: shell formatting not supported; see the documentation for `std::fmt`
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors