aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/path
diff options
context:
space:
mode:
authorSeivan Heidari <[email protected]>2019-12-23 14:35:31 +0000
committerSeivan Heidari <[email protected]>2019-12-23 14:35:31 +0000
commitb21d9337d9200e2cfdc90b386591c72c302dc03e (patch)
treef81f5c08f821115cee26fa4d3ceaae88c7807fd5 /crates/ra_hir_def/src/path
parent18a0937585b836ec5ed054b9ae48e0156ab6d9ef (diff)
parentce07a2daa9e53aa86a769f8641b14c2878444fbc (diff)
Merge branch 'master' into feature/themes
Diffstat (limited to 'crates/ra_hir_def/src/path')
-rw-r--r--crates/ra_hir_def/src/path/lower.rs178
-rw-r--r--crates/ra_hir_def/src/path/lower/lower_use.rs118
2 files changed, 296 insertions, 0 deletions
diff --git a/crates/ra_hir_def/src/path/lower.rs b/crates/ra_hir_def/src/path/lower.rs
new file mode 100644
index 000000000..62aafd508
--- /dev/null
+++ b/crates/ra_hir_def/src/path/lower.rs
@@ -0,0 +1,178 @@
1//! Transforms syntax into `Path` objects, ideally with accounting for hygiene
2
3mod lower_use;
4
5use std::sync::Arc;
6
7use either::Either;
8use hir_expand::{
9 hygiene::Hygiene,
10 name::{name, AsName},
11};
12use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner};
13
14use crate::{
15 path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
16 type_ref::TypeRef,
17};
18
19pub(super) use lower_use::lower_use_tree;
20
21/// Converts an `ast::Path` to `Path`. Works with use trees.
22/// It correctly handles `$crate` based path from macro call.
23pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
24 let mut kind = PathKind::Plain;
25 let mut type_anchor = None;
26 let mut segments = Vec::new();
27 let mut generic_args = Vec::new();
28 loop {
29 let segment = path.segment()?;
30
31 if segment.has_colon_colon() {
32 kind = PathKind::Abs;
33 }
34
35 match segment.kind()? {
36 ast::PathSegmentKind::Name(name_ref) => {
37 // FIXME: this should just return name
38 match hygiene.name_ref_to_name(name_ref) {
39 Either::Left(name) => {
40 let args = segment
41 .type_arg_list()
42 .and_then(lower_generic_args)
43 .or_else(|| {
44 lower_generic_args_from_fn_path(
45 segment.param_list(),
46 segment.ret_type(),
47 )
48 })
49 .map(Arc::new);
50 segments.push(name);
51 generic_args.push(args)
52 }
53 Either::Right(crate_id) => {
54 kind = PathKind::DollarCrate(crate_id);
55 break;
56 }
57 }
58 }
59 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
60 assert!(path.qualifier().is_none()); // this can only occur at the first segment
61
62 let self_type = TypeRef::from_ast(type_ref?);
63
64 match trait_ref {
65 // <T>::foo
66 None => {
67 type_anchor = Some(Box::new(self_type));
68 kind = PathKind::Plain;
69 }
70 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
71 Some(trait_ref) => {
72 let path = Path::from_src(trait_ref.path()?, hygiene)?;
73 kind = path.mod_path.kind;
74
75 let mut prefix_segments = path.mod_path.segments;
76 prefix_segments.reverse();
77 segments.extend(prefix_segments);
78
79 let mut prefix_args = path.generic_args;
80 prefix_args.reverse();
81 generic_args.extend(prefix_args);
82
83 // Insert the type reference (T in the above example) as Self parameter for the trait
84 let last_segment = generic_args.last_mut()?;
85 if last_segment.is_none() {
86 *last_segment = Some(Arc::new(GenericArgs::empty()));
87 };
88 let args = last_segment.as_mut().unwrap();
89 let mut args_inner = Arc::make_mut(args);
90 args_inner.has_self_type = true;
91 args_inner.args.insert(0, GenericArg::Type(self_type));
92 }
93 }
94 }
95 ast::PathSegmentKind::CrateKw => {
96 kind = PathKind::Crate;
97 break;
98 }
99 ast::PathSegmentKind::SelfKw => {
100 kind = PathKind::Super(0);
101 break;
102 }
103 ast::PathSegmentKind::SuperKw => {
104 kind = PathKind::Super(1);
105 break;
106 }
107 }
108 path = match qualifier(&path) {
109 Some(it) => it,
110 None => break,
111 };
112 }
113 segments.reverse();
114 generic_args.reverse();
115 let mod_path = ModPath { kind, segments };
116 return Some(Path { type_anchor, mod_path, generic_args });
117
118 fn qualifier(path: &ast::Path) -> Option<ast::Path> {
119 if let Some(q) = path.qualifier() {
120 return Some(q);
121 }
122 // FIXME: this bottom up traversal is not too precise.
123 // Should we handle do a top-down analysis, recording results?
124 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
125 let use_tree = use_tree_list.parent_use_tree();
126 use_tree.path()
127 }
128}
129
130pub(super) fn lower_generic_args(node: ast::TypeArgList) -> Option<GenericArgs> {
131 let mut args = Vec::new();
132 for type_arg in node.type_args() {
133 let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
134 args.push(GenericArg::Type(type_ref));
135 }
136 // lifetimes ignored for now
137 let mut bindings = Vec::new();
138 for assoc_type_arg in node.assoc_type_args() {
139 if let Some(name_ref) = assoc_type_arg.name_ref() {
140 let name = name_ref.as_name();
141 let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref());
142 bindings.push((name, type_ref));
143 }
144 }
145 if args.is_empty() && bindings.is_empty() {
146 None
147 } else {
148 Some(GenericArgs { args, has_self_type: false, bindings })
149 }
150}
151
152/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
153/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
154fn lower_generic_args_from_fn_path(
155 params: Option<ast::ParamList>,
156 ret_type: Option<ast::RetType>,
157) -> Option<GenericArgs> {
158 let mut args = Vec::new();
159 let mut bindings = Vec::new();
160 if let Some(params) = params {
161 let mut param_types = Vec::new();
162 for param in params.params() {
163 let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
164 param_types.push(type_ref);
165 }
166 let arg = GenericArg::Type(TypeRef::Tuple(param_types));
167 args.push(arg);
168 }
169 if let Some(ret_type) = ret_type {
170 let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
171 bindings.push((name![Output], type_ref))
172 }
173 if args.is_empty() && bindings.is_empty() {
174 None
175 } else {
176 Some(GenericArgs { args, has_self_type: false, bindings })
177 }
178}
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..3218eaf0a
--- /dev/null
+++ b/crates/ra_hir_def/src/path/lower/lower_use.rs
@@ -0,0 +1,118 @@
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};
12use test_utils::tested_by;
13
14use crate::path::{ModPath, PathKind};
15
16pub(crate) fn lower_use_tree(
17 prefix: Option<ModPath>,
18 tree: ast::UseTree,
19 hygiene: &Hygiene,
20 cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option<Name>),
21) {
22 if let Some(use_tree_list) = tree.use_tree_list() {
23 let prefix = match tree.path() {
24 // E.g. use something::{{{inner}}};
25 None => prefix,
26 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
27 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
28 Some(path) => match convert_path(prefix, path, hygiene) {
29 Some(it) => Some(it),
30 None => return, // FIXME: report errors somewhere
31 },
32 };
33 for child_tree in use_tree_list.use_trees() {
34 lower_use_tree(prefix.clone(), child_tree, hygiene, cb);
35 }
36 } else {
37 let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
38 let is_glob = tree.has_star();
39 if let Some(ast_path) = tree.path() {
40 // Handle self in a path.
41 // E.g. `use something::{self, <...>}`
42 if ast_path.qualifier().is_none() {
43 if let Some(segment) = ast_path.segment() {
44 if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
45 if let Some(prefix) = prefix {
46 cb(prefix, &tree, false, alias);
47 return;
48 }
49 }
50 }
51 }
52 if let Some(path) = convert_path(prefix, ast_path, hygiene) {
53 cb(path, &tree, is_glob, alias)
54 }
55 // FIXME: report errors somewhere
56 // We get here if we do
57 } else if is_glob {
58 tested_by!(glob_enum_group);
59 if let Some(prefix) = prefix {
60 cb(prefix, &tree, is_glob, None)
61 }
62 }
63 }
64}
65
66fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
67 let prefix = if let Some(qual) = path.qualifier() {
68 Some(convert_path(prefix, qual, hygiene)?)
69 } else {
70 prefix
71 };
72
73 let segment = path.segment()?;
74 let res = match segment.kind()? {
75 ast::PathSegmentKind::Name(name_ref) => {
76 match hygiene.name_ref_to_name(name_ref) {
77 Either::Left(name) => {
78 // no type args in use
79 let mut res = prefix.unwrap_or_else(|| ModPath {
80 kind: PathKind::Plain,
81 segments: Vec::with_capacity(1),
82 });
83 res.segments.push(name);
84 res
85 }
86 Either::Right(crate_id) => {
87 return Some(ModPath::from_simple_segments(
88 PathKind::DollarCrate(crate_id),
89 iter::empty(),
90 ))
91 }
92 }
93 }
94 ast::PathSegmentKind::CrateKw => {
95 if prefix.is_some() {
96 return None;
97 }
98 ModPath::from_simple_segments(PathKind::Crate, iter::empty())
99 }
100 ast::PathSegmentKind::SelfKw => {
101 if prefix.is_some() {
102 return None;
103 }
104 ModPath::from_simple_segments(PathKind::Super(0), iter::empty())
105 }
106 ast::PathSegmentKind::SuperKw => {
107 if prefix.is_some() {
108 return None;
109 }
110 ModPath::from_simple_segments(PathKind::Super(1), iter::empty())
111 }
112 ast::PathSegmentKind::Type { .. } => {
113 // not allowed in imports
114 return None;
115 }
116 };
117 Some(res)
118}