diff options
author | Dmitry <[email protected]> | 2020-08-14 19:32:05 +0100 |
---|---|---|
committer | Dmitry <[email protected]> | 2020-08-14 19:32:05 +0100 |
commit | 178c3e135a2a249692f7784712492e7884ae0c00 (patch) | |
tree | ac6b769dbf7162150caa0c1624786a4dd79ff3be /crates/hir_def/src/path/lower | |
parent | 06ff8e6c760ff05f10e868b5d1f9d79e42fbb49c (diff) | |
parent | c2594daf2974dbd4ce3d9b7ec72481764abaceb5 (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.rs | 120 |
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 | |||
4 | use std::iter; | ||
5 | |||
6 | use either::Either; | ||
7 | use hir_expand::{hygiene::Hygiene, name::AsName}; | ||
8 | use syntax::ast::{self, NameOwner}; | ||
9 | use test_utils::mark; | ||
10 | |||
11 | use crate::path::{ImportAlias, ModPath, PathKind}; | ||
12 | |||
13 | pub(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 | |||
65 | fn 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 | } | ||