aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/path
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/path')
-rw-r--r--crates/hir_def/src/path/lower.rs215
-rw-r--r--crates/hir_def/src/path/lower/lower_use.rs120
2 files changed, 335 insertions, 0 deletions
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
new file mode 100644
index 000000000..07b9723ce
--- /dev/null
+++ b/crates/hir_def/src/path/lower.rs
@@ -0,0 +1,215 @@
1//! Transforms syntax into `Path` objects, ideally with accounting for hygiene
2
3mod lower_use;
4
5use std::sync::Arc;
6
7use either::Either;
8use hir_expand::{
9 hygiene::Hygiene,
10 name::{name, AsName},
11};
12use syntax::ast::{self, AstNode, TypeBoundsOwner};
13
14use super::AssociatedTypeBinding;
15use crate::{
16 body::LowerCtx,
17 path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
18 type_ref::{TypeBound, TypeRef},
19};
20
21pub(super) use lower_use::lower_use_tree;
22
23/// Converts an `ast::Path` to `Path`. Works with use trees.
24/// It correctly handles `$crate` based path from macro call.
25pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
26 let mut kind = PathKind::Plain;
27 let mut type_anchor = None;
28 let mut segments = Vec::new();
29 let mut generic_args = Vec::new();
30 let ctx = LowerCtx::with_hygiene(hygiene);
31 loop {
32 let segment = path.segment()?;
33
34 if segment.coloncolon_token().is_some() {
35 kind = PathKind::Abs;
36 }
37
38 match segment.kind()? {
39 ast::PathSegmentKind::Name(name_ref) => {
40 // FIXME: this should just return name
41 match hygiene.name_ref_to_name(name_ref) {
42 Either::Left(name) => {
43 let args = segment
44 .generic_arg_list()
45 .and_then(|it| lower_generic_args(&ctx, it))
46 .or_else(|| {
47 lower_generic_args_from_fn_path(
48 &ctx,
49 segment.param_list(),
50 segment.ret_type(),
51 )
52 })
53 .map(Arc::new);
54 segments.push(name);
55 generic_args.push(args)
56 }
57 Either::Right(crate_id) => {
58 kind = PathKind::DollarCrate(crate_id);
59 break;
60 }
61 }
62 }
63 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
64 assert!(path.qualifier().is_none()); // this can only occur at the first segment
65
66 let self_type = TypeRef::from_ast(&ctx, type_ref?);
67
68 match trait_ref {
69 // <T>::foo
70 None => {
71 type_anchor = Some(Box::new(self_type));
72 kind = PathKind::Plain;
73 }
74 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
75 Some(trait_ref) => {
76 let path = Path::from_src(trait_ref.path()?, hygiene)?;
77 kind = path.mod_path.kind;
78
79 let mut prefix_segments = path.mod_path.segments;
80 prefix_segments.reverse();
81 segments.extend(prefix_segments);
82
83 let mut prefix_args = path.generic_args;
84 prefix_args.reverse();
85 generic_args.extend(prefix_args);
86
87 // Insert the type reference (T in the above example) as Self parameter for the trait
88 let last_segment = generic_args.last_mut()?;
89 if last_segment.is_none() {
90 *last_segment = Some(Arc::new(GenericArgs::empty()));
91 };
92 let args = last_segment.as_mut().unwrap();
93 let mut args_inner = Arc::make_mut(args);
94 args_inner.has_self_type = true;
95 args_inner.args.insert(0, GenericArg::Type(self_type));
96 }
97 }
98 }
99 ast::PathSegmentKind::CrateKw => {
100 kind = PathKind::Crate;
101 break;
102 }
103 ast::PathSegmentKind::SelfKw => {
104 kind = PathKind::Super(0);
105 break;
106 }
107 ast::PathSegmentKind::SuperKw => {
108 let nested_super_count = if let PathKind::Super(n) = kind { n } else { 0 };
109 kind = PathKind::Super(nested_super_count + 1);
110 }
111 }
112 path = match qualifier(&path) {
113 Some(it) => it,
114 None => break,
115 };
116 }
117 segments.reverse();
118 generic_args.reverse();
119
120 // handle local_inner_macros :
121 // Basically, even in rustc it is quite hacky:
122 // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
123 // We follow what it did anyway :)
124 if segments.len() == 1 && kind == PathKind::Plain {
125 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
126 if macro_call.is_bang() {
127 if let Some(crate_id) = hygiene.local_inner_macros() {
128 kind = PathKind::DollarCrate(crate_id);
129 }
130 }
131 }
132 }
133
134 let mod_path = ModPath { kind, segments };
135 return Some(Path { type_anchor, mod_path, generic_args });
136
137 fn qualifier(path: &ast::Path) -> Option<ast::Path> {
138 if let Some(q) = path.qualifier() {
139 return Some(q);
140 }
141 // FIXME: this bottom up traversal is not too precise.
142 // Should we handle do a top-down analysis, recording results?
143 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
144 let use_tree = use_tree_list.parent_use_tree();
145 use_tree.path()
146 }
147}
148
149pub(super) fn lower_generic_args(
150 lower_ctx: &LowerCtx,
151 node: ast::GenericArgList,
152) -> Option<GenericArgs> {
153 let mut args = Vec::new();
154 let mut bindings = Vec::new();
155 for generic_arg in node.generic_args() {
156 match generic_arg {
157 ast::GenericArg::TypeArg(type_arg) => {
158 let type_ref = TypeRef::from_ast_opt(lower_ctx, type_arg.ty());
159 args.push(GenericArg::Type(type_ref));
160 }
161 ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
162 if let Some(name_ref) = assoc_type_arg.name_ref() {
163 let name = name_ref.as_name();
164 let type_ref = assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it));
165 let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
166 l.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
167 } else {
168 Vec::new()
169 };
170 bindings.push(AssociatedTypeBinding { name, type_ref, bounds });
171 }
172 }
173 // Lifetimes and constants are ignored for now.
174 ast::GenericArg::LifetimeArg(_) | ast::GenericArg::ConstArg(_) => (),
175 }
176 }
177
178 if args.is_empty() && bindings.is_empty() {
179 return None;
180 }
181 Some(GenericArgs { args, has_self_type: false, bindings })
182}
183
184/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
185/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
186fn lower_generic_args_from_fn_path(
187 ctx: &LowerCtx,
188 params: Option<ast::ParamList>,
189 ret_type: Option<ast::RetType>,
190) -> Option<GenericArgs> {
191 let mut args = Vec::new();
192 let mut bindings = Vec::new();
193 if let Some(params) = params {
194 let mut param_types = Vec::new();
195 for param in params.params() {
196 let type_ref = TypeRef::from_ast_opt(&ctx, param.ty());
197 param_types.push(type_ref);
198 }
199 let arg = GenericArg::Type(TypeRef::Tuple(param_types));
200 args.push(arg);
201 }
202 if let Some(ret_type) = ret_type {
203 let type_ref = TypeRef::from_ast_opt(&ctx, ret_type.ty());
204 bindings.push(AssociatedTypeBinding {
205 name: name![Output],
206 type_ref: Some(type_ref),
207 bounds: Vec::new(),
208 });
209 }
210 if args.is_empty() && bindings.is_empty() {
211 None
212 } else {
213 Some(GenericArgs { args, has_self_type: false, bindings })
214 }
215}
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs
new file mode 100644
index 000000000..53cecb05f
--- /dev/null
+++ b/crates/hir_def/src/path/lower/lower_use.rs
@@ -0,0 +1,120 @@
1//! Lowers a single complex use like `use foo::{bar, baz};` into a list of paths like
2//! `foo::bar`, `foo::baz`;
3
4use std::iter;
5
6use either::Either;
7use hir_expand::{hygiene::Hygiene, name::AsName};
8use syntax::ast::{self, NameOwner};
9use test_utils::mark;
10
11use crate::path::{ImportAlias, ModPath, PathKind};
12
13pub(crate) fn lower_use_tree(
14 prefix: Option<ModPath>,
15 tree: ast::UseTree,
16 hygiene: &Hygiene,
17 cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option<ImportAlias>),
18) {
19 if let Some(use_tree_list) = tree.use_tree_list() {
20 let prefix = match tree.path() {
21 // E.g. use something::{{{inner}}};
22 None => prefix,
23 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
24 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
25 Some(path) => match convert_path(prefix, path, hygiene) {
26 Some(it) => Some(it),
27 None => return, // FIXME: report errors somewhere
28 },
29 };
30 for child_tree in use_tree_list.use_trees() {
31 lower_use_tree(prefix.clone(), child_tree, hygiene, cb);
32 }
33 } else {
34 let alias = tree.rename().map(|a| {
35 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
36 });
37 let is_glob = tree.star_token().is_some();
38 if let Some(ast_path) = tree.path() {
39 // Handle self in a path.
40 // E.g. `use something::{self, <...>}`
41 if ast_path.qualifier().is_none() {
42 if let Some(segment) = ast_path.segment() {
43 if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
44 if let Some(prefix) = prefix {
45 cb(prefix, &tree, false, alias);
46 return;
47 }
48 }
49 }
50 }
51 if let Some(path) = convert_path(prefix, ast_path, hygiene) {
52 cb(path, &tree, is_glob, alias)
53 }
54 // FIXME: report errors somewhere
55 // We get here if we do
56 } else if is_glob {
57 mark::hit!(glob_enum_group);
58 if let Some(prefix) = prefix {
59 cb(prefix, &tree, is_glob, None)
60 }
61 }
62 }
63}
64
65fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
66 let prefix = if let Some(qual) = path.qualifier() {
67 Some(convert_path(prefix, qual, hygiene)?)
68 } else {
69 prefix
70 };
71
72 let segment = path.segment()?;
73 let res = match segment.kind()? {
74 ast::PathSegmentKind::Name(name_ref) => {
75 match hygiene.name_ref_to_name(name_ref) {
76 Either::Left(name) => {
77 // no type args in use
78 let mut res = prefix.unwrap_or_else(|| ModPath {
79 kind: PathKind::Plain,
80 segments: Vec::with_capacity(1),
81 });
82 res.segments.push(name);
83 res
84 }
85 Either::Right(crate_id) => {
86 return Some(ModPath::from_segments(
87 PathKind::DollarCrate(crate_id),
88 iter::empty(),
89 ))
90 }
91 }
92 }
93 ast::PathSegmentKind::CrateKw => {
94 if prefix.is_some() {
95 return None;
96 }
97 ModPath::from_segments(PathKind::Crate, iter::empty())
98 }
99 ast::PathSegmentKind::SelfKw => {
100 if prefix.is_some() {
101 return None;
102 }
103 ModPath::from_segments(PathKind::Super(0), iter::empty())
104 }
105 ast::PathSegmentKind::SuperKw => {
106 let nested_super_count = match prefix.map(|p| p.kind) {
107 Some(PathKind::Super(n)) => n,
108 Some(_) => return None,
109 None => 0,
110 };
111
112 ModPath::from_segments(PathKind::Super(nested_super_count + 1), iter::empty())
113 }
114 ast::PathSegmentKind::Type { .. } => {
115 // not allowed in imports
116 return None;
117 }
118 };
119 Some(res)
120}