Repository: dtolnay/thiserror (rust)

Issue:
Tuple Struct interior can't be accessed via .0.0
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ada5458f736b0dd393bbe31e10b4ff31

```rust
use thiserror::Error;

#[derive(Debug)]
pub struct S1(String);

#[derive(Debug)]
pub struct S2 {
    inner: String,
}

#[derive(Debug, Error)]
pub enum BigError {
    #[error("{:?}", .0)]
    E1(S1),
    #[error("{}", .0.inner)]
    E2(S2),
    
    #[error("{}", .0.0)]
    E3(S2),
}

fn main() {
}
```

E1 and E2 compile.
E3 fails to compile with 

```
error: expected expression, found `.`
  --> src/main.rs:18:19
   |
18 |     #[error("{}", .0.0)]
   |                   ^ expected expression
```

I found it a bit surprising - it seems like it should work.

Pull request:
Support `.0.0` nested tuple index
Fixes #309

Patch:
diff --git a/impl/src/attr.rs b/impl/src/attr.rs
index 395edb1..e0ac02b 100644
--- a/impl/src/attr.rs
+++ b/impl/src/attr.rs
@@ -1,11 +1,11 @@
-use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
+use proc_macro2::{Delimiter, Group, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
 use quote::{format_ident, quote, ToTokens};
 use std::collections::BTreeSet as Set;
 use syn::parse::discouraged::Speculative;
 use syn::parse::ParseStream;
 use syn::{
-    braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitInt, LitStr, Meta,
-    Result, Token,
+    braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitFloat, LitInt,
+    LitStr, Meta, Result, Token,
 };
 
 pub struct Attrs<'a> {
@@ -145,14 +145,42 @@ fn parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result<TokenStr
                 input.parse::<Token![.]>()?;
                 begin_expr = false;
                 continue;
-            }
-            if input.peek2(LitInt) {
+            } else if input.peek2(LitInt) {
                 input.parse::<Token![.]>()?;
                 let int: Index = input.parse()?;
-                let ident = format_ident!("_{}", int.index, span = int.span);
-                tokens.push(TokenTree::Ident(ident));
+                tokens.push({
+                    let ident = format_ident!("_{}", int.index, span = int.span);
+                    TokenTree::Ident(ident)
+                });
                 begin_expr = false;
                 continue;
+            } else if input.peek2(LitFloat) {
+                let ahead = input.fork();
+                ahead.parse::<Token![.]>()?;
+                let float: LitFloat = ahead.parse()?;
+                let repr = float.to_string();
+                let mut indices = repr.split('.').map(syn::parse_str::<Index>);
+                if let (Some(Ok(first)), Some(Ok(second)), None) =
+                    (indices.next(), indices.next(), indices.next())
+                {
+                    input.advance_to(&ahead);
+                    tokens.push({
+                        let ident = format_ident!("_{}", first, span = float.span());
+                        TokenTree::Ident(ident)
+                    });
+                    tokens.push({
+                        let mut punct = Punct::new('.', Spacing::Alone);
+                        punct.set_span(float.span());
+                        TokenTree::Punct(punct)
+                    });
+                    tokens.push({
+                        let mut literal = Literal::u32_unsuffixed(second.index);
+                        literal.set_span(float.span());
+                        TokenTree::Literal(literal)
+                    });
+                    begin_expr = false;
+                    continue;
+                }
             }
         }
 


Test patch:
diff --git a/tests/test_display.rs b/tests/test_display.rs
index c05d157..7a9057c 100644
--- a/tests/test_display.rs
+++ b/tests/test_display.rs
@@ -234,6 +234,18 @@ fn test_field() {
     assert("0", Error(Inner { data: 0 }));
 }
 
+#[test]
+fn test_nested_tuple_field() {
+    #[derive(Debug)]
+    struct Inner(usize);
+
+    #[derive(Error, Debug)]
+    #[error("{}", .0.0)]
+    struct Error(Inner);
+
+    assert("0", Error(Inner(0)));
+}
+
 #[test]
 fn test_macro_rules() {
     // Regression test for https://github.com/dtolnay/thiserror/issues/86