1 //! Transforms syntax into `Path` objects, ideally with accounting for hygiene
5 use crate::type_ref
::ConstRefOrPath
;
8 use hir_expand
::name
::{name, AsName}
;
10 use syntax
::ast
::{self, AstNode, HasTypeBounds}
;
12 use super::AssociatedTypeBinding
;
15 path
::{GenericArg, GenericArgs, ModPath, Path, PathKind}
,
16 type_ref
::{LifetimeRef, TypeBound, TypeRef}
,
19 /// Converts an `ast::Path` to `Path`. Works with use trees.
20 /// It correctly handles `$crate` based path from macro call.
21 pub(super) fn lower_path(mut path
: ast
::Path
, ctx
: &LowerCtx
<'_
>) -> Option
<Path
> {
22 let mut kind
= PathKind
::Plain
;
23 let mut type_anchor
= None
;
24 let mut segments
= Vec
::new();
25 let mut generic_args
= Vec
::new();
26 let hygiene
= ctx
.hygiene();
28 let segment
= path
.segment()?
;
30 if segment
.coloncolon_token().is_some() {
34 match segment
.kind()?
{
35 ast
::PathSegmentKind
::Name(name_ref
) => {
36 // FIXME: this should just return name
37 match hygiene
.name_ref_to_name(ctx
.db
.upcast(), name_ref
) {
38 Either
::Left(name
) => {
41 .and_then(|it
| lower_generic_args(ctx
, it
))
43 lower_generic_args_from_fn_path(
50 if let Some(_
) = args
{
51 generic_args
.resize(segments
.len(), None
);
52 generic_args
.push(args
);
56 Either
::Right(crate_id
) => {
57 kind
= PathKind
::DollarCrate(crate_id
);
62 ast
::PathSegmentKind
::SelfTypeKw
=> {
63 segments
.push(name
![Self]);
65 ast
::PathSegmentKind
::Type { type_ref, trait_ref }
=> {
66 assert
!(path
.qualifier().is_none()); // this can only occur at the first segment
68 let self_type
= TypeRef
::from_ast(ctx
, type_ref?
);
73 type_anchor
= Some(Interned
::new(self_type
));
74 kind
= PathKind
::Plain
;
76 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
78 let Path { mod_path, generic_args: path_generic_args, .. }
=
79 Path
::from_src(trait_ref
.path()?
, ctx
)?
;
80 let num_segments
= mod_path
.segments().len();
83 segments
.extend(mod_path
.segments().iter().cloned().rev());
84 if let Some(path_generic_args
) = path_generic_args
{
85 generic_args
.resize(segments
.len() - num_segments
, None
);
86 generic_args
.extend(Vec
::from(path_generic_args
).into_iter().rev());
88 generic_args
.resize(segments
.len(), None
);
91 let self_type
= GenericArg
::Type(self_type
);
93 // Insert the type reference (T in the above example) as Self parameter for the trait
94 let last_segment
= generic_args
.get_mut(segments
.len() - num_segments
)?
;
95 *last_segment
= Some(Interned
::new(match last_segment
.take() {
96 Some(it
) => GenericArgs
{
97 args
: iter
::once(self_type
)
98 .chain(it
.args
.iter().cloned())
102 bindings
: it
.bindings
.clone(),
103 desugared_from_fn
: it
.desugared_from_fn
,
105 None
=> GenericArgs
{
106 args
: Box
::new([self_type
]),
108 ..GenericArgs
::empty()
114 ast
::PathSegmentKind
::CrateKw
=> {
115 kind
= PathKind
::Crate
;
118 ast
::PathSegmentKind
::SelfKw
=> {
119 // don't break out if `self` is the last segment of a path, this mean we got a
120 // use tree like `foo::{self}` which we want to resolve as `foo`
121 if !segments
.is_empty() {
122 kind
= PathKind
::Super(0);
126 ast
::PathSegmentKind
::SuperKw
=> {
127 let nested_super_count
= if let PathKind
::Super(n
) = kind { n }
else { 0 }
;
128 kind
= PathKind
::Super(nested_super_count
+ 1);
131 path
= match qualifier(&path
) {
137 if !generic_args
.is_empty() {
138 generic_args
.resize(segments
.len(), None
);
139 generic_args
.reverse();
142 if segments
.is_empty() && kind
== PathKind
::Plain
&& type_anchor
.is_none() {
143 // plain empty paths don't exist, this means we got a single `self` segment as our path
144 kind
= PathKind
::Super(0);
147 // handle local_inner_macros :
148 // Basically, even in rustc it is quite hacky:
149 // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
150 // We follow what it did anyway :)
151 if segments
.len() == 1 && kind
== PathKind
::Plain
{
152 if let Some(_macro_call
) = path
.syntax().parent().and_then(ast
::MacroCall
::cast
) {
153 if let Some(crate_id
) = hygiene
.local_inner_macros(ctx
.db
.upcast(), path
) {
154 kind
= PathKind
::DollarCrate(crate_id
);
159 let mod_path
= Interned
::new(ModPath
::from_segments(kind
, segments
));
163 generic_args
: if generic_args
.is_empty() { None }
else { Some(generic_args.into()) }
,
166 fn qualifier(path
: &ast
::Path
) -> Option
<ast
::Path
> {
167 if let Some(q
) = path
.qualifier() {
170 // FIXME: this bottom up traversal is not too precise.
171 // Should we handle do a top-down analysis, recording results?
172 let use_tree_list
= path
.syntax().ancestors().find_map(ast
::UseTreeList
::cast
)?
;
173 let use_tree
= use_tree_list
.parent_use_tree();
178 pub(super) fn lower_generic_args(
179 lower_ctx
: &LowerCtx
<'_
>,
180 node
: ast
::GenericArgList
,
181 ) -> Option
<GenericArgs
> {
182 let mut args
= Vec
::new();
183 let mut bindings
= Vec
::new();
184 for generic_arg
in node
.generic_args() {
186 ast
::GenericArg
::TypeArg(type_arg
) => {
187 let type_ref
= TypeRef
::from_ast_opt(lower_ctx
, type_arg
.ty());
188 args
.push(GenericArg
::Type(type_ref
));
190 ast
::GenericArg
::AssocTypeArg(assoc_type_arg
) => {
191 if let Some(name_ref
) = assoc_type_arg
.name_ref() {
192 let name
= name_ref
.as_name();
193 let args
= assoc_type_arg
195 .and_then(|args
| lower_generic_args(lower_ctx
, args
))
197 let type_ref
= assoc_type_arg
.ty().map(|it
| TypeRef
::from_ast(lower_ctx
, it
));
198 let bounds
= if let Some(l
) = assoc_type_arg
.type_bound_list() {
200 .map(|it
| Interned
::new(TypeBound
::from_ast(lower_ctx
, it
)))
205 bindings
.push(AssociatedTypeBinding { name, args, type_ref, bounds }
);
208 ast
::GenericArg
::LifetimeArg(lifetime_arg
) => {
209 if let Some(lifetime
) = lifetime_arg
.lifetime() {
210 let lifetime_ref
= LifetimeRef
::new(&lifetime
);
211 args
.push(GenericArg
::Lifetime(lifetime_ref
))
214 ast
::GenericArg
::ConstArg(arg
) => {
215 let arg
= ConstRefOrPath
::from_expr_opt(arg
.expr());
216 args
.push(GenericArg
::Const(arg
))
221 if args
.is_empty() && bindings
.is_empty() {
225 args
: args
.into_boxed_slice(),
226 has_self_type
: false,
227 bindings
: bindings
.into_boxed_slice(),
228 desugared_from_fn
: false,
232 /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
233 /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
234 fn lower_generic_args_from_fn_path(
236 params
: Option
<ast
::ParamList
>,
237 ret_type
: Option
<ast
::RetType
>,
238 ) -> Option
<GenericArgs
> {
239 let params
= params?
;
240 let mut param_types
= Vec
::new();
241 for param
in params
.params() {
242 let type_ref
= TypeRef
::from_ast_opt(ctx
, param
.ty());
243 param_types
.push(type_ref
);
245 let args
= Box
::new([GenericArg
::Type(TypeRef
::Tuple(param_types
))]);
246 let bindings
= if let Some(ret_type
) = ret_type
{
247 let type_ref
= TypeRef
::from_ast_opt(ctx
, ret_type
.ty());
248 Box
::new([AssociatedTypeBinding
{
251 type_ref
: Some(type_ref
),
252 bounds
: Box
::default(),
256 let type_ref
= TypeRef
::Tuple(Vec
::new());
257 Box
::new([AssociatedTypeBinding
{
260 type_ref
: Some(type_ref
),
261 bounds
: Box
::default(),
264 Some(GenericArgs { args, has_self_type: false, bindings, desugared_from_fn: true }
)