about summary refs log tree commit diff
path: root/src/librustc_span
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-02-26 02:07:11 +0100
committerGitHub <noreply@github.com>2020-02-26 02:07:11 +0100
commit41bf200073da3413e9f4b71db5122bee09e5e88d (patch)
treee6202cd26a182d436cae8cd612ae8b343f933c49 /src/librustc_span
parent0860f5aebd1939b821cf9af5ba568cbd6be8077e (diff)
parente355a330772a7b605649651f7e670ea636e73a6e (diff)
Rollup merge of #69387 - petrochenkov:idprint, r=Mark-Simulacrum
Deduplicate identifier printing a bit

https://github.com/rust-lang/rust/pull/67010 introduced a couple more subtly different ways to print an identifier.
This PR attempts to restore the order.

The most basic identifier printing interface is `Formatter`-based now, so `String`s are not allocated unless required.

r? @Mark-Simulacrum
Diffstat (limited to 'src/librustc_span')
-rw-r--r--src/librustc_span/symbol.rs67
1 files changed, 59 insertions, 8 deletions
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index 97708d91d7e..b8e5ea97f4e 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -893,19 +893,17 @@ impl Hash for Ident {
 
 impl fmt::Debug for Ident {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.is_raw_guess() {
-            write!(f, "r#")?;
-        }
-        write!(f, "{}{:?}", self.name, self.span.ctxt())
+        fmt::Display::fmt(self, f)?;
+        fmt::Debug::fmt(&self.span.ctxt(), f)
     }
 }
 
+/// This implementation is supposed to be used in error messages, so it's expected to be identical
+/// to printing the original identifier token written in source code (`token_to_string`),
+/// except that AST identifiers don't keep the rawness flag, so we have to guess it.
 impl fmt::Display for Ident {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.is_raw_guess() {
-            write!(f, "r#")?;
-        }
-        fmt::Display::fmt(&self.name, f)
+        fmt::Display::fmt(&IdentPrinter::new(self.name, self.is_raw_guess(), None), f)
     }
 }
 
@@ -929,6 +927,59 @@ impl UseSpecializedDecodable for Ident {
     }
 }
 
+/// This is the most general way to print identifiers.
+/// AST pretty-printer is used as a fallback for turning AST structures into token streams for
+/// proc macros. Additionally, proc macros may stringify their input and expect it survive the
+/// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
+/// So we need to somehow pretty-print `$crate` in a way preserving at least some of its
+/// hygiene data, most importantly name of the crate it refers to.
+/// As a result we print `$crate` as `crate` if it refers to the local crate
+/// and as `::other_crate_name` if it refers to some other crate.
+/// Note, that this is only done if the ident token is printed from inside of AST pretty-pringing,
+/// but not otherwise. Pretty-printing is the only way for proc macros to discover token contents,
+/// so we should not perform this lossy conversion if the top level call to the pretty-printer was
+/// done for a token stream or a single token.
+pub struct IdentPrinter {
+    symbol: Symbol,
+    is_raw: bool,
+    /// Span used for retrieving the crate name to which `$crate` refers to,
+    /// if this field is `None` then the `$crate` conversion doesn't happen.
+    convert_dollar_crate: Option<Span>,
+}
+
+impl IdentPrinter {
+    /// The most general `IdentPrinter` constructor. Do not use this.
+    pub fn new(symbol: Symbol, is_raw: bool, convert_dollar_crate: Option<Span>) -> IdentPrinter {
+        IdentPrinter { symbol, is_raw, convert_dollar_crate }
+    }
+
+    /// This implementation is supposed to be used when printing identifiers
+    /// as a part of pretty-printing for larger AST pieces.
+    /// Do not use this either.
+    pub fn for_ast_ident(ident: Ident, is_raw: bool) -> IdentPrinter {
+        IdentPrinter::new(ident.name, is_raw, Some(ident.span))
+    }
+}
+
+impl fmt::Display for IdentPrinter {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if self.is_raw {
+            f.write_str("r#")?;
+        } else {
+            if self.symbol == kw::DollarCrate {
+                if let Some(span) = self.convert_dollar_crate {
+                    let converted = span.ctxt().dollar_crate_name();
+                    if !converted.is_path_segment_keyword() {
+                        f.write_str("::")?;
+                    }
+                    return fmt::Display::fmt(&converted, f);
+                }
+            }
+        }
+        fmt::Display::fmt(&self.symbol, f)
+    }
+}
+
 /// An interned string.
 ///
 /// Internally, a `Symbol` is implemented as an index, and all operations