about summary refs log tree commit diff
path: root/src/libsyntax_ext/format_foreign.rs
diff options
context:
space:
mode:
authorMark Rousskov <mark.simulacrum@gmail.com>2018-07-26 09:18:30 -0600
committerGitHub <noreply@github.com>2018-07-26 09:18:30 -0600
commit2aec4e882c6136ff34d931043fb16bd35abedc3e (patch)
treee0f5623aadc727d05f8e28de9250be4389d84efe /src/libsyntax_ext/format_foreign.rs
parent662fb069fd45d44c5828c335690598d712226325 (diff)
parent9a893cc2b82ac6259aead1319758404b80b8a959 (diff)
downloadrust-2aec4e882c6136ff34d931043fb16bd35abedc3e.tar.gz
rust-2aec4e882c6136ff34d931043fb16bd35abedc3e.zip
Rollup merge of #52649 - estebank:fmt-span, r=oli-obk
Point spans to inner elements of format strings

- Point at missing positional specifiers in string literal
```
error: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments)
  --> $DIR/ifmt-bad-arg.rs:34:38
   |
LL |     format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2);
   |                                      ^^ ^^ ^^
   |
   = note: positional arguments are zero-based
```

- Point at named formatting specifier in string literal
```
error: there is no argument named `foo`
  --> $DIR/ifmt-bad-arg.rs:37:17
   |
LL |     format!("{} {foo} {} {bar} {}", 1, 2, 3);
   |                 ^^^^^
```

- Update label for formatting string in "multiple unused formatting arguments" to be more correct
```
error: multiple unused formatting arguments
  --> $DIR/ifmt-bad-arg.rs:42:17
   |
LL |     format!("", 1, 2);               //~ ERROR: multiple unused formatting arguments
   |             --  ^  ^
   |             |
   |             multiple missing formatting specifiers
```

- When using `printf` string formatting, provide a structured suggestion instead of a note
```
error: multiple unused formatting arguments
  --> $DIR/format-foreign.rs:12:30
   |
LL |     println!("%.*3$s %s!/n", "Hello,", "World", 4); //~ ERROR multiple unused formatting arguments
   |              --------------  ^^^^^^^^  ^^^^^^^  ^
   |              |
   |              multiple missing formatting specifiers
   |
   = 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
   |               ^^^^^^ ^^
```
Diffstat (limited to 'src/libsyntax_ext/format_foreign.rs')
-rw-r--r--src/libsyntax_ext/format_foreign.rs102
1 files changed, 74 insertions, 28 deletions
diff --git a/src/libsyntax_ext/format_foreign.rs b/src/libsyntax_ext/format_foreign.rs
index 8ccb3be1ad9..23a37ca3485 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,7 @@ pub mod printf {
                 _ => {/* fall-through */},
             }
 
-            Cur::new_at_start(&s[start..])
+            Cur::new_at(&s[..], start)
         };
 
         // This is meant to be a translation of the following regex:
@@ -355,6 +383,7 @@ pub mod printf {
                     precision: None,
                     length: None,
                     type_: at.slice_between(next).unwrap(),
+                    position: (start.at, next.at),
                 }),
                 next.slice_after()
             ));
@@ -541,6 +570,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 +580,7 @@ pub mod printf {
             precision,
             length,
             type_,
+            position,
         };
         Some((Substitution::Format(f), end.slice_after()))
     }
@@ -616,6 +647,7 @@ pub mod printf {
                 ($in_:expr, {
                     $param:expr, $flags:expr,
                     $width:expr, $prec:expr, $len:expr, $type_:expr,
+                    $pos:expr,
                 }) => {
                     assert_eq!(
                         pns(concat!($in_, "!")),
@@ -628,6 +660,7 @@ pub mod printf {
                                 precision: $prec,
                                 length: $len,
                                 type_: $type_,
+                                position: $pos,
                             }),
                             "!"
                         ))
