aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/utils.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/utils.rs')
-rw-r--r--crates/ra_assists/src/utils.rs278
1 files changed, 0 insertions, 278 deletions
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
deleted file mode 100644
index bb16ebd4e..000000000
--- a/crates/ra_assists/src/utils.rs
+++ /dev/null
@@ -1,278 +0,0 @@
1//! Assorted functions shared by several assists.
2pub(crate) mod insert_use;
3
4use std::{iter, ops};
5
6use hir::{Adt, Crate, Enum, ScopeDef, Semantics, Trait, Type};
7use ra_ide_db::RootDatabase;
8use ra_syntax::{
9 ast::{self, make, NameOwner},
10 AstNode,
11 SyntaxKind::*,
12 SyntaxNode, TextSize, T,
13};
14use rustc_hash::FxHashSet;
15
16use crate::assist_config::SnippetCap;
17
18pub(crate) use insert_use::{find_insert_use_container, insert_use_statement};
19
20#[derive(Clone, Copy, Debug)]
21pub(crate) enum Cursor<'a> {
22 Replace(&'a SyntaxNode),
23 Before(&'a SyntaxNode),
24}
25
26impl<'a> Cursor<'a> {
27 fn node(self) -> &'a SyntaxNode {
28 match self {
29 Cursor::Replace(node) | Cursor::Before(node) => node,
30 }
31 }
32}
33
34pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor) -> String {
35 assert!(cursor.node().ancestors().any(|it| it == *node));
36 let range = cursor.node().text_range() - node.text_range().start();
37 let range: ops::Range<usize> = range.into();
38
39 let mut placeholder = cursor.node().to_string();
40 escape(&mut placeholder);
41 let tab_stop = match cursor {
42 Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder),
43 Cursor::Before(placeholder) => format!("$0{}", placeholder),
44 };
45
46 let mut buf = node.to_string();
47 buf.replace_range(range, &tab_stop);
48 return buf;
49
50 fn escape(buf: &mut String) {
51 stdx::replace(buf, '{', r"\{");
52 stdx::replace(buf, '}', r"\}");
53 stdx::replace(buf, '$', r"\$");
54 }
55}
56
57pub fn get_missing_assoc_items(
58 sema: &Semantics<RootDatabase>,
59 impl_def: &ast::Impl,
60) -> Vec<hir::AssocItem> {
61 // Names must be unique between constants and functions. However, type aliases
62 // may share the same name as a function or constant.
63 let mut impl_fns_consts = FxHashSet::default();
64 let mut impl_type = FxHashSet::default();
65
66 if let Some(item_list) = impl_def.assoc_item_list() {
67 for item in item_list.assoc_items() {
68 match item {
69 ast::AssocItem::Fn(f) => {
70 if let Some(n) = f.name() {
71 impl_fns_consts.insert(n.syntax().to_string());
72 }
73 }
74
75 ast::AssocItem::TypeAlias(t) => {
76 if let Some(n) = t.name() {
77 impl_type.insert(n.syntax().to_string());
78 }
79 }
80
81 ast::AssocItem::Const(c) => {
82 if let Some(n) = c.name() {
83 impl_fns_consts.insert(n.syntax().to_string());
84 }
85 }
86 ast::AssocItem::MacroCall(_) => (),
87 }
88 }
89 }
90
91 resolve_target_trait(sema, impl_def).map_or(vec![], |target_trait| {
92 target_trait
93 .items(sema.db)
94 .iter()
95 .filter(|i| match i {
96 hir::AssocItem::Function(f) => {
97 !impl_fns_consts.contains(&f.name(sema.db).to_string())
98 }
99 hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()),
100 hir::AssocItem::Const(c) => c
101 .name(sema.db)
102 .map(|n| !impl_fns_consts.contains(&n.to_string()))
103 .unwrap_or_default(),
104 })
105 .cloned()
106 .collect()
107 })
108}
109
110pub(crate) fn resolve_target_trait(
111 sema: &Semantics<RootDatabase>,
112 impl_def: &ast::Impl,
113) -> Option<hir::Trait> {
114 let ast_path = impl_def
115 .target_trait()
116 .map(|it| it.syntax().clone())
117 .and_then(ast::PathType::cast)?
118 .path()?;
119
120 match sema.resolve_path(&ast_path) {
121 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def),
122 _ => None,
123 }
124}
125
126pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
127 node.children_with_tokens()
128 .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
129 .map(|it| it.text_range().start())
130 .unwrap_or_else(|| node.text_range().start())
131}
132
133pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
134 if let Some(expr) = invert_special_case(&expr) {
135 return expr;
136 }
137 make::expr_prefix(T![!], expr)
138}
139
140fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
141 match expr {
142 ast::Expr::BinExpr(bin) => match bin.op_kind()? {
143 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
144 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()),
145 _ => None,
146 },
147 ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(),
148 // FIXME:
149 // ast::Expr::Literal(true | false )
150 _ => None,
151 }
152}
153
154#[derive(Clone, Copy)]
155pub enum TryEnum {
156 Result,
157 Option,
158}
159
160impl TryEnum {
161 const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result];
162
163 pub fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> {
164 let enum_ = match ty.as_adt() {
165 Some(Adt::Enum(it)) => it,
166 _ => return None,
167 };
168 TryEnum::ALL.iter().find_map(|&var| {
169 if &enum_.name(sema.db).to_string() == var.type_name() {
170 return Some(var);
171 }
172 None
173 })
174 }
175
176 pub(crate) fn happy_case(self) -> &'static str {
177 match self {
178 TryEnum::Result => "Ok",
179 TryEnum::Option => "Some",
180 }
181 }
182
183 pub(crate) fn sad_pattern(self) -> ast::Pat {
184 match self {
185 TryEnum::Result => make::tuple_struct_pat(
186 make::path_unqualified(make::path_segment(make::name_ref("Err"))),
187 iter::once(make::placeholder_pat().into()),
188 )
189 .into(),
190 TryEnum::Option => make::bind_pat(make::name("None")).into(),
191 }
192 }
193
194 fn type_name(self) -> &'static str {
195 match self {
196 TryEnum::Result => "Result",
197 TryEnum::Option => "Option",
198 }
199 }
200}
201
202/// Helps with finding well-know things inside the standard library. This is
203/// somewhat similar to the known paths infra inside hir, but it different; We
204/// want to make sure that IDE specific paths don't become interesting inside
205/// the compiler itself as well.
206pub(crate) struct FamousDefs<'a, 'b>(pub(crate) &'a Semantics<'b, RootDatabase>, pub(crate) Crate);
207
208#[allow(non_snake_case)]
209impl FamousDefs<'_, '_> {
210 #[cfg(test)]
211 pub(crate) const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
212pub mod convert {
213 pub trait From<T> {
214 fn from(T) -> Self;
215 }
216}
217
218pub mod option {
219 pub enum Option<T> { None, Some(T)}
220}
221
222pub mod prelude {
223 pub use crate::{convert::From, option::Option::{self, *}};
224}
225#[prelude_import]
226pub use prelude::*;
227"#;
228
229 pub(crate) fn core_convert_From(&self) -> Option<Trait> {
230 self.find_trait("core:convert:From")
231 }
232
233 pub(crate) fn core_option_Option(&self) -> Option<Enum> {
234 self.find_enum("core:option:Option")
235 }
236
237 fn find_trait(&self, path: &str) -> Option<Trait> {
238 match self.find_def(path)? {
239 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
240 _ => None,
241 }
242 }
243
244 fn find_enum(&self, path: &str) -> Option<Enum> {
245 match self.find_def(path)? {
246 hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it),
247 _ => None,
248 }
249 }
250
251 fn find_def(&self, path: &str) -> Option<ScopeDef> {
252 let db = self.0.db;
253 let mut path = path.split(':');
254 let trait_ = path.next_back()?;
255 let std_crate = path.next()?;
256 let std_crate = self
257 .1
258 .dependencies(db)
259 .into_iter()
260 .find(|dep| &dep.name.to_string() == std_crate)?
261 .krate;
262
263 let mut module = std_crate.root_module(db)?;
264 for segment in path {
265 module = module.children(db).find_map(|child| {
266 let name = child.name(db)?;
267 if &name.to_string() == segment {
268 Some(child)
269 } else {
270 None
271 }
272 })?;
273 }
274 let def =
275 module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1;
276 Some(def)
277 }
278}