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