aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/path
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/path')
-rw-r--r--crates/ra_hir_def/src/path/lower_use.rs115
1 files changed, 115 insertions, 0 deletions
diff --git a/crates/ra_hir_def/src/path/lower_use.rs b/crates/ra_hir_def/src/path/lower_use.rs
new file mode 100644
index 000000000..e2e1f716d
--- /dev/null
+++ b/crates/ra_hir_def/src/path/lower_use.rs
@@ -0,0 +1,115 @@
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::{Path, PathKind, PathSegment};
14
15pub(crate) fn lower_use_tree(
16 prefix: Option<Path>,
17 tree: ast::UseTree,
18 hygiene: &Hygiene,
19 cb: &mut dyn FnMut(Path, &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<Path>, path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
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(|| Path {
74 kind: PathKind::Plain,
75 segments: Vec::with_capacity(1),
76 });
77 res.segments.push(PathSegment {
78 name,
79 args_and_bindings: None, // no type args in use
80 });
81 res
82 }
83 Either::Right(crate_id) => {
84 return Some(Path::from_simple_segments(
85 PathKind::DollarCrate(crate_id),
86 iter::empty(),
87 ))
88 }
89 }
90 }
91 ast::PathSegmentKind::CrateKw => {
92 if prefix.is_some() {
93 return None;
94 }
95 Path::from_simple_segments(PathKind::Crate, iter::empty())
96 }
97 ast::PathSegmentKind::SelfKw => {
98 if prefix.is_some() {
99 return None;
100 }
101 Path::from_simple_segments(PathKind::Self_, iter::empty())
102 }
103 ast::PathSegmentKind::SuperKw => {
104 if prefix.is_some() {
105 return None;
106 }
107 Path::from_simple_segments(PathKind::Super, iter::empty())
108 }
109 ast::PathSegmentKind::Type { .. } => {
110 // not allowed in imports
111 return None;
112 }
113 };
114 Some(res)
115}