aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir_def/src/path.rs110
-rw-r--r--crates/ra_hir_def/src/path/lower_use.rs115
2 files changed, 119 insertions, 106 deletions
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index e547b2f03..ec9d13e82 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -1,4 +1,5 @@
1//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`. 1//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`.
2mod lower_use;
2 3
3use std::{iter, sync::Arc}; 4use std::{iter, sync::Arc};
4 5
@@ -9,7 +10,7 @@ use hir_expand::{
9}; 10};
10use ra_db::CrateId; 11use ra_db::CrateId;
11use ra_syntax::{ 12use ra_syntax::{
12 ast::{self, NameOwner, TypeAscriptionOwner}, 13 ast::{self, TypeAscriptionOwner},
13 AstNode, 14 AstNode,
14}; 15};
15 16
@@ -28,8 +29,7 @@ pub struct PathSegment {
28} 29}
29 30
30/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This 31/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
31/// can (in the future) also include bindings of associated types, like in 32/// also includes bindings of associated types, like in `Iterator<Item = Foo>`.
32/// `Iterator<Item = Foo>`.
33#[derive(Debug, Clone, PartialEq, Eq, Hash)] 33#[derive(Debug, Clone, PartialEq, Eq, Hash)]
34pub struct GenericArgs { 34pub struct GenericArgs {
35 pub args: Vec<GenericArg>, 35 pub args: Vec<GenericArg>,
@@ -72,7 +72,7 @@ impl Path {
72 mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>), 72 mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
73 ) { 73 ) {
74 if let Some(tree) = item_src.value.use_tree() { 74 if let Some(tree) = item_src.value.use_tree() {
75 expand_use_tree(None, tree, hygiene, &mut cb); 75 lower_use::lower_use_tree(None, tree, hygiene, &mut cb);
76 } 76 }
77 } 77 }
78 78
@@ -296,108 +296,6 @@ impl From<Name> for Path {
296 } 296 }
297} 297}
298 298
299fn expand_use_tree(
300 prefix: Option<Path>,
301 tree: ast::UseTree,
302 hygiene: &Hygiene,
303 cb: &mut dyn FnMut(Path, &ast::UseTree, bool, Option<Name>),
304) {
305 if let Some(use_tree_list) = tree.use_tree_list() {
306 let prefix = match tree.path() {
307 // E.g. use something::{{{inner}}};
308 None => prefix,
309 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
310 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
311 Some(path) => match convert_path(prefix, path, hygiene) {
312 Some(it) => Some(it),
313 None => return, // FIXME: report errors somewhere
314 },
315 };
316 for child_tree in use_tree_list.use_trees() {
317 expand_use_tree(prefix.clone(), child_tree, hygiene, cb);
318 }
319 } else {
320 let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
321 if let Some(ast_path) = tree.path() {
322 // Handle self in a path.
323 // E.g. `use something::{self, <...>}`
324 if ast_path.qualifier().is_none() {
325 if let Some(segment) = ast_path.segment() {
326 if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
327 if let Some(prefix) = prefix {
328 cb(prefix, &tree, false, alias);
329 return;
330 }
331 }
332 }
333 }
334 if let Some(path) = convert_path(prefix, ast_path, hygiene) {
335 let is_glob = tree.has_star();
336 cb(path, &tree, is_glob, alias)
337 }
338 // FIXME: report errors somewhere
339 // We get here if we do
340 }
341 }
342}
343
344fn convert_path(prefix: Option<Path>, path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
345 let prefix = if let Some(qual) = path.qualifier() {
346 Some(convert_path(prefix, qual, hygiene)?)
347 } else {
348 prefix
349 };
350
351 let segment = path.segment()?;
352 let res = match segment.kind()? {
353 ast::PathSegmentKind::Name(name_ref) => {
354 match hygiene.name_ref_to_name(name_ref) {
355 Either::Left(name) => {
356 // no type args in use
357 let mut res = prefix.unwrap_or_else(|| Path {
358 kind: PathKind::Plain,
359 segments: Vec::with_capacity(1),
360 });
361 res.segments.push(PathSegment {
362 name,
363 args_and_bindings: None, // no type args in use
364 });
365 res
366 }
367 Either::Right(crate_id) => {
368 return Some(Path::from_simple_segments(
369 PathKind::DollarCrate(crate_id),
370 iter::empty(),
371 ))
372 }
373 }
374 }
375 ast::PathSegmentKind::CrateKw => {
376 if prefix.is_some() {
377 return None;
378 }
379 Path::from_simple_segments(PathKind::Crate, iter::empty())
380 }
381 ast::PathSegmentKind::SelfKw => {
382 if prefix.is_some() {
383 return None;
384 }
385 Path::from_simple_segments(PathKind::Self_, iter::empty())
386 }
387 ast::PathSegmentKind::SuperKw => {
388 if prefix.is_some() {
389 return None;
390 }
391 Path::from_simple_segments(PathKind::Super, iter::empty())
392 }
393 ast::PathSegmentKind::Type { .. } => {
394 // not allowed in imports
395 return None;
396 }
397 };
398 Some(res)
399}
400
401pub mod known { 299pub mod known {
402 use hir_expand::name; 300 use hir_expand::name;
403 301
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}