diff options
Diffstat (limited to 'crates/ra_assists/src/utils.rs')
-rw-r--r-- | crates/ra_assists/src/utils.rs | 110 |
1 files changed, 109 insertions, 1 deletions
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 3d6c59bda..efd988697 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -1,7 +1,9 @@ | |||
1 | //! Assorted functions shared by several assists. | 1 | //! Assorted functions shared by several assists. |
2 | pub(crate) mod insert_use; | 2 | pub(crate) mod insert_use; |
3 | 3 | ||
4 | use hir::Semantics; | 4 | use std::iter; |
5 | |||
6 | use hir::{Adt, Crate, Semantics, Trait, Type}; | ||
5 | use ra_ide_db::RootDatabase; | 7 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 8 | use ra_syntax::{ |
7 | ast::{self, make, NameOwner}, | 9 | ast::{self, make, NameOwner}, |
@@ -99,3 +101,109 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
99 | _ => None, | 101 | _ => None, |
100 | } | 102 | } |
101 | } | 103 | } |
104 | |||
105 | #[derive(Clone, Copy)] | ||
106 | pub(crate) enum TryEnum { | ||
107 | Result, | ||
108 | Option, | ||
109 | } | ||
110 | |||
111 | impl TryEnum { | ||
112 | const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result]; | ||
113 | |||
114 | pub(crate) fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> { | ||
115 | let enum_ = match ty.as_adt() { | ||
116 | Some(Adt::Enum(it)) => it, | ||
117 | _ => return None, | ||
118 | }; | ||
119 | TryEnum::ALL.iter().find_map(|&var| { | ||
120 | if &enum_.name(sema.db).to_string() == var.type_name() { | ||
121 | return Some(var); | ||
122 | } | ||
123 | None | ||
124 | }) | ||
125 | } | ||
126 | |||
127 | pub(crate) fn happy_case(self) -> &'static str { | ||
128 | match self { | ||
129 | TryEnum::Result => "Ok", | ||
130 | TryEnum::Option => "Some", | ||
131 | } | ||
132 | } | ||
133 | |||
134 | pub(crate) fn sad_pattern(self) -> ast::Pat { | ||
135 | match self { | ||
136 | TryEnum::Result => make::tuple_struct_pat( | ||
137 | make::path_unqualified(make::path_segment(make::name_ref("Err"))), | ||
138 | iter::once(make::placeholder_pat().into()), | ||
139 | ) | ||
140 | .into(), | ||
141 | TryEnum::Option => make::bind_pat(make::name("None")).into(), | ||
142 | } | ||
143 | } | ||
144 | |||
145 | fn type_name(self) -> &'static str { | ||
146 | match self { | ||
147 | TryEnum::Result => "Result", | ||
148 | TryEnum::Option => "Option", | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | /// Helps with finding well-know things inside the standard library. This is | ||
154 | /// somewhat similar to the known paths infra inside hir, but it different; We | ||
155 | /// want to make sure that IDE specific paths don't become interesting inside | ||
156 | /// the compiler itself as well. | ||
157 | pub(crate) struct FamousDefs<'a, 'b>(pub(crate) &'a Semantics<'b, RootDatabase>, pub(crate) Crate); | ||
158 | |||
159 | #[allow(non_snake_case)] | ||
160 | impl FamousDefs<'_, '_> { | ||
161 | #[cfg(test)] | ||
162 | pub(crate) const FIXTURE: &'static str = r#" | ||
163 | //- /libcore.rs crate:core | ||
164 | pub mod convert{ | ||
165 | pub trait From<T> { | ||
166 | fn from(T) -> Self; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | pub mod prelude { pub use crate::convert::From } | ||
171 | #[prelude_import] | ||
172 | pub use prelude::*; | ||
173 | "#; | ||
174 | |||
175 | pub(crate) fn core_convert_From(&self) -> Option<Trait> { | ||
176 | self.find_trait("core:convert:From") | ||
177 | } | ||
178 | |||
179 | fn find_trait(&self, path: &str) -> Option<Trait> { | ||
180 | let db = self.0.db; | ||
181 | let mut path = path.split(':'); | ||
182 | let trait_ = path.next_back()?; | ||
183 | let std_crate = path.next()?; | ||
184 | let std_crate = self | ||
185 | .1 | ||
186 | .dependencies(db) | ||
187 | .into_iter() | ||
188 | .find(|dep| &dep.name.to_string() == std_crate)? | ||
189 | .krate; | ||
190 | |||
191 | let mut module = std_crate.root_module(db)?; | ||
192 | for segment in path { | ||
193 | module = module.children(db).find_map(|child| { | ||
194 | let name = child.name(db)?; | ||
195 | if &name.to_string() == segment { | ||
196 | Some(child) | ||
197 | } else { | ||
198 | None | ||
199 | } | ||
200 | })?; | ||
201 | } | ||
202 | let def = | ||
203 | module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1; | ||
204 | match def { | ||
205 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), | ||
206 | _ => None, | ||
207 | } | ||
208 | } | ||
209 | } | ||