aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/path/lower
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/path/lower')
-rw-r--r--crates/ra_hir_def/src/path/lower/lower_use.rs112
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
4use std::iter;
5
6use either::Either;
7use hir_expand::{
8 hygiene::Hygiene,
9 name::{AsName, Name},
10};
11use ra_syntax::ast::{self, NameOwner};
12
13use crate::path::{ModPath, PathKind};
14
15pub(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
60fn 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}