@@ -636,53 +669,53 @@ pub mod printf {
             }
 
             assert_pns_eq_sub!("%!",
-                { None, "", None, None, None, "!", });
+                { None, "", None, None, None, "!", (0, 2), });
             assert_pns_eq_sub!("%c",
-                { None, "", None, None, None, "c", });
+                { None, "", None, None, None, "c", (0, 2), });
             assert_pns_eq_sub!("%s",
-                { None, "", None, None, None, "s", });
+                { None, "", None, None, None, "s", (0, 2), });
             assert_pns_eq_sub!("%06d",
-                { None, "0", Some(N::Num(6)), None, None, "d", });
+                { None, "0", Some(N::Num(6)), None, None, "d", (0, 4), });
             assert_pns_eq_sub!("%4.2f",
-                { None, "", Some(N::Num(4)), Some(N::Num(2)), None, "f", });
+                { None, "", Some(N::Num(4)), Some(N::Num(2)), None, "f", (0, 5), });
             assert_pns_eq_sub!("%#x",
-                { None, "#", None, None, None, "x", });
+                { None, "#", None, None, None, "x", (0, 3), });
             assert_pns_eq_sub!("%-10s",
-                { None, "-", Some(N::Num(10)), None, None, "s", });
+                { None, "-", Some(N::Num(10)), None, None, "s", (0, 5), });
             assert_pns_eq_sub!("%*s",
-                { None, "", Some(N::Next), None, None, "s", });
+                { None, "", Some(N::Next), None, None, "s", (0, 3), });
             assert_pns_eq_sub!("%-10.*s",
-                { None, "-", Some(N::Num(10)), Some(N::Next), None, "s", });
+                { None, "-", Some(N::Num(10)), Some(N::Next), None, "s", (0, 7), });
             assert_pns_eq_sub!("%-*.*s",
-                { None, "-", Some(N::Next), Some(N::Next), None, "s", });
+                { None, "-", Some(N::Next), Some(N::Next), None, "s", (0, 6), });
             assert_pns_eq_sub!("%.6i",
-                { None, "", None, Some(N::Num(6)), None, "i", });
+                { None, "", None, Some(N::Num(6)), None, "i", (0, 4), });
             assert_pns_eq_sub!("%+i",
-                { None, "+", None, None, None, "i", });
+                { None, "+", None, None, None, "i", (0, 3), });
             assert_pns_eq_sub!("%08X",
-                { None, "0", Some(N::Num(8)), None, None, "X", });
+                { None, "0", Some(N::Num(8)), None, None, "X", (0, 4), });
             assert_pns_eq_sub!("%lu",
-                { None, "", None, None, Some("l"), "u", });
+                { None, "", None, None, Some("l"), "u", (0, 3), });
             assert_pns_eq_sub!("%Iu",
-                { None, "", None, None, Some("I"), "u", });
+                { None, "", None, None, Some("I"), "u", (0, 3), });
             assert_pns_eq_sub!("%I32u",
-                { None, "", None, None, Some("I32"), "u", });
+                { None, "", None, None, Some("I32"), "u", (0, 5), });
             assert_pns_eq_sub!("%I64u",
-                { None, "", None, None, Some("I64"), "u", });
+                { None, "", None, None, Some("I64"), "u", (0, 5), });
             assert_pns_eq_sub!("%'d",
-                { None, "'", None, None, None, "d", });
+                { None, "'", None, None, None, "d", (0, 3), });
             assert_pns_eq_sub!("%10s",
-                { None, "", Some(N::Num(10)), None, None, "s", });
+                { None, "", Some(N::Num(10)), None, None, "s", (0, 4), });
             assert_pns_eq_sub!("%-10.10s",
-                { None, "-", Some(N::Num(10)), Some(N::Num(10)), None, "s", });
+                { None, "-", Some(N::Num(10)), Some(N::Num(10)), None, "s", (0, 8), });
             assert_pns_eq_sub!("%1$d",
-                { Some(1), "", None, None, None, "d", });
+                { Some(1), "", None, None, None, "d", (0, 4), });
             assert_pns_eq_sub!("%2$.*3$d",
-                { Some(2), "", None, Some(N::Arg(3)), None, "d", });
+                { Some(2), "", None, Some(N::Arg(3)), None, "d", (0, 8), });
             assert_pns_eq_sub!("%1$*2$.*3$d",
-                { Some(1), "", Some(N::Arg(2)), Some(N::Arg(3)), None, "d", });
+                { Some(1), "", Some(N::Arg(2)), Some(N::Arg(3)), None, "d", (0, 11), });
             assert_pns_eq_sub!("%-8ld",
-                { None, "-", Some(N::Num(8)), None, Some("l"), "d", });
+                { None, "-", Some(N::Num(8)), None, Some("l"), "d", (0, 5), });
         }
 
         #[test]
@@ -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(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),