1#![allow(clippy::assertions_on_result_states)]
2
3use proc_macro2::{Delimiter, Literal, Spacing, TokenStream, TokenTree};
4
5// #[doc = "..."] -> "..."
6fn lit_of_outer_doc_comment(tokens: &TokenStream) -> Literal {
7    lit_of_doc_comment(tokens, false)
8}
9
10// #![doc = "..."] -> "..."
11fn lit_of_inner_doc_comment(tokens: &TokenStream) -> Literal {
12    lit_of_doc_comment(tokens, true)
13}
14
15fn lit_of_doc_comment(tokens: &TokenStream, inner: bool) -> Literal {
16    let mut iter = tokens.clone().into_iter();
17    match iter.next().unwrap() {
18        TokenTree::Punct(punct) => {
19            assert_eq!(punct.as_char(), '#');
20            assert_eq!(punct.spacing(), Spacing::Alone);
21        }
22        _ => panic!("wrong token {:?}", tokens),
23    }
24    if inner {
25        match iter.next().unwrap() {
26            TokenTree::Punct(punct) => {
27                assert_eq!(punct.as_char(), '!');
28                assert_eq!(punct.spacing(), Spacing::Alone);
29            }
30            _ => panic!("wrong token {:?}", tokens),
31        }
32    }
33    iter = match iter.next().unwrap() {
34        TokenTree::Group(group) => {
35            assert_eq!(group.delimiter(), Delimiter::Bracket);
36            assert!(iter.next().is_none(), "unexpected token {:?}", tokens);
37            group.stream().into_iter()
38        }
39        _ => panic!("wrong token {:?}", tokens),
40    };
41    match iter.next().unwrap() {
42        TokenTree::Ident(ident) => assert_eq!(ident.to_string(), "doc"),
43        _ => panic!("wrong token {:?}", tokens),
44    }
45    match iter.next().unwrap() {
46        TokenTree::Punct(punct) => {
47            assert_eq!(punct.as_char(), '=');
48            assert_eq!(punct.spacing(), Spacing::Alone);
49        }
50        _ => panic!("wrong token {:?}", tokens),
51    }
52    match iter.next().unwrap() {
53        TokenTree::Literal(literal) => {
54            assert!(iter.next().is_none(), "unexpected token {:?}", tokens);
55            literal
56        }
57        _ => panic!("wrong token {:?}", tokens),
58    }
59}
60
61#[test]
62fn closed_immediately() {
63    let stream = "/**/".parse::<TokenStream>().unwrap();
64    let tokens = stream.into_iter().collect::<Vec<_>>();
65    assert!(tokens.is_empty(), "not empty -- {:?}", tokens);
66}
67
68#[test]
69fn incomplete() {
70    assert!("/*/".parse::<TokenStream>().is_err());
71}
72
73#[test]
74fn lit() {
75    let stream = "/// doc".parse::<TokenStream>().unwrap();
76    let lit = lit_of_outer_doc_comment(&stream);
77    assert_eq!(lit.to_string(), "\" doc\"");
78
79    let stream = "//! doc".parse::<TokenStream>().unwrap();
80    let lit = lit_of_inner_doc_comment(&stream);
81    assert_eq!(lit.to_string(), "\" doc\"");
82
83    let stream = "/** doc */".parse::<TokenStream>().unwrap();
84    let lit = lit_of_outer_doc_comment(&stream);
85    assert_eq!(lit.to_string(), "\" doc \"");
86
87    let stream = "/*! doc */".parse::<TokenStream>().unwrap();
88    let lit = lit_of_inner_doc_comment(&stream);
89    assert_eq!(lit.to_string(), "\" doc \"");
90}
91
92#[test]
93fn carriage_return() {
94    let stream = "///\r\n".parse::<TokenStream>().unwrap();
95    let lit = lit_of_outer_doc_comment(&stream);
96    assert_eq!(lit.to_string(), "\"\"");
97
98    let stream = "/**\r\n*/".parse::<TokenStream>().unwrap();
99    let lit = lit_of_outer_doc_comment(&stream);
100    assert_eq!(lit.to_string(), "\"\\r\\n\"");
101
102    "///\r".parse::<TokenStream>().unwrap_err();
103    "///\r \n".parse::<TokenStream>().unwrap_err();
104    "/**\r \n*/".parse::<TokenStream>().unwrap_err();
105}
106