1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use std::borrow::Cow;
use proc_macro2::{Span, TokenStream};
use quote::{TokenStreamExt as _, format_ident, quote, quote_spanned};
use syn::{parse_quote, spanned::Spanned as _};
use crate::{Alias, alias, syn_util, version};
#[repr(transparent)]
#[derive(Debug)]
pub(crate) struct Public<'map>(Alias<'map>);
impl<'map> Public<'map> {
pub(crate) fn new(alias: Alias<'map>) -> Self {
Self(alias)
}
}
impl<'map> quote::ToTokens for Public<'map> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let Alias {
map,
path,
index,
ref arguments,
kind,
} = self.0;
let ident = index.ident(path.friendly_path());
let visibility = map.visibility();
let super_visibility = syn_util::super_visibility(visibility);
// If the alias is for Self, we have a full path, and don't need any glob shenanigans
let alias_tokens = if index == alias::Index::Primary {
let aliased_type_path = &path.truncated_path;
quote!(#super_visibility use #aliased_type_path as #ident;)
} else {
// Traits cannot have `type` aliases, so we can't do conditional macro combination
// or the visibility workaround
let use_vis_workaround = kind != alias::Kind::Trait;
let ident_internal = index.ident_internal(path.friendly_path());
let aliased_path = &path.truncated_path;
let span = aliased_path.span();
let parameters = &arguments.args;
let unique_ident = map.unique_ident();
let alias_unique_ident = format_ident!("{unique_ident}_{ident}");
let macro_maker_ident = format_ident!("make_{alias_unique_ident}");
let submodule_ident = format_ident!("{ident}_mod");
let macro_vis = syn_util::visibility_macro_export(map.visibility());
let telety_path_override = map.telety_path();
let telety_path = telety_path_override
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(parse_quote!(::telety)));
if use_vis_workaround {
// We are allowed to export items *in* private modules at their original visibility. e.g.
// ```rust
// use my_crate;
// pub use my_crate::MyPubStruct;
// ```
// But we can't re-export items themselves at greater visibility than our import, even if their
// original visibility is greater. This is invalid:
// ```rust
// use my_crate::MyPubStruct;
// pub use MyPubStruct as MyPubReexport;
// ```
// We can work around this, but it requires 2 extra exported macros per item, so prefer the simple
// way if we have a multi-segment path as our type.
if aliased_path.segments.len() == 1 {
let needle = syn::Ident::new("__needle", Span::call_site());
let haystack = quote! {
#[doc(hidden)]
#macro_vis
macro_rules! #alias_unique_ident {
($($tokens:tt)*) => {
#telety_path::__private::crateify! {
#needle!($($tokens)*);
};
};
}
};
let mut exported_apply = version::v0::PATH
.apply(parse_quote!(self::exact::#ident), needle.clone(), &haystack)
.with_fallback(TokenStream::new())
.with_macro_forwarding(macro_maker_ident);
if let Some(telety_path_override) = telety_path_override {
exported_apply =
exported_apply.with_telety_path(telety_path_override.clone());
}
quote! {
// Create an exported macro. If the type's macro existed, it is a forwarder.
// If it did not exist, it is a noop
#exported_apply
// Create an alias for just the type
#super_visibility type #alias_unique_ident #parameters = self::exact::#ident_internal #parameters;
#super_visibility use #alias_unique_ident as #ident;
}
} else {
let super2_visibility = syn_util::super_visibility(&super_visibility);
let super3_visibility = syn_util::super_visibility(&super2_visibility);
quote_spanned! { span =>
// Setup for a glob import
mod #submodule_ident {
#super2_visibility use super::exact::#ident as #ident;
pub(super) mod globbed {
// Use the macro if it exists. The type will be imported, but...
#super3_visibility use super::*;
// It is overwritten by our 'reduced generics' type alias
#super3_visibility type #ident #parameters = super::super::exact::#ident_internal #parameters;
}
}
#super_visibility use #submodule_ident::globbed::#ident;
}
}
} else {
// For traits, we can't use the `type` workaround, so correct visibility
// must be explicity confirmed. If we made it this far, the user has OKed
// aliasing this trait, so they can deal with the compile error if the
// visibility is incorrect.
quote_spanned! { span =>
#super_visibility use self::exact::#ident as #ident;
}
}
};
tokens.append_all(alias_tokens);
}
}