Repository: dtolnay/cxx (language: rust)

Issue:
Expansion of type_id fails if rename is used for cxx crate
```toml
[dependencies]
cxx_fork = { package = "cxx", version = "1.0" }
```

```rust
use cxx_fork::{type_id, ExternType};

unsafe impl ExternType for Foo {
    type Id = type_id!("Foo");
    type Kind = cxx_fork::kind::Opaque;
}
```

```console
error[E0433]: failed to resolve: could not find `cxx` in crate root
 --> src/main.rs:4:15
  |
4 |     type Id = type_id!("Foo");
  |               ^^^^^^^^^^^^^^^ could not find `cxx` in crate root
```

It should be possible to support this by making use of `$crate` inside of the macro.


Discussion / hints:

Fix patch:
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 639880d10..f88b602fe 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -10,6 +10,7 @@ use crate::syntax::{
     self, check, mangle, Api, Doc, Enum, ExternFn, ExternType, Impl, Lifetimes, Pair, Signature,
     Struct, Trait, Type, TypeAlias, Types,
 };
+use crate::type_id::Crate;
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote, quote_spanned, ToTokens};
 use std::mem;
@@ -1095,7 +1096,7 @@ fn type_id(name: &Pair) -> TokenStream {
     segments.extend(namespace_segments.cloned());
     segments.push(Ident::new(&name.cxx.to_string(), Span::call_site()));
     let qualified = QualifiedName { segments };
-    crate::type_id::expand(qualified)
+    crate::type_id::expand(Crate::Cxx, qualified)
 }
 
 fn expand_rust_box(ident: &Ident, types: &Types, explicit_impl: Option<&Impl>) -> TokenStream {
diff --git a/macro/src/lib.rs b/macro/src/lib.rs
index fc7828b15..a92eae74a 100644
--- a/macro/src/lib.rs
+++ b/macro/src/lib.rs
@@ -36,6 +36,7 @@ mod type_id;
 use crate::syntax::file::Module;
 use crate::syntax::namespace::Namespace;
 use crate::syntax::qualified::QualifiedName;
+use crate::type_id::Crate;
 use proc_macro::TokenStream;
 use syn::parse::{Parse, ParseStream, Parser, Result};
 use syn::parse_macro_input;
@@ -73,16 +74,22 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream {
         .into()
 }
 
+#[doc(hidden)]
 #[proc_macro]
 pub fn type_id(input: TokenStream) -> TokenStream {
-    struct TypeId(QualifiedName);
+    struct TypeId {
+        krate: Crate,
+        path: QualifiedName,
+    }
 
     impl Parse for TypeId {
         fn parse(input: ParseStream) -> Result<Self> {
-            QualifiedName::parse_quoted_or_unquoted(input).map(TypeId)
+            let krate = input.parse().map(Crate::DollarCrate)?;
+            let path = QualifiedName::parse_quoted_or_unquoted(input)?;
+            Ok(TypeId { krate, path })
         }
     }
 
     let arg = parse_macro_input!(input as TypeId);
-    type_id::expand(arg.0).into()
+    type_id::expand(arg.krate, arg.path).into()
 }
diff --git a/macro/src/type_id.rs b/macro/src/type_id.rs
index 5c5d9ccc5..74f0c1c92 100644
--- a/macro/src/type_id.rs
+++ b/macro/src/type_id.rs
@@ -1,9 +1,23 @@
 use crate::syntax::qualified::QualifiedName;
-use proc_macro2::TokenStream;
-use quote::{format_ident, quote};
+use proc_macro2::{TokenStream, TokenTree};
+use quote::{format_ident, quote, ToTokens};
+
+pub enum Crate {
+    Cxx,
+    DollarCrate(TokenTree),
+}
+
+impl ToTokens for Crate {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        match self {
+            Crate::Cxx => tokens.extend(quote!(::cxx)),
+            Crate::DollarCrate(krate) => krate.to_tokens(tokens),
+        }
+    }
+}
 
 // "folly::File" => `(f, o, l, l, y, (), F, i, l, e)`
-pub fn expand(arg: QualifiedName) -> TokenStream {
+pub fn expand(krate: Crate, arg: QualifiedName) -> TokenStream {
     let mut ids = Vec::new();
 
     for word in arg.segments {
@@ -14,11 +28,11 @@ pub fn expand(arg: QualifiedName) -> TokenStream {
             ids.push(match ch {
                 'A'..='Z' | 'a'..='z' => {
                     let t = format_ident!("{}", ch);
-                    quote!(::cxx::#t)
+                    quote!(#krate::#t)
                 }
                 '0'..='9' | '_' => {
                     let t = format_ident!("_{}", ch);
-                    quote!(::cxx::#t)
+                    quote!(#krate::#t)
                 }
                 _ => quote!([(); #ch as _]),
             });
diff --git a/src/lib.rs b/src/lib.rs
index 018582dce..0bba6a20d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -413,6 +413,7 @@ mod shared_ptr;
 #[path = "cxx_string.rs"]
 mod string;
 mod symbols;
+mod type_id;
 mod unique_ptr;
 mod unwind;
 #[path = "cxx_vector.rs"]
@@ -429,11 +430,6 @@ pub use crate::vector::CxxVector;
 pub use crate::weak_ptr::WeakPtr;
 pub use cxxbridge_macro::bridge;
 
-/// For use in impls of the `ExternType` trait. See [`ExternType`].
-///
-/// [`ExternType`]: trait.ExternType.html
-pub use cxxbridge_macro::type_id;
-
 /// Synonym for `CxxString`.
 ///
 /// To avoid confusion with Rust's standard library string you probably
@@ -466,6 +462,7 @@ pub mod private {
     pub use crate::unwind::catch_unwind;
     pub use crate::vector::VectorElement;
     pub use crate::weak_ptr::WeakPtrTarget;
+    pub use cxxbridge_macro::type_id;
 }
 
 mod actually_private {
diff --git a/src/type_id.rs b/src/type_id.rs
new file mode 100644
index 000000000..bd2b4ea61
--- /dev/null
+++ b/src/type_id.rs
@@ -0,0 +1,9 @@
+/// For use in impls of the `ExternType` trait. See [`ExternType`].
+///
+/// [`ExternType`]: trait.ExternType.html
+#[macro_export]
+macro_rules! type_id {
+    ($($path:tt)*) => {
+        $crate::private::type_id! { $crate $($path)* }
+    };
+}


Test patch: