Repository: Aleph-Alpha/ts-rs
Pull request #267 (2024-03-13 18:21:11)

Add leading colon to `ts_rs` path in macro expansion
## Goal

If, for some (very bespoke and cursed) reason, a user creates a `mod` called `ts_rs` and has it in scope, it becomes impossible to
derive `TS`, all the `ts_rs::<something>` paths will look inside that `mod` instead of our library.
This PR aims to make such (cursed) code compile

## Changes

All paths to `ts_rs` generated by `ts_rs_macros` now have two leading colons, e.g.: `::ts_rs::TS`

## Checklist

- [x] I have followed the steps listed in the [Contributing guide](https://github.com/Aleph-Alpha/ts-rs/blob/main/CHANGELOG.md).
- [x] I have added or updated the tests related to the changes made.



Patch:
diff --git a/macros/src/deps.rs b/macros/src/deps.rs
index 1fd2563..630fda7 100644
--- a/macros/src/deps.rs
+++ b/macros/src/deps.rs
@@ -12,7 +12,7 @@ impl Dependencies {
     /// Adds all dependencies from the given type
     pub fn append_from(&mut self, ty: &Type) {
         self.dependencies
-            .push(quote![.extend(<#ty as ts_rs::TS>::dependency_types())]);
+            .push(quote![.extend(<#ty as ::ts_rs::TS>::dependency_types())]);
         self.types.push(ty.clone());
     }
 
@@ -20,7 +20,7 @@ impl Dependencies {
     pub fn push(&mut self, ty: &Type) {
         self.dependencies.push(quote![.push::<#ty>()]);
         self.dependencies.push(quote![
-            .extend(<#ty as ts_rs::TS>::generics())
+            .extend(<#ty as ::ts_rs::TS>::generics())
         ]);
         self.types.push(ty.clone());
     }
@@ -35,7 +35,7 @@ impl ToTokens for Dependencies {
     fn to_tokens(&self, tokens: &mut TokenStream) {
         let lines = &self.dependencies;
         tokens.extend(quote![{
-            use ts_rs::typelist::TypeList;
+            use ::ts_rs::typelist::TypeList;
             ()#(#lines)*
         }])
     }
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 08376e5..0ddb662 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -83,7 +83,7 @@ impl DerivedTS {
                 #output_path_fn
 
                 #[allow(clippy::unused_unit)]
-                fn dependency_types() -> impl ts_rs::typelist::TypeList
+                fn dependency_types() -> impl ::ts_rs::typelist::TypeList
                 where
                     Self: 'static,
                 {
@@ -103,7 +103,7 @@ impl DerivedTS {
             .type_params()
             .filter(|ty| !self.concrete.contains_key(&ty.ident))
             .map(|ty| &ty.ident)
-            .map(|generic| quote!(<#generic as ts_rs::TS>::name()))
+            .map(|generic| quote!(<#generic as ::ts_rs::TS>::name()))
             .peekable();
 
         if generics_ts_names.peek().is_some() {
@@ -163,10 +163,10 @@ impl DerivedTS {
         let generic_params = generics
             .type_params()
             .map(|ty| match self.concrete.get(&ty.ident) {
-                None => quote! { ts_rs::Dummy },
+                None => quote! { ::ts_rs::Dummy },
                 Some(ty) => quote! { #ty },
             });
-        let ty = quote!(<#rust_ty<#(#generic_params),*> as ts_rs::TS>);
+        let ty = quote!(<#rust_ty<#(#generic_params),*> as ::ts_rs::TS>);
 
         quote! {
             #[cfg(test)]
@@ -181,14 +181,14 @@ impl DerivedTS {
         let generics = generics
             .type_params()
             .filter(|ty| !self.concrete.contains_key(&ty.ident))
-            .map(|TypeParam { ident, .. }| quote![.push::<#ident>().extend(<#ident as ts_rs::TS>::generics())]);
+            .map(|TypeParam { ident, .. }| quote![.push::<#ident>().extend(<#ident as ::ts_rs::TS>::generics())]);
         quote! {
             #[allow(clippy::unused_unit)]
-            fn generics() -> impl ts_rs::typelist::TypeList
+            fn generics() -> impl ::ts_rs::typelist::TypeList
             where
                 Self: 'static,
             {
-                use ts_rs::typelist::TypeList;
+                use ::ts_rs::typelist::TypeList;
                 ()#(#generics)*
             }
         }
@@ -264,7 +264,7 @@ impl DerivedTS {
             }
             fn decl() -> String {
                 #generic_types
-                let inline = <#rust_ty<#(#generic_idents,)*> as ts_rs::TS>::inline();
+                let inline = <#rust_ty<#(#generic_idents,)*> as ::ts_rs::TS>::inline();
                 let generics = #ts_generics;
                 format!("type {}{generics} = {inline};", #name)
             }
@@ -281,7 +281,7 @@ fn generate_assoc_type(
 
     let generics_params = generics.params.iter().map(|x| match x {
         G::Type(ty) => match concrete.get(&ty.ident) {
-            None => quote! { ts_rs::Dummy },
+            None => quote! { ::ts_rs::Dummy },
             Some(ty) => quote! { #ty },
         },
         G::Const(ConstParam { ident, .. }) => quote! { #ident },
@@ -326,7 +326,7 @@ fn generate_impl_block_header(
     });
 
     let where_bound = generate_where_clause(generics, dependencies);
-    quote!(impl <#(#bounds),*> ts_rs::TS for #ty <#(#type_args),*> #where_bound)
+    quote!(impl <#(#bounds),*> ::ts_rs::TS for #ty <#(#type_args),*> #where_bound)
 }
 
 fn generate_where_clause(generics: &Generics, dependencies: &Dependencies) -> WhereClause {
@@ -342,7 +342,7 @@ fn generate_where_clause(generics: &Generics, dependencies: &Dependencies) -> Wh
 
     let existing = generics.where_clause.iter().flat_map(|w| &w.predicates);
     parse_quote! {
-        where #(#existing,)* #(#used_types: ts_rs::TS),*
+        where #(#existing,)* #(#used_types: ::ts_rs::TS),*
     }
 }
 
diff --git a/macros/src/types/enum.rs b/macros/src/types/enum.rs
index 4d87b64..f94a6a7 100644
--- a/macros/src/types/enum.rs
+++ b/macros/src/types/enum.rs
@@ -118,11 +118,11 @@ fn format_variant(
                         }
                         (Some(type_override), None) => quote! { #type_override },
                         (None, Some(type_as)) => {
-                            quote!(<#type_as as ts_rs::TS>::name())
+                            quote!(<#type_as as ::ts_rs::TS>::name())
                         }
                         (None, None) => {
                             let ty = &unnamed.unnamed[0].ty;
-                            quote!(<#ty as ts_rs::TS>::name())
+                            quote!(<#ty as ::ts_rs::TS>::name())
                         }
                     };
 
@@ -169,11 +169,11 @@ fn format_variant(
                             }
                             (Some(type_override), None) => quote! { #type_override },
                             (None, Some(type_as)) => {
-                                quote!(<#type_as as ts_rs::TS>::name())
+                                quote!(<#type_as as ::ts_rs::TS>::name())
                             }
                             (None, None) => {
                                 let ty = &unnamed.unnamed[0].ty;
-                                quote!(<#ty as ts_rs::TS>::name())
+                                quote!(<#ty as ::ts_rs::TS>::name())
                             }
                         };
 
diff --git a/macros/src/types/named.rs b/macros/src/types/named.rs
index 8cba5bb..c21c679 100644
--- a/macros/src/types/named.rs
+++ b/macros/src/types/named.rs
@@ -125,7 +125,7 @@ fn format_field(
             _ => {}
         }
 
-        flattened_fields.push(quote!(<#ty as ts_rs::TS>::inline_flattened()));
+        flattened_fields.push(quote!(<#ty as ::ts_rs::TS>::inline_flattened()));
         dependencies.append_from(ty);
         return Ok(());
     }
@@ -133,10 +133,10 @@ fn format_field(
     let formatted_ty = type_override.map(|t| quote!(#t)).unwrap_or_else(|| {
         if inline {
             dependencies.append_from(ty);
-            quote!(<#ty as ts_rs::TS>::inline())
+            quote!(<#ty as ::ts_rs::TS>::inline())
         } else {
             dependencies.push(ty);
-            quote!(<#ty as ts_rs::TS>::name())
+            quote!(<#ty as ::ts_rs::TS>::name())
         }
     });
     let field_name = to_ts_ident(field.ident.as_ref().unwrap());
diff --git a/macros/src/types/newtype.rs b/macros/src/types/newtype.rs
index df56a20..4d29fbd 100644
--- a/macros/src/types/newtype.rs
+++ b/macros/src/types/newtype.rs
@@ -58,8 +58,8 @@ pub(crate) fn newtype(attr: &StructAttr, name: &str, fields: &FieldsUnnamed) ->
 
     let inline_def = match type_override {
         Some(ref o) => quote!(#o.to_owned()),
-        None if inline => quote!(<#inner_ty as ts_rs::TS>::inline()),
-        None => quote!(<#inner_ty as ts_rs::TS>::name()),
+        None if inline => quote!(<#inner_ty as ::ts_rs::TS>::inline()),
+        None => quote!(<#inner_ty as ::ts_rs::TS>::name()),
     };
 
     Ok(DerivedTS {
diff --git a/macros/src/types/tuple.rs b/macros/src/types/tuple.rs
index d223279..b815b92 100644
--- a/macros/src/types/tuple.rs
+++ b/macros/src/types/tuple.rs
@@ -83,8 +83,8 @@ fn format_field(
 
     formatted_fields.push(match type_override {
         Some(ref o) => quote!(#o.to_owned()),
-        None if inline => quote!(<#ty as ts_rs::TS>::inline()),
-        None => quote!(<#ty as ts_rs::TS>::name()),
+        None if inline => quote!(<#ty as ::ts_rs::TS>::inline()),
+        None => quote!(<#ty as ::ts_rs::TS>::name()),
     });
 
     match (inline, type_override) {
diff --git a/macros/src/utils.rs b/macros/src/utils.rs
index 19325ce..c7c4bee 100644
--- a/macros/src/utils.rs
+++ b/macros/src/utils.rs
@@ -241,7 +241,7 @@ pub fn format_generics(
                 if let Some(default) = &type_param.default {
                     deps.push(default);
                     Some(quote!(
-                        format!("{} = {}", #ty, <#default as ts_rs::TS>::name())
+                        format!("{} = {}", #ty, <#default as ::ts_rs::TS>::name())
                     ))
                 } else {
                     Some(quote!(#ty.to_owned()))


Test patch:
diff --git a/ts-rs/tests/leading_colon.rs b/ts-rs/tests/leading_colon.rs
new file mode 100644
index 0000000..6ee31cd
--- /dev/null
+++ b/ts-rs/tests/leading_colon.rs
@@ -0,0 +1,10 @@
+#![allow(unused)]
+
+use ::ts_rs::TS;
+
+mod ts_rs {}
+
+#[derive(TS)]
+struct Foo {
+    x: u32
+}