diff options
Diffstat (limited to 'crates/ra_hir_def/src/path/lower')
-rw-r--r-- | crates/ra_hir_def/src/path/lower/lower_use.rs | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/crates/ra_hir_def/src/path/lower/lower_use.rs b/crates/ra_hir_def/src/path/lower/lower_use.rs new file mode 100644 index 000000000..ea3fdb56c --- /dev/null +++ b/crates/ra_hir_def/src/path/lower/lower_use.rs | |||
@@ -0,0 +1,112 @@ | |||
1 | //! Lowers a single complex use like `use foo::{bar, baz};` into a list of paths like | ||
2 | //! `foo::bar`, `foo::baz`; | ||
3 | |||
4 | use std::iter; | ||
5 | |||
6 | use either::Either; | ||
7 | use hir_expand::{ | ||
8 | hygiene::Hygiene, | ||
9 | name::{AsName, Name}, | ||
10 | }; | ||
11 | use ra_syntax::ast::{self, NameOwner}; | ||
12 | |||
13 | use crate::path::{ModPath, PathKind}; | ||
14 | |||
15 | pub(crate) fn lower_use_tree( | ||
16 | prefix: Option<ModPath>, | ||
17 | tree: ast::UseTree, | ||
18 | hygiene: &Hygiene, | ||
19 | cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option<Name>), | ||
20 | ) { | ||
21 | if let Some(use_tree_list) = tree.use_tree_list() { | ||
22 | let prefix = match tree.path() { | ||
23 | // E.g. use something::{{{inner}}}; | ||
24 | None => prefix, | ||
25 | // E.g. `use something::{inner}` (prefix is `None`, path is `something`) | ||
26 | // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`) | ||
27 | Some(path) => match convert_path(prefix, path, hygiene) { | ||
28 | Some(it) => Some(it), | ||
29 | None => return, // FIXME: report errors somewhere | ||
30 | }, | ||
31 | }; | ||
32 | for child_tree in use_tree_list.use_trees() { | ||
33 | lower_use_tree(prefix.clone(), child_tree, hygiene, cb); | ||
34 | } | ||
35 | } else { | ||
36 | let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name()); | ||
37 | if let Some(ast_path) = tree.path() { | ||
38 | // Handle self in a path. | ||
39 | // E.g. `use something::{self, <...>}` | ||
40 | if ast_path.qualifier().is_none() { | ||
41 | if let Some(segment) = ast_path.segment() { | ||
42 | if segment.kind() == Some(ast::PathSegmentKind::SelfKw) { | ||
43 | if let Some(prefix) = prefix { | ||
44 | cb(prefix, &tree, false, alias); | ||
45 | return; | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | if let Some(path) = convert_path(prefix, ast_path, hygiene) { | ||
51 | let is_glob = tree.has_star(); | ||
52 | cb(path, &tree, is_glob, alias) | ||
53 | } | ||
54 | // FIXME: report errors somewhere | ||
55 | // We get here if we do | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
60 | fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { | ||
61 | let prefix = if let Some(qual) = path.qualifier() { | ||
62 | Some(convert_path(prefix, qual, hygiene)?) | ||
63 | } else { | ||
64 | prefix | ||
65 | }; | ||
66 | |||
67 | let segment = path.segment()?; | ||
68 | let res = match segment.kind()? { | ||
69 | ast::PathSegmentKind::Name(name_ref) => { | ||
70 | match hygiene.name_ref_to_name(name_ref) { | ||
71 | Either::Left(name) => { | ||
72 | // no type args in use | ||
73 | let mut res = prefix.unwrap_or_else(|| ModPath { | ||
74 | kind: PathKind::Plain, | ||
75 | segments: Vec::with_capacity(1), | ||
76 | }); | ||
77 | res.segments.push(name); | ||
78 | res | ||
79 | } | ||
80 | Either::Right(crate_id) => { | ||
81 | return Some(ModPath::from_simple_segments( | ||
82 | PathKind::DollarCrate(crate_id), | ||
83 | iter::empty(), | ||
84 | )) | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | ast::PathSegmentKind::CrateKw => { | ||
89 | if prefix.is_some() { | ||
90 | return None; | ||
91 | } | ||
92 | ModPath::from_simple_segments(PathKind::Crate, iter::empty()) | ||
93 | } | ||
94 | ast::PathSegmentKind::SelfKw => { | ||
95 | if prefix.is_some() { | ||
96 | return None; | ||
97 | } | ||
98 | ModPath::from_simple_segments(PathKind::Self_, iter::empty()) | ||
99 | } | ||
100 | ast::PathSegmentKind::SuperKw => { | ||
101 | if prefix.is_some() { | ||
102 | return None; | ||
103 | } | ||
104 | ModPath::from_simple_segments(PathKind::Super, iter::empty()) | ||
105 | } | ||
106 | ast::PathSegmentKind::Type { .. } => { | ||
107 | // not allowed in imports | ||
108 | return None; | ||
109 | } | ||
110 | }; | ||
111 | Some(res) | ||
112 | } | ||