Correctly format `::` and `:` in generated Rust code.
This also means, in a followup CL, we can get rid of `rust_std` and instead use `::std`.
PiperOrigin-RevId: 459736625
diff --git a/common/token_stream_printer.rs b/common/token_stream_printer.rs
index 71d6502..72928c4 100644
--- a/common/token_stream_printer.rs
+++ b/common/token_stream_printer.rs
@@ -137,12 +137,13 @@
_ => {
write!(result, "{}", tt)?;
- // Insert spaces between tokens only when they are needed to separate
- // identifiers or literals from each other.
- if is_ident_or_literal(&tt)
- && matches!(it.peek(), Some(tt_next) if is_ident_or_literal(tt_next))
- {
- write!(result, " ")?;
+ // Insert spaces between tokens when they are needed to separate tokens.
+ // In particular, `a b` is different than `ab`, and `: ::` is different from
+ // `:::`.
+ if let Some(tt_next) = it.peek() {
+ if tokens_require_whitespace(&tt, tt_next) {
+ write!(result, " ")?;
+ }
}
}
}
@@ -150,6 +151,25 @@
Ok(())
}
+/// Returns true if token1 and token2 should have whitespace between them, and
+/// false if they should not.
+///
+/// For example, `a b` is different than `ab`, and `: ::` is different from
+/// `:::`.
+fn tokens_require_whitespace(token1: &TokenTree, token2: &TokenTree) -> bool {
+ if is_ident_or_literal(token1) && is_ident_or_literal(token2) {
+ return true;
+ }
+ match (token1, token2) {
+ (TokenTree::Punct(p1), TokenTree::Punct(p2)) => {
+ p1.spacing() == proc_macro2::Spacing::Alone
+ && p1.as_char() == ':'
+ && p2.as_char() == ':'
+ }
+ _ => false,
+ }
+}
+
fn is_ident_or_literal(tt: &TokenTree) -> bool {
match tt {
TokenTree::Ident(id) => id != "__NEWLINE__" && id != "__SPACE__",
@@ -217,6 +237,13 @@
Ok(())
}
+ /// `foo : ::bar` is valid syntax, but `foo:::bar` is not.
+ #[test]
+ fn test_paamayim_nekudotayim() -> Result<()> {
+ assert_eq!(tokens_to_string(quote! { x : ::y })?, "x: ::y");
+ Ok(())
+ }
+
#[test]
fn test_newline_token() -> Result<()> {
let token_stream = quote! { a __NEWLINE__ b };
@@ -275,7 +302,9 @@
"///hello\nstruct X {}\n"
);
assert_eq!(
- rs_tokens_to_formatted_string_for_tests(quote! { #[doc = "hello\nworld"] struct X {} })?,
+ rs_tokens_to_formatted_string_for_tests(
+ quote! { #[doc = "hello\nworld"] struct X {} }
+ )?,
"///hello\n///world\nstruct X {}\n"
);
Ok(())
@@ -288,7 +317,9 @@
"/// hello\nstruct X {}\n"
);
assert_eq!(
- rs_tokens_to_formatted_string_for_tests(quote! { #[doc = " hello\n world"] struct X {} })?,
+ rs_tokens_to_formatted_string_for_tests(
+ quote! { #[doc = " hello\n world"] struct X {} }
+ )?,
"/// hello\n/// world\nstruct X {}\n"
);
Ok(())