aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/path/lower
diff options
context:
space:
mode:
authorDmitry <[email protected]>2020-08-14 19:32:05 +0100
committerDmitry <[email protected]>2020-08-14 19:32:05 +0100
commit178c3e135a2a249692f7784712492e7884ae0c00 (patch)
treeac6b769dbf7162150caa0c1624786a4dd79ff3be /crates/hir_def/src/path/lower
parent06ff8e6c760ff05f10e868b5d1f9d79e42fbb49c (diff)
parentc2594daf2974dbd4ce3d9b7ec72481764abaceb5 (diff)
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'crates/hir_def/src/path/lower')
-rw-r--r--crates/hir_def/src/path/lower/lower_use.rs120
1 files changed, 120 insertions, 0 deletions
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}