diff options
Diffstat (limited to 'crates')
45 files changed, 3176 insertions, 489 deletions
diff --git a/crates/assists/src/handlers/extract_function.rs b/crates/assists/src/handlers/extract_function.rs new file mode 100644 index 000000000..d876eabca --- /dev/null +++ b/crates/assists/src/handlers/extract_function.rs | |||
@@ -0,0 +1,2159 @@ | |||
1 | use either::Either; | ||
2 | use hir::{HirDisplay, Local}; | ||
3 | use ide_db::{ | ||
4 | defs::{Definition, NameRefClass}, | ||
5 | search::{FileReference, ReferenceAccess, SearchScope}, | ||
6 | }; | ||
7 | use itertools::Itertools; | ||
8 | use stdx::format_to; | ||
9 | use syntax::{ | ||
10 | algo::SyntaxRewriter, | ||
11 | ast::{ | ||
12 | self, | ||
13 | edit::{AstNodeEdit, IndentLevel}, | ||
14 | AstNode, | ||
15 | }, | ||
16 | AstToken, Direction, SyntaxElement, | ||
17 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, | ||
18 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, T, | ||
19 | }; | ||
20 | use test_utils::mark; | ||
21 | |||
22 | use crate::{ | ||
23 | assist_context::{AssistContext, Assists}, | ||
24 | AssistId, | ||
25 | }; | ||
26 | |||
27 | // Assist: extract_function | ||
28 | // | ||
29 | // Extracts selected statements into new function. | ||
30 | // | ||
31 | // ``` | ||
32 | // fn main() { | ||
33 | // let n = 1; | ||
34 | // $0let m = n + 2; | ||
35 | // let k = m + n;$0 | ||
36 | // let g = 3; | ||
37 | // } | ||
38 | // ``` | ||
39 | // -> | ||
40 | // ``` | ||
41 | // fn main() { | ||
42 | // let n = 1; | ||
43 | // fun_name(n); | ||
44 | // let g = 3; | ||
45 | // } | ||
46 | // | ||
47 | // fn $0fun_name(n: i32) { | ||
48 | // let m = n + 2; | ||
49 | // let k = m + n; | ||
50 | // } | ||
51 | // ``` | ||
52 | pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
53 | if ctx.frange.range.is_empty() { | ||
54 | return None; | ||
55 | } | ||
56 | |||
57 | let node = ctx.covering_element(); | ||
58 | if node.kind() == COMMENT { | ||
59 | mark::hit!(extract_function_in_comment_is_not_applicable); | ||
60 | return None; | ||
61 | } | ||
62 | |||
63 | let node = element_to_node(node); | ||
64 | |||
65 | let body = extraction_target(&node, ctx.frange.range)?; | ||
66 | |||
67 | let vars_used_in_body = vars_used_in_body(ctx, &body); | ||
68 | let self_param = self_param_from_usages(ctx, &body, &vars_used_in_body); | ||
69 | |||
70 | let anchor = if self_param.is_some() { Anchor::Method } else { Anchor::Freestanding }; | ||
71 | let insert_after = scope_for_fn_insertion(&body, anchor)?; | ||
72 | let module = ctx.sema.scope(&insert_after).module()?; | ||
73 | |||
74 | let vars_defined_in_body_and_outlive = vars_defined_in_body_and_outlive(ctx, &body); | ||
75 | let ret_ty = body_return_ty(ctx, &body)?; | ||
76 | |||
77 | // FIXME: we compute variables that outlive here just to check `never!` condition | ||
78 | // this requires traversing whole `body` (cheap) and finding all references (expensive) | ||
79 | // maybe we can move this check to `edit` closure somehow? | ||
80 | if stdx::never!(!vars_defined_in_body_and_outlive.is_empty() && !ret_ty.is_unit()) { | ||
81 | // We should not have variables that outlive body if we have expression block | ||
82 | return None; | ||
83 | } | ||
84 | |||
85 | let target_range = match &body { | ||
86 | FunctionBody::Expr(expr) => expr.syntax().text_range(), | ||
87 | FunctionBody::Span { .. } => ctx.frange.range, | ||
88 | }; | ||
89 | |||
90 | acc.add( | ||
91 | AssistId("extract_function", crate::AssistKind::RefactorExtract), | ||
92 | "Extract into function", | ||
93 | target_range, | ||
94 | move |builder| { | ||
95 | let params = extracted_function_params(ctx, &body, &vars_used_in_body); | ||
96 | |||
97 | let fun = Function { | ||
98 | name: "fun_name".to_string(), | ||
99 | self_param: self_param.map(|(_, pat)| pat), | ||
100 | params, | ||
101 | ret_ty, | ||
102 | body, | ||
103 | vars_defined_in_body_and_outlive, | ||
104 | }; | ||
105 | |||
106 | builder.replace(target_range, format_replacement(ctx, &fun)); | ||
107 | |||
108 | let new_indent = IndentLevel::from_node(&insert_after); | ||
109 | let old_indent = fun.body.indent_level(); | ||
110 | |||
111 | let fn_def = format_function(ctx, module, &fun, old_indent, new_indent); | ||
112 | let insert_offset = insert_after.text_range().end(); | ||
113 | builder.insert(insert_offset, fn_def); | ||
114 | }, | ||
115 | ) | ||
116 | } | ||
117 | |||
118 | #[derive(Debug)] | ||
119 | struct Function { | ||
120 | name: String, | ||
121 | self_param: Option<ast::SelfParam>, | ||
122 | params: Vec<Param>, | ||
123 | ret_ty: RetType, | ||
124 | body: FunctionBody, | ||
125 | vars_defined_in_body_and_outlive: Vec<Local>, | ||
126 | } | ||
127 | |||
128 | #[derive(Debug)] | ||
129 | struct Param { | ||
130 | var: Local, | ||
131 | ty: hir::Type, | ||
132 | has_usages_afterwards: bool, | ||
133 | has_mut_inside_body: bool, | ||
134 | is_copy: bool, | ||
135 | } | ||
136 | |||
137 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
138 | enum ParamKind { | ||
139 | Value, | ||
140 | MutValue, | ||
141 | SharedRef, | ||
142 | MutRef, | ||
143 | } | ||
144 | |||
145 | impl ParamKind { | ||
146 | fn is_ref(&self) -> bool { | ||
147 | matches!(self, ParamKind::SharedRef | ParamKind::MutRef) | ||
148 | } | ||
149 | } | ||
150 | |||
151 | impl Param { | ||
152 | fn kind(&self) -> ParamKind { | ||
153 | match (self.has_usages_afterwards, self.has_mut_inside_body, self.is_copy) { | ||
154 | (true, true, _) => ParamKind::MutRef, | ||
155 | (true, false, false) => ParamKind::SharedRef, | ||
156 | (false, true, _) => ParamKind::MutValue, | ||
157 | (true, false, true) | (false, false, _) => ParamKind::Value, | ||
158 | } | ||
159 | } | ||
160 | |||
161 | fn value_prefix(&self) -> &'static str { | ||
162 | match self.kind() { | ||
163 | ParamKind::Value | ParamKind::MutValue => "", | ||
164 | ParamKind::SharedRef => "&", | ||
165 | ParamKind::MutRef => "&mut ", | ||
166 | } | ||
167 | } | ||
168 | |||
169 | fn type_prefix(&self) -> &'static str { | ||
170 | match self.kind() { | ||
171 | ParamKind::Value | ParamKind::MutValue => "", | ||
172 | ParamKind::SharedRef => "&", | ||
173 | ParamKind::MutRef => "&mut ", | ||
174 | } | ||
175 | } | ||
176 | |||
177 | fn mut_pattern(&self) -> &'static str { | ||
178 | match self.kind() { | ||
179 | ParamKind::MutValue => "mut ", | ||
180 | _ => "", | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | #[derive(Debug)] | ||
186 | enum RetType { | ||
187 | Expr(hir::Type), | ||
188 | Stmt, | ||
189 | } | ||
190 | |||
191 | impl RetType { | ||
192 | fn is_unit(&self) -> bool { | ||
193 | match self { | ||
194 | RetType::Expr(ty) => ty.is_unit(), | ||
195 | RetType::Stmt => true, | ||
196 | } | ||
197 | } | ||
198 | |||
199 | fn as_fn_ret(&self) -> Option<&hir::Type> { | ||
200 | match self { | ||
201 | RetType::Stmt => None, | ||
202 | RetType::Expr(ty) if ty.is_unit() => None, | ||
203 | RetType::Expr(ty) => Some(ty), | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | /// Semantically same as `ast::Expr`, but preserves identity when using only part of the Block | ||
209 | #[derive(Debug)] | ||
210 | enum FunctionBody { | ||
211 | Expr(ast::Expr), | ||
212 | Span { elements: Vec<SyntaxElement>, leading_indent: String }, | ||
213 | } | ||
214 | |||
215 | impl FunctionBody { | ||
216 | fn from_whole_node(node: SyntaxNode) -> Option<Self> { | ||
217 | match node.kind() { | ||
218 | PATH_EXPR => None, | ||
219 | BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()).map(Self::Expr), | ||
220 | RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()).map(Self::Expr), | ||
221 | BLOCK_EXPR => ast::BlockExpr::cast(node) | ||
222 | .filter(|it| it.is_standalone()) | ||
223 | .map(Into::into) | ||
224 | .map(Self::Expr), | ||
225 | _ => ast::Expr::cast(node).map(Self::Expr), | ||
226 | } | ||
227 | } | ||
228 | |||
229 | fn from_range(node: &SyntaxNode, range: TextRange) -> Option<FunctionBody> { | ||
230 | let mut first = node.token_at_offset(range.start()).left_biased()?; | ||
231 | let last = node.token_at_offset(range.end()).right_biased()?; | ||
232 | |||
233 | let mut leading_indent = String::new(); | ||
234 | |||
235 | let leading_trivia = first | ||
236 | .siblings_with_tokens(Direction::Prev) | ||
237 | .skip(1) | ||
238 | .take_while(|e| e.kind() == SyntaxKind::WHITESPACE && e.as_token().is_some()); | ||
239 | |||
240 | for e in leading_trivia { | ||
241 | let token = e.as_token().unwrap(); | ||
242 | let text = token.text(); | ||
243 | match text.rfind('\n') { | ||
244 | Some(pos) => { | ||
245 | leading_indent = text[pos..].to_owned(); | ||
246 | break; | ||
247 | } | ||
248 | None => first = token.clone(), | ||
249 | } | ||
250 | } | ||
251 | |||
252 | let mut elements: Vec<_> = first | ||
253 | .siblings_with_tokens(Direction::Next) | ||
254 | .take_while(|e| e.as_token() != Some(&last)) | ||
255 | .collect(); | ||
256 | |||
257 | if !(last.kind() == SyntaxKind::WHITESPACE && last.text().lines().count() <= 2) { | ||
258 | elements.push(last.into()); | ||
259 | } | ||
260 | |||
261 | Some(FunctionBody::Span { elements, leading_indent }) | ||
262 | } | ||
263 | |||
264 | fn indent_level(&self) -> IndentLevel { | ||
265 | match &self { | ||
266 | FunctionBody::Expr(expr) => IndentLevel::from_node(expr.syntax()), | ||
267 | FunctionBody::Span { elements, .. } => elements | ||
268 | .iter() | ||
269 | .filter_map(SyntaxElement::as_node) | ||
270 | .map(IndentLevel::from_node) | ||
271 | .min_by_key(|level| level.0) | ||
272 | .expect("body must contain at least one node"), | ||
273 | } | ||
274 | } | ||
275 | |||
276 | fn tail_expr(&self) -> Option<ast::Expr> { | ||
277 | match &self { | ||
278 | FunctionBody::Expr(expr) => Some(expr.clone()), | ||
279 | FunctionBody::Span { elements, .. } => { | ||
280 | elements.iter().rev().find_map(|e| e.as_node()).cloned().and_then(ast::Expr::cast) | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | |||
285 | fn descendants(&self) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
286 | match self { | ||
287 | FunctionBody::Expr(expr) => Either::Right(expr.syntax().descendants()), | ||
288 | FunctionBody::Span { elements, .. } => Either::Left( | ||
289 | elements | ||
290 | .iter() | ||
291 | .filter_map(SyntaxElement::as_node) | ||
292 | .flat_map(SyntaxNode::descendants), | ||
293 | ), | ||
294 | } | ||
295 | } | ||
296 | |||
297 | fn text_range(&self) -> TextRange { | ||
298 | match self { | ||
299 | FunctionBody::Expr(expr) => expr.syntax().text_range(), | ||
300 | FunctionBody::Span { elements, .. } => TextRange::new( | ||
301 | elements.first().unwrap().text_range().start(), | ||
302 | elements.last().unwrap().text_range().end(), | ||
303 | ), | ||
304 | } | ||
305 | } | ||
306 | |||
307 | fn contains_range(&self, range: TextRange) -> bool { | ||
308 | self.text_range().contains_range(range) | ||
309 | } | ||
310 | |||
311 | fn preceedes_range(&self, range: TextRange) -> bool { | ||
312 | self.text_range().end() <= range.start() | ||
313 | } | ||
314 | |||
315 | fn contains_node(&self, node: &SyntaxNode) -> bool { | ||
316 | self.contains_range(node.text_range()) | ||
317 | } | ||
318 | } | ||
319 | |||
320 | impl HasTokenAtOffset for FunctionBody { | ||
321 | fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { | ||
322 | match self { | ||
323 | FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset), | ||
324 | FunctionBody::Span { elements, .. } => { | ||
325 | stdx::always!(self.text_range().contains(offset)); | ||
326 | let mut iter = elements | ||
327 | .iter() | ||
328 | .filter(|element| element.text_range().contains_inclusive(offset)); | ||
329 | let element1 = iter.next().expect("offset does not fall into body"); | ||
330 | let element2 = iter.next(); | ||
331 | stdx::always!(iter.next().is_none(), "> 2 tokens at offset"); | ||
332 | let t1 = match element1 { | ||
333 | syntax::NodeOrToken::Node(node) => node.token_at_offset(offset), | ||
334 | syntax::NodeOrToken::Token(token) => TokenAtOffset::Single(token.clone()), | ||
335 | }; | ||
336 | let t2 = element2.map(|e| match e { | ||
337 | syntax::NodeOrToken::Node(node) => node.token_at_offset(offset), | ||
338 | syntax::NodeOrToken::Token(token) => TokenAtOffset::Single(token.clone()), | ||
339 | }); | ||
340 | |||
341 | match t2 { | ||
342 | Some(t2) => match (t1.clone().right_biased(), t2.clone().left_biased()) { | ||
343 | (Some(e1), Some(e2)) => TokenAtOffset::Between(e1, e2), | ||
344 | (Some(_), None) => t1, | ||
345 | (None, _) => t2, | ||
346 | }, | ||
347 | None => t1, | ||
348 | } | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | } | ||
353 | |||
354 | /// node or token's parent | ||
355 | fn element_to_node(node: SyntaxElement) -> SyntaxNode { | ||
356 | match node { | ||
357 | syntax::NodeOrToken::Node(n) => n, | ||
358 | syntax::NodeOrToken::Token(t) => t.parent(), | ||
359 | } | ||
360 | } | ||
361 | |||
362 | /// Try to guess what user wants to extract | ||
363 | /// | ||
364 | /// We have basically have two cases: | ||
365 | /// * We want whole node, like `loop {}`, `2 + 2`, `{ let n = 1; }` exprs. | ||
366 | /// Then we can use `ast::Expr` | ||
367 | /// * We want a few statements for a block. E.g. | ||
368 | /// ```rust,no_run | ||
369 | /// fn foo() -> i32 { | ||
370 | /// let m = 1; | ||
371 | /// $0 | ||
372 | /// let n = 2; | ||
373 | /// let k = 3; | ||
374 | /// k + n | ||
375 | /// $0 | ||
376 | /// } | ||
377 | /// ``` | ||
378 | /// | ||
379 | fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<FunctionBody> { | ||
380 | // we have selected exactly the expr node | ||
381 | // wrap it before anything else | ||
382 | if node.text_range() == selection_range { | ||
383 | let body = FunctionBody::from_whole_node(node.clone()); | ||
384 | if body.is_some() { | ||
385 | return body; | ||
386 | } | ||
387 | } | ||
388 | |||
389 | // we have selected a few statements in a block | ||
390 | // so covering_element returns the whole block | ||
391 | if node.kind() == BLOCK_EXPR { | ||
392 | let body = FunctionBody::from_range(&node, selection_range); | ||
393 | if body.is_some() { | ||
394 | return body; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | // we have selected single statement | ||
399 | // `from_whole_node` failed because (let) statement is not and expression | ||
400 | // so we try to expand covering_element to parent and repeat the previous | ||
401 | if let Some(parent) = node.parent() { | ||
402 | if parent.kind() == BLOCK_EXPR { | ||
403 | let body = FunctionBody::from_range(&parent, selection_range); | ||
404 | if body.is_some() { | ||
405 | return body; | ||
406 | } | ||
407 | } | ||
408 | } | ||
409 | |||
410 | // select the closest containing expr (both ifs are used) | ||
411 | std::iter::once(node.clone()).chain(node.ancestors()).find_map(FunctionBody::from_whole_node) | ||
412 | } | ||
413 | |||
414 | /// list local variables that are referenced in `body` | ||
415 | fn vars_used_in_body(ctx: &AssistContext, body: &FunctionBody) -> Vec<Local> { | ||
416 | // FIXME: currently usages inside macros are not found | ||
417 | body.descendants() | ||
418 | .filter_map(ast::NameRef::cast) | ||
419 | .filter_map(|name_ref| NameRefClass::classify(&ctx.sema, &name_ref)) | ||
420 | .map(|name_kind| name_kind.referenced(ctx.db())) | ||
421 | .filter_map(|definition| match definition { | ||
422 | Definition::Local(local) => Some(local), | ||
423 | _ => None, | ||
424 | }) | ||
425 | .unique() | ||
426 | .collect() | ||
427 | } | ||
428 | |||
429 | /// find `self` param, that was not defined inside `body` | ||
430 | /// | ||
431 | /// It should skip `self` params from impls inside `body` | ||
432 | fn self_param_from_usages( | ||
433 | ctx: &AssistContext, | ||
434 | body: &FunctionBody, | ||
435 | vars_used_in_body: &[Local], | ||
436 | ) -> Option<(Local, ast::SelfParam)> { | ||
437 | let mut iter = vars_used_in_body | ||
438 | .iter() | ||
439 | .filter(|var| var.is_self(ctx.db())) | ||
440 | .map(|var| (var, var.source(ctx.db()))) | ||
441 | .filter(|(_, src)| is_defined_before(ctx, body, src)) | ||
442 | .filter_map(|(&node, src)| match src.value { | ||
443 | Either::Right(it) => Some((node, it)), | ||
444 | Either::Left(_) => { | ||
445 | stdx::never!(false, "Local::is_self returned true, but source is IdentPat"); | ||
446 | None | ||
447 | } | ||
448 | }); | ||
449 | |||
450 | let self_param = iter.next(); | ||
451 | stdx::always!( | ||
452 | iter.next().is_none(), | ||
453 | "body references two different self params, both defined outside" | ||
454 | ); | ||
455 | |||
456 | self_param | ||
457 | } | ||
458 | |||
459 | /// find variables that should be extracted as params | ||
460 | /// | ||
461 | /// Computes additional info that affects param type and mutability | ||
462 | fn extracted_function_params( | ||
463 | ctx: &AssistContext, | ||
464 | body: &FunctionBody, | ||
465 | vars_used_in_body: &[Local], | ||
466 | ) -> Vec<Param> { | ||
467 | vars_used_in_body | ||
468 | .iter() | ||
469 | .filter(|var| !var.is_self(ctx.db())) | ||
470 | .map(|node| (node, node.source(ctx.db()))) | ||
471 | .filter(|(_, src)| is_defined_before(ctx, body, src)) | ||
472 | .filter_map(|(&node, src)| { | ||
473 | if src.value.is_left() { | ||
474 | Some(node) | ||
475 | } else { | ||
476 | stdx::never!(false, "Local::is_self returned false, but source is SelfParam"); | ||
477 | None | ||
478 | } | ||
479 | }) | ||
480 | .map(|var| { | ||
481 | let usages = LocalUsages::find(ctx, var); | ||
482 | let ty = var.ty(ctx.db()); | ||
483 | let is_copy = ty.is_copy(ctx.db()); | ||
484 | Param { | ||
485 | var, | ||
486 | ty, | ||
487 | has_usages_afterwards: has_usages_after_body(&usages, body), | ||
488 | has_mut_inside_body: has_exclusive_usages(ctx, &usages, body), | ||
489 | is_copy, | ||
490 | } | ||
491 | }) | ||
492 | .collect() | ||
493 | } | ||
494 | |||
495 | fn has_usages_after_body(usages: &LocalUsages, body: &FunctionBody) -> bool { | ||
496 | usages.iter().any(|reference| body.preceedes_range(reference.range)) | ||
497 | } | ||
498 | |||
499 | /// checks if relevant var is used with `&mut` access inside body | ||
500 | fn has_exclusive_usages(ctx: &AssistContext, usages: &LocalUsages, body: &FunctionBody) -> bool { | ||
501 | usages | ||
502 | .iter() | ||
503 | .filter(|reference| body.contains_range(reference.range)) | ||
504 | .any(|reference| reference_is_exclusive(reference, body, ctx)) | ||
505 | } | ||
506 | |||
507 | /// checks if this reference requires `&mut` access inside body | ||
508 | fn reference_is_exclusive( | ||
509 | reference: &FileReference, | ||
510 | body: &FunctionBody, | ||
511 | ctx: &AssistContext, | ||
512 | ) -> bool { | ||
513 | // we directly modify variable with set: `n = 0`, `n += 1` | ||
514 | if reference.access == Some(ReferenceAccess::Write) { | ||
515 | return true; | ||
516 | } | ||
517 | |||
518 | // we take `&mut` reference to variable: `&mut v` | ||
519 | let path = match path_element_of_reference(body, reference) { | ||
520 | Some(path) => path, | ||
521 | None => return false, | ||
522 | }; | ||
523 | |||
524 | expr_require_exclusive_access(ctx, &path).unwrap_or(false) | ||
525 | } | ||
526 | |||
527 | /// checks if this expr requires `&mut` access, recurses on field access | ||
528 | fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Option<bool> { | ||
529 | let parent = expr.syntax().parent()?; | ||
530 | |||
531 | if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) { | ||
532 | if bin_expr.op_kind()?.is_assignment() { | ||
533 | return Some(bin_expr.lhs()?.syntax() == expr.syntax()); | ||
534 | } | ||
535 | return Some(false); | ||
536 | } | ||
537 | |||
538 | if let Some(ref_expr) = ast::RefExpr::cast(parent.clone()) { | ||
539 | return Some(ref_expr.mut_token().is_some()); | ||
540 | } | ||
541 | |||
542 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | ||
543 | let func = ctx.sema.resolve_method_call(&method_call)?; | ||
544 | let self_param = func.self_param(ctx.db())?; | ||
545 | let access = self_param.access(ctx.db()); | ||
546 | |||
547 | return Some(matches!(access, hir::Access::Exclusive)); | ||
548 | } | ||
549 | |||
550 | if let Some(field) = ast::FieldExpr::cast(parent) { | ||
551 | return expr_require_exclusive_access(ctx, &field.into()); | ||
552 | } | ||
553 | |||
554 | Some(false) | ||
555 | } | ||
556 | |||
557 | /// Container of local varaible usages | ||
558 | /// | ||
559 | /// Semanticall same as `UsageSearchResult`, but provides more convenient interface | ||
560 | struct LocalUsages(ide_db::search::UsageSearchResult); | ||
561 | |||
562 | impl LocalUsages { | ||
563 | fn find(ctx: &AssistContext, var: Local) -> Self { | ||
564 | Self( | ||
565 | Definition::Local(var) | ||
566 | .usages(&ctx.sema) | ||
567 | .in_scope(SearchScope::single_file(ctx.frange.file_id)) | ||
568 | .all(), | ||
569 | ) | ||
570 | } | ||
571 | |||
572 | fn iter(&self) -> impl Iterator<Item = &FileReference> + '_ { | ||
573 | self.0.iter().flat_map(|(_, rs)| rs.iter()) | ||
574 | } | ||
575 | } | ||
576 | |||
577 | trait HasTokenAtOffset { | ||
578 | fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken>; | ||
579 | } | ||
580 | |||
581 | impl HasTokenAtOffset for SyntaxNode { | ||
582 | fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { | ||
583 | SyntaxNode::token_at_offset(&self, offset) | ||
584 | } | ||
585 | } | ||
586 | |||
587 | /// find relevant `ast::PathExpr` for reference | ||
588 | /// | ||
589 | /// # Preconditions | ||
590 | /// | ||
591 | /// `node` must cover `reference`, that is `node.text_range().contains_range(reference.range)` | ||
592 | fn path_element_of_reference( | ||
593 | node: &dyn HasTokenAtOffset, | ||
594 | reference: &FileReference, | ||
595 | ) -> Option<ast::Expr> { | ||
596 | let token = node.token_at_offset(reference.range.start()).right_biased().or_else(|| { | ||
597 | stdx::never!(false, "cannot find token at variable usage: {:?}", reference); | ||
598 | None | ||
599 | })?; | ||
600 | let path = token.ancestors().find_map(ast::Expr::cast).or_else(|| { | ||
601 | stdx::never!(false, "cannot find path parent of variable usage: {:?}", token); | ||
602 | None | ||
603 | })?; | ||
604 | stdx::always!(matches!(path, ast::Expr::PathExpr(_))); | ||
605 | Some(path) | ||
606 | } | ||
607 | |||
608 | /// list local variables defined inside `body` | ||
609 | fn vars_defined_in_body(body: &FunctionBody, ctx: &AssistContext) -> Vec<Local> { | ||
610 | // FIXME: this doesn't work well with macros | ||
611 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7535#discussion_r570048550 | ||
612 | body.descendants() | ||
613 | .filter_map(ast::IdentPat::cast) | ||
614 | .filter_map(|let_stmt| ctx.sema.to_def(&let_stmt)) | ||
615 | .unique() | ||
616 | .collect() | ||
617 | } | ||
618 | |||
619 | /// list local variables defined inside `body` that should be returned from extracted function | ||
620 | fn vars_defined_in_body_and_outlive(ctx: &AssistContext, body: &FunctionBody) -> Vec<Local> { | ||
621 | let mut vars_defined_in_body = vars_defined_in_body(&body, ctx); | ||
622 | vars_defined_in_body.retain(|var| var_outlives_body(ctx, body, var)); | ||
623 | vars_defined_in_body | ||
624 | } | ||
625 | |||
626 | /// checks if the relevant local was defined before(outside of) body | ||
627 | fn is_defined_before( | ||
628 | ctx: &AssistContext, | ||
629 | body: &FunctionBody, | ||
630 | src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>, | ||
631 | ) -> bool { | ||
632 | src.file_id.original_file(ctx.db()) == ctx.frange.file_id | ||
633 | && !body.contains_node(&either_syntax(&src.value)) | ||
634 | } | ||
635 | |||
636 | fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode { | ||
637 | match value { | ||
638 | Either::Left(pat) => pat.syntax(), | ||
639 | Either::Right(it) => it.syntax(), | ||
640 | } | ||
641 | } | ||
642 | |||
643 | /// checks if local variable is used after(outside of) body | ||
644 | fn var_outlives_body(ctx: &AssistContext, body: &FunctionBody, var: &Local) -> bool { | ||
645 | let usages = Definition::Local(*var) | ||
646 | .usages(&ctx.sema) | ||
647 | .in_scope(SearchScope::single_file(ctx.frange.file_id)) | ||
648 | .all(); | ||
649 | let mut usages = usages.iter().flat_map(|(_, rs)| rs.iter()); | ||
650 | |||
651 | usages.any(|reference| body.preceedes_range(reference.range)) | ||
652 | } | ||
653 | |||
654 | fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> { | ||
655 | match body.tail_expr() { | ||
656 | Some(expr) => { | ||
657 | let ty = ctx.sema.type_of_expr(&expr)?; | ||
658 | Some(RetType::Expr(ty)) | ||
659 | } | ||
660 | None => Some(RetType::Stmt), | ||
661 | } | ||
662 | } | ||
663 | /// Where to put extracted function definition | ||
664 | #[derive(Debug)] | ||
665 | enum Anchor { | ||
666 | /// Extract free function and put right after current top-level function | ||
667 | Freestanding, | ||
668 | /// Extract method and put right after current function in the impl-block | ||
669 | Method, | ||
670 | } | ||
671 | |||
672 | /// find where to put extracted function definition | ||
673 | /// | ||
674 | /// Function should be put right after returned node | ||
675 | fn scope_for_fn_insertion(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNode> { | ||
676 | match body { | ||
677 | FunctionBody::Expr(e) => scope_for_fn_insertion_node(e.syntax(), anchor), | ||
678 | FunctionBody::Span { elements, .. } => { | ||
679 | let node = elements.iter().find_map(|e| e.as_node())?; | ||
680 | scope_for_fn_insertion_node(&node, anchor) | ||
681 | } | ||
682 | } | ||
683 | } | ||
684 | |||
685 | fn scope_for_fn_insertion_node(node: &SyntaxNode, anchor: Anchor) -> Option<SyntaxNode> { | ||
686 | let mut ancestors = node.ancestors().peekable(); | ||
687 | let mut last_ancestor = None; | ||
688 | while let Some(next_ancestor) = ancestors.next() { | ||
689 | match next_ancestor.kind() { | ||
690 | SyntaxKind::SOURCE_FILE => break, | ||
691 | SyntaxKind::ITEM_LIST => { | ||
692 | if !matches!(anchor, Anchor::Freestanding) { | ||
693 | continue; | ||
694 | } | ||
695 | if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) { | ||
696 | break; | ||
697 | } | ||
698 | } | ||
699 | SyntaxKind::ASSOC_ITEM_LIST => { | ||
700 | if !matches!(anchor, Anchor::Method) { | ||
701 | continue; | ||
702 | } | ||
703 | if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::IMPL) { | ||
704 | break; | ||
705 | } | ||
706 | } | ||
707 | _ => {} | ||
708 | } | ||
709 | last_ancestor = Some(next_ancestor); | ||
710 | } | ||
711 | last_ancestor | ||
712 | } | ||
713 | |||
714 | fn format_replacement(ctx: &AssistContext, fun: &Function) -> String { | ||
715 | let mut buf = String::new(); | ||
716 | |||
717 | match fun.vars_defined_in_body_and_outlive.as_slice() { | ||
718 | [] => {} | ||
719 | [var] => format_to!(buf, "let {} = ", var.name(ctx.db()).unwrap()), | ||
720 | [v0, vs @ ..] => { | ||
721 | buf.push_str("let ("); | ||
722 | format_to!(buf, "{}", v0.name(ctx.db()).unwrap()); | ||
723 | for var in vs { | ||
724 | format_to!(buf, ", {}", var.name(ctx.db()).unwrap()); | ||
725 | } | ||
726 | buf.push_str(") = "); | ||
727 | } | ||
728 | } | ||
729 | |||
730 | if fun.self_param.is_some() { | ||
731 | format_to!(buf, "self."); | ||
732 | } | ||
733 | format_to!(buf, "{}(", fun.name); | ||
734 | format_arg_list_to(&mut buf, fun, ctx); | ||
735 | format_to!(buf, ")"); | ||
736 | |||
737 | if fun.ret_ty.is_unit() { | ||
738 | format_to!(buf, ";"); | ||
739 | } | ||
740 | |||
741 | buf | ||
742 | } | ||
743 | |||
744 | fn format_arg_list_to(buf: &mut String, fun: &Function, ctx: &AssistContext) { | ||
745 | let mut it = fun.params.iter(); | ||
746 | if let Some(param) = it.next() { | ||
747 | format_arg_to(buf, ctx, param); | ||
748 | } | ||
749 | for param in it { | ||
750 | buf.push_str(", "); | ||
751 | format_arg_to(buf, ctx, param); | ||
752 | } | ||
753 | } | ||
754 | |||
755 | fn format_arg_to(buf: &mut String, ctx: &AssistContext, param: &Param) { | ||
756 | format_to!(buf, "{}{}", param.value_prefix(), param.var.name(ctx.db()).unwrap()); | ||
757 | } | ||
758 | |||
759 | fn format_function( | ||
760 | ctx: &AssistContext, | ||
761 | module: hir::Module, | ||
762 | fun: &Function, | ||
763 | old_indent: IndentLevel, | ||
764 | new_indent: IndentLevel, | ||
765 | ) -> String { | ||
766 | let mut fn_def = String::new(); | ||
767 | format_to!(fn_def, "\n\n{}fn $0{}(", new_indent, fun.name); | ||
768 | format_function_param_list_to(&mut fn_def, ctx, module, fun); | ||
769 | fn_def.push(')'); | ||
770 | format_function_ret_to(&mut fn_def, ctx, module, fun); | ||
771 | fn_def.push_str(" {"); | ||
772 | format_function_body_to(&mut fn_def, ctx, old_indent, new_indent, fun); | ||
773 | format_to!(fn_def, "{}}}", new_indent); | ||
774 | |||
775 | fn_def | ||
776 | } | ||
777 | |||
778 | fn format_function_param_list_to( | ||
779 | fn_def: &mut String, | ||
780 | ctx: &AssistContext, | ||
781 | module: hir::Module, | ||
782 | fun: &Function, | ||
783 | ) { | ||
784 | let mut it = fun.params.iter(); | ||
785 | if let Some(self_param) = &fun.self_param { | ||
786 | format_to!(fn_def, "{}", self_param); | ||
787 | } else if let Some(param) = it.next() { | ||
788 | format_param_to(fn_def, ctx, module, param); | ||
789 | } | ||
790 | for param in it { | ||
791 | fn_def.push_str(", "); | ||
792 | format_param_to(fn_def, ctx, module, param); | ||
793 | } | ||
794 | } | ||
795 | |||
796 | fn format_param_to(fn_def: &mut String, ctx: &AssistContext, module: hir::Module, param: &Param) { | ||
797 | format_to!( | ||
798 | fn_def, | ||
799 | "{}{}: {}{}", | ||
800 | param.mut_pattern(), | ||
801 | param.var.name(ctx.db()).unwrap(), | ||
802 | param.type_prefix(), | ||
803 | format_type(¶m.ty, ctx, module) | ||
804 | ); | ||
805 | } | ||
806 | |||
807 | fn format_function_ret_to( | ||
808 | fn_def: &mut String, | ||
809 | ctx: &AssistContext, | ||
810 | module: hir::Module, | ||
811 | fun: &Function, | ||
812 | ) { | ||
813 | if let Some(ty) = fun.ret_ty.as_fn_ret() { | ||
814 | format_to!(fn_def, " -> {}", format_type(ty, ctx, module)); | ||
815 | } else { | ||
816 | match fun.vars_defined_in_body_and_outlive.as_slice() { | ||
817 | [] => {} | ||
818 | [var] => { | ||
819 | format_to!(fn_def, " -> {}", format_type(&var.ty(ctx.db()), ctx, module)); | ||
820 | } | ||
821 | [v0, vs @ ..] => { | ||
822 | format_to!(fn_def, " -> ({}", format_type(&v0.ty(ctx.db()), ctx, module)); | ||
823 | for var in vs { | ||
824 | format_to!(fn_def, ", {}", format_type(&var.ty(ctx.db()), ctx, module)); | ||
825 | } | ||
826 | fn_def.push(')'); | ||
827 | } | ||
828 | } | ||
829 | } | ||
830 | } | ||
831 | |||
832 | fn format_function_body_to( | ||
833 | fn_def: &mut String, | ||
834 | ctx: &AssistContext, | ||
835 | old_indent: IndentLevel, | ||
836 | new_indent: IndentLevel, | ||
837 | fun: &Function, | ||
838 | ) { | ||
839 | match &fun.body { | ||
840 | FunctionBody::Expr(expr) => { | ||
841 | fn_def.push('\n'); | ||
842 | let expr = expr.dedent(old_indent).indent(new_indent + 1); | ||
843 | let expr = fix_param_usages(ctx, &fun.params, expr.syntax()); | ||
844 | format_to!(fn_def, "{}{}", new_indent + 1, expr); | ||
845 | fn_def.push('\n'); | ||
846 | } | ||
847 | FunctionBody::Span { elements, leading_indent } => { | ||
848 | format_to!(fn_def, "{}", leading_indent); | ||
849 | let new_indent_str = format!("\n{}", new_indent + 1); | ||
850 | for mut element in elements { | ||
851 | let new_ws; | ||
852 | if let Some(ws) = element.as_token().cloned().and_then(ast::Whitespace::cast) { | ||
853 | let text = ws.syntax().text(); | ||
854 | if text.contains('\n') { | ||
855 | let new_text = text.replace(&format!("\n{}", old_indent), &new_indent_str); | ||
856 | new_ws = ast::make::tokens::whitespace(&new_text).into(); | ||
857 | element = &new_ws; | ||
858 | } | ||
859 | } | ||
860 | |||
861 | match element { | ||
862 | syntax::NodeOrToken::Node(node) => { | ||
863 | format_to!(fn_def, "{}", fix_param_usages(ctx, &fun.params, node)); | ||
864 | } | ||
865 | syntax::NodeOrToken::Token(token) => { | ||
866 | format_to!(fn_def, "{}", token); | ||
867 | } | ||
868 | } | ||
869 | } | ||
870 | if !fn_def.ends_with('\n') { | ||
871 | fn_def.push('\n'); | ||
872 | } | ||
873 | } | ||
874 | } | ||
875 | |||
876 | match fun.vars_defined_in_body_and_outlive.as_slice() { | ||
877 | [] => {} | ||
878 | [var] => format_to!(fn_def, "{}{}\n", new_indent + 1, var.name(ctx.db()).unwrap()), | ||
879 | [v0, vs @ ..] => { | ||
880 | format_to!(fn_def, "{}({}", new_indent + 1, v0.name(ctx.db()).unwrap()); | ||
881 | for var in vs { | ||
882 | format_to!(fn_def, ", {}", var.name(ctx.db()).unwrap()); | ||
883 | } | ||
884 | fn_def.push_str(")\n"); | ||
885 | } | ||
886 | } | ||
887 | } | ||
888 | |||
889 | fn format_type(ty: &hir::Type, ctx: &AssistContext, module: hir::Module) -> String { | ||
890 | ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "()".to_string()) | ||
891 | } | ||
892 | |||
893 | /// change all usages to account for added `&`/`&mut` for some params | ||
894 | fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { | ||
895 | let mut rewriter = SyntaxRewriter::default(); | ||
896 | for param in params { | ||
897 | if !param.kind().is_ref() { | ||
898 | continue; | ||
899 | } | ||
900 | |||
901 | let usages = LocalUsages::find(ctx, param.var); | ||
902 | let usages = usages | ||
903 | .iter() | ||
904 | .filter(|reference| syntax.text_range().contains_range(reference.range)) | ||
905 | .filter_map(|reference| path_element_of_reference(syntax, reference)); | ||
906 | for path in usages { | ||
907 | match path.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { | ||
908 | Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { | ||
909 | // do nothing | ||
910 | } | ||
911 | Some(ast::Expr::RefExpr(node)) | ||
912 | if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => | ||
913 | { | ||
914 | rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); | ||
915 | } | ||
916 | Some(ast::Expr::RefExpr(node)) | ||
917 | if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => | ||
918 | { | ||
919 | rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); | ||
920 | } | ||
921 | Some(_) | None => { | ||
922 | rewriter.replace_ast(&path, &ast::make::expr_prefix(T![*], path.clone())); | ||
923 | } | ||
924 | }; | ||
925 | } | ||
926 | } | ||
927 | |||
928 | rewriter.rewrite(syntax) | ||
929 | } | ||
930 | |||
931 | #[cfg(test)] | ||
932 | mod tests { | ||
933 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
934 | |||
935 | use super::*; | ||
936 | |||
937 | #[test] | ||
938 | fn no_args_from_binary_expr() { | ||
939 | check_assist( | ||
940 | extract_function, | ||
941 | r#" | ||
942 | fn foo() { | ||
943 | foo($01 + 1$0); | ||
944 | }"#, | ||
945 | r#" | ||
946 | fn foo() { | ||
947 | foo(fun_name()); | ||
948 | } | ||
949 | |||
950 | fn $0fun_name() -> i32 { | ||
951 | 1 + 1 | ||
952 | }"#, | ||
953 | ); | ||
954 | } | ||
955 | |||
956 | #[test] | ||
957 | fn no_args_from_binary_expr_in_module() { | ||
958 | check_assist( | ||
959 | extract_function, | ||
960 | r#" | ||
961 | mod bar { | ||
962 | fn foo() { | ||
963 | foo($01 + 1$0); | ||
964 | } | ||
965 | }"#, | ||
966 | r#" | ||
967 | mod bar { | ||
968 | fn foo() { | ||
969 | foo(fun_name()); | ||
970 | } | ||
971 | |||
972 | fn $0fun_name() -> i32 { | ||
973 | 1 + 1 | ||
974 | } | ||
975 | }"#, | ||
976 | ); | ||
977 | } | ||
978 | |||
979 | #[test] | ||
980 | fn no_args_from_binary_expr_indented() { | ||
981 | check_assist( | ||
982 | extract_function, | ||
983 | r#" | ||
984 | fn foo() { | ||
985 | $0{ 1 + 1 }$0; | ||
986 | }"#, | ||
987 | r#" | ||
988 | fn foo() { | ||
989 | fun_name(); | ||
990 | } | ||
991 | |||
992 | fn $0fun_name() -> i32 { | ||
993 | { 1 + 1 } | ||
994 | }"#, | ||
995 | ); | ||
996 | } | ||
997 | |||
998 | #[test] | ||
999 | fn no_args_from_stmt_with_last_expr() { | ||
1000 | check_assist( | ||
1001 | extract_function, | ||
1002 | r#" | ||
1003 | fn foo() -> i32 { | ||
1004 | let k = 1; | ||
1005 | $0let m = 1; | ||
1006 | m + 1$0 | ||
1007 | }"#, | ||
1008 | r#" | ||
1009 | fn foo() -> i32 { | ||
1010 | let k = 1; | ||
1011 | fun_name() | ||
1012 | } | ||
1013 | |||
1014 | fn $0fun_name() -> i32 { | ||
1015 | let m = 1; | ||
1016 | m + 1 | ||
1017 | }"#, | ||
1018 | ); | ||
1019 | } | ||
1020 | |||
1021 | #[test] | ||
1022 | fn no_args_from_stmt_unit() { | ||
1023 | check_assist( | ||
1024 | extract_function, | ||
1025 | r#" | ||
1026 | fn foo() { | ||
1027 | let k = 3; | ||
1028 | $0let m = 1; | ||
1029 | let n = m + 1;$0 | ||
1030 | let g = 5; | ||
1031 | }"#, | ||
1032 | r#" | ||
1033 | fn foo() { | ||
1034 | let k = 3; | ||
1035 | fun_name(); | ||
1036 | let g = 5; | ||
1037 | } | ||
1038 | |||
1039 | fn $0fun_name() { | ||
1040 | let m = 1; | ||
1041 | let n = m + 1; | ||
1042 | }"#, | ||
1043 | ); | ||
1044 | } | ||
1045 | |||
1046 | #[test] | ||
1047 | fn no_args_if() { | ||
1048 | check_assist( | ||
1049 | extract_function, | ||
1050 | r#" | ||
1051 | fn foo() { | ||
1052 | $0if true { }$0 | ||
1053 | }"#, | ||
1054 | r#" | ||
1055 | fn foo() { | ||
1056 | fun_name(); | ||
1057 | } | ||
1058 | |||
1059 | fn $0fun_name() { | ||
1060 | if true { } | ||
1061 | }"#, | ||
1062 | ); | ||
1063 | } | ||
1064 | |||
1065 | #[test] | ||
1066 | fn no_args_if_else() { | ||
1067 | check_assist( | ||
1068 | extract_function, | ||
1069 | r#" | ||
1070 | fn foo() -> i32 { | ||
1071 | $0if true { 1 } else { 2 }$0 | ||
1072 | }"#, | ||
1073 | r#" | ||
1074 | fn foo() -> i32 { | ||
1075 | fun_name() | ||
1076 | } | ||
1077 | |||
1078 | fn $0fun_name() -> i32 { | ||
1079 | if true { 1 } else { 2 } | ||
1080 | }"#, | ||
1081 | ); | ||
1082 | } | ||
1083 | |||
1084 | #[test] | ||
1085 | fn no_args_if_let_else() { | ||
1086 | check_assist( | ||
1087 | extract_function, | ||
1088 | r#" | ||
1089 | fn foo() -> i32 { | ||
1090 | $0if let true = false { 1 } else { 2 }$0 | ||
1091 | }"#, | ||
1092 | r#" | ||
1093 | fn foo() -> i32 { | ||
1094 | fun_name() | ||
1095 | } | ||
1096 | |||
1097 | fn $0fun_name() -> i32 { | ||
1098 | if let true = false { 1 } else { 2 } | ||
1099 | }"#, | ||
1100 | ); | ||
1101 | } | ||
1102 | |||
1103 | #[test] | ||
1104 | fn no_args_match() { | ||
1105 | check_assist( | ||
1106 | extract_function, | ||
1107 | r#" | ||
1108 | fn foo() -> i32 { | ||
1109 | $0match true { | ||
1110 | true => 1, | ||
1111 | false => 2, | ||
1112 | }$0 | ||
1113 | }"#, | ||
1114 | r#" | ||
1115 | fn foo() -> i32 { | ||
1116 | fun_name() | ||
1117 | } | ||
1118 | |||
1119 | fn $0fun_name() -> i32 { | ||
1120 | match true { | ||
1121 | true => 1, | ||
1122 | false => 2, | ||
1123 | } | ||
1124 | }"#, | ||
1125 | ); | ||
1126 | } | ||
1127 | |||
1128 | #[test] | ||
1129 | fn no_args_while() { | ||
1130 | check_assist( | ||
1131 | extract_function, | ||
1132 | r#" | ||
1133 | fn foo() { | ||
1134 | $0while true { }$0 | ||
1135 | }"#, | ||
1136 | r#" | ||
1137 | fn foo() { | ||
1138 | fun_name(); | ||
1139 | } | ||
1140 | |||
1141 | fn $0fun_name() { | ||
1142 | while true { } | ||
1143 | }"#, | ||
1144 | ); | ||
1145 | } | ||
1146 | |||
1147 | #[test] | ||
1148 | fn no_args_for() { | ||
1149 | check_assist( | ||
1150 | extract_function, | ||
1151 | r#" | ||
1152 | fn foo() { | ||
1153 | $0for v in &[0, 1] { }$0 | ||
1154 | }"#, | ||
1155 | r#" | ||
1156 | fn foo() { | ||
1157 | fun_name(); | ||
1158 | } | ||
1159 | |||
1160 | fn $0fun_name() { | ||
1161 | for v in &[0, 1] { } | ||
1162 | }"#, | ||
1163 | ); | ||
1164 | } | ||
1165 | |||
1166 | #[test] | ||
1167 | fn no_args_from_loop_unit() { | ||
1168 | check_assist( | ||
1169 | extract_function, | ||
1170 | r#" | ||
1171 | fn foo() { | ||
1172 | $0loop { | ||
1173 | let m = 1; | ||
1174 | }$0 | ||
1175 | }"#, | ||
1176 | r#" | ||
1177 | fn foo() { | ||
1178 | fun_name() | ||
1179 | } | ||
1180 | |||
1181 | fn $0fun_name() -> ! { | ||
1182 | loop { | ||
1183 | let m = 1; | ||
1184 | } | ||
1185 | }"#, | ||
1186 | ); | ||
1187 | } | ||
1188 | |||
1189 | #[test] | ||
1190 | fn no_args_from_loop_with_return() { | ||
1191 | check_assist( | ||
1192 | extract_function, | ||
1193 | r#" | ||
1194 | fn foo() { | ||
1195 | let v = $0loop { | ||
1196 | let m = 1; | ||
1197 | break m; | ||
1198 | }$0; | ||
1199 | }"#, | ||
1200 | r#" | ||
1201 | fn foo() { | ||
1202 | let v = fun_name(); | ||
1203 | } | ||
1204 | |||
1205 | fn $0fun_name() -> i32 { | ||
1206 | loop { | ||
1207 | let m = 1; | ||
1208 | break m; | ||
1209 | } | ||
1210 | }"#, | ||
1211 | ); | ||
1212 | } | ||
1213 | |||
1214 | #[test] | ||
1215 | fn no_args_from_match() { | ||
1216 | check_assist( | ||
1217 | extract_function, | ||
1218 | r#" | ||
1219 | fn foo() { | ||
1220 | let v: i32 = $0match Some(1) { | ||
1221 | Some(x) => x, | ||
1222 | None => 0, | ||
1223 | }$0; | ||
1224 | }"#, | ||
1225 | r#" | ||
1226 | fn foo() { | ||
1227 | let v: i32 = fun_name(); | ||
1228 | } | ||
1229 | |||
1230 | fn $0fun_name() -> i32 { | ||
1231 | match Some(1) { | ||
1232 | Some(x) => x, | ||
1233 | None => 0, | ||
1234 | } | ||
1235 | }"#, | ||
1236 | ); | ||
1237 | } | ||
1238 | |||
1239 | #[test] | ||
1240 | fn argument_form_expr() { | ||
1241 | check_assist( | ||
1242 | extract_function, | ||
1243 | r" | ||
1244 | fn foo() -> u32 { | ||
1245 | let n = 2; | ||
1246 | $0n+2$0 | ||
1247 | }", | ||
1248 | r" | ||
1249 | fn foo() -> u32 { | ||
1250 | let n = 2; | ||
1251 | fun_name(n) | ||
1252 | } | ||
1253 | |||
1254 | fn $0fun_name(n: u32) -> u32 { | ||
1255 | n+2 | ||
1256 | }", | ||
1257 | ) | ||
1258 | } | ||
1259 | |||
1260 | #[test] | ||
1261 | fn argument_used_twice_form_expr() { | ||
1262 | check_assist( | ||
1263 | extract_function, | ||
1264 | r" | ||
1265 | fn foo() -> u32 { | ||
1266 | let n = 2; | ||
1267 | $0n+n$0 | ||
1268 | }", | ||
1269 | r" | ||
1270 | fn foo() -> u32 { | ||
1271 | let n = 2; | ||
1272 | fun_name(n) | ||
1273 | } | ||
1274 | |||
1275 | fn $0fun_name(n: u32) -> u32 { | ||
1276 | n+n | ||
1277 | }", | ||
1278 | ) | ||
1279 | } | ||
1280 | |||
1281 | #[test] | ||
1282 | fn two_arguments_form_expr() { | ||
1283 | check_assist( | ||
1284 | extract_function, | ||
1285 | r" | ||
1286 | fn foo() -> u32 { | ||
1287 | let n = 2; | ||
1288 | let m = 3; | ||
1289 | $0n+n*m$0 | ||
1290 | }", | ||
1291 | r" | ||
1292 | fn foo() -> u32 { | ||
1293 | let n = 2; | ||
1294 | let m = 3; | ||
1295 | fun_name(n, m) | ||
1296 | } | ||
1297 | |||
1298 | fn $0fun_name(n: u32, m: u32) -> u32 { | ||
1299 | n+n*m | ||
1300 | }", | ||
1301 | ) | ||
1302 | } | ||
1303 | |||
1304 | #[test] | ||
1305 | fn argument_and_locals() { | ||
1306 | check_assist( | ||
1307 | extract_function, | ||
1308 | r" | ||
1309 | fn foo() -> u32 { | ||
1310 | let n = 2; | ||
1311 | $0let m = 1; | ||
1312 | n + m$0 | ||
1313 | }", | ||
1314 | r" | ||
1315 | fn foo() -> u32 { | ||
1316 | let n = 2; | ||
1317 | fun_name(n) | ||
1318 | } | ||
1319 | |||
1320 | fn $0fun_name(n: u32) -> u32 { | ||
1321 | let m = 1; | ||
1322 | n + m | ||
1323 | }", | ||
1324 | ) | ||
1325 | } | ||
1326 | |||
1327 | #[test] | ||
1328 | fn in_comment_is_not_applicable() { | ||
1329 | mark::check!(extract_function_in_comment_is_not_applicable); | ||
1330 | check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }"); | ||
1331 | } | ||
1332 | |||
1333 | #[test] | ||
1334 | fn part_of_expr_stmt() { | ||
1335 | check_assist( | ||
1336 | extract_function, | ||
1337 | " | ||
1338 | fn foo() { | ||
1339 | $01$0 + 1; | ||
1340 | }", | ||
1341 | " | ||
1342 | fn foo() { | ||
1343 | fun_name() + 1; | ||
1344 | } | ||
1345 | |||
1346 | fn $0fun_name() -> i32 { | ||
1347 | 1 | ||
1348 | }", | ||
1349 | ); | ||
1350 | } | ||
1351 | |||
1352 | #[test] | ||
1353 | fn function_expr() { | ||
1354 | check_assist( | ||
1355 | extract_function, | ||
1356 | r#" | ||
1357 | fn foo() { | ||
1358 | $0bar(1 + 1)$0 | ||
1359 | }"#, | ||
1360 | r#" | ||
1361 | fn foo() { | ||
1362 | fun_name(); | ||
1363 | } | ||
1364 | |||
1365 | fn $0fun_name() { | ||
1366 | bar(1 + 1) | ||
1367 | }"#, | ||
1368 | ) | ||
1369 | } | ||
1370 | |||
1371 | #[test] | ||
1372 | fn extract_from_nested() { | ||
1373 | check_assist( | ||
1374 | extract_function, | ||
1375 | r" | ||
1376 | fn main() { | ||
1377 | let x = true; | ||
1378 | let tuple = match x { | ||
1379 | true => ($02 + 2$0, true) | ||
1380 | _ => (0, false) | ||
1381 | }; | ||
1382 | }", | ||
1383 | r" | ||
1384 | fn main() { | ||
1385 | let x = true; | ||
1386 | let tuple = match x { | ||
1387 | true => (fun_name(), true) | ||
1388 | _ => (0, false) | ||
1389 | }; | ||
1390 | } | ||
1391 | |||
1392 | fn $0fun_name() -> i32 { | ||
1393 | 2 + 2 | ||
1394 | }", | ||
1395 | ); | ||
1396 | } | ||
1397 | |||
1398 | #[test] | ||
1399 | fn param_from_closure() { | ||
1400 | check_assist( | ||
1401 | extract_function, | ||
1402 | r" | ||
1403 | fn main() { | ||
1404 | let lambda = |x: u32| $0x * 2$0; | ||
1405 | }", | ||
1406 | r" | ||
1407 | fn main() { | ||
1408 | let lambda = |x: u32| fun_name(x); | ||
1409 | } | ||
1410 | |||
1411 | fn $0fun_name(x: u32) -> u32 { | ||
1412 | x * 2 | ||
1413 | }", | ||
1414 | ); | ||
1415 | } | ||
1416 | |||
1417 | #[test] | ||
1418 | fn extract_return_stmt() { | ||
1419 | check_assist( | ||
1420 | extract_function, | ||
1421 | r" | ||
1422 | fn foo() -> u32 { | ||
1423 | $0return 2 + 2$0; | ||
1424 | }", | ||
1425 | r" | ||
1426 | fn foo() -> u32 { | ||
1427 | return fun_name(); | ||
1428 | } | ||
1429 | |||
1430 | fn $0fun_name() -> u32 { | ||
1431 | 2 + 2 | ||
1432 | }", | ||
1433 | ); | ||
1434 | } | ||
1435 | |||
1436 | #[test] | ||
1437 | fn does_not_add_extra_whitespace() { | ||
1438 | check_assist( | ||
1439 | extract_function, | ||
1440 | r" | ||
1441 | fn foo() -> u32 { | ||
1442 | |||
1443 | |||
1444 | $0return 2 + 2$0; | ||
1445 | }", | ||
1446 | r" | ||
1447 | fn foo() -> u32 { | ||
1448 | |||
1449 | |||
1450 | return fun_name(); | ||
1451 | } | ||
1452 | |||
1453 | fn $0fun_name() -> u32 { | ||
1454 | 2 + 2 | ||
1455 | }", | ||
1456 | ); | ||
1457 | } | ||
1458 | |||
1459 | #[test] | ||
1460 | fn break_stmt() { | ||
1461 | check_assist( | ||
1462 | extract_function, | ||
1463 | r" | ||
1464 | fn main() { | ||
1465 | let result = loop { | ||
1466 | $0break 2 + 2$0; | ||
1467 | }; | ||
1468 | }", | ||
1469 | r" | ||
1470 | fn main() { | ||
1471 | let result = loop { | ||
1472 | break fun_name(); | ||
1473 | }; | ||
1474 | } | ||
1475 | |||
1476 | fn $0fun_name() -> i32 { | ||
1477 | 2 + 2 | ||
1478 | }", | ||
1479 | ); | ||
1480 | } | ||
1481 | |||
1482 | #[test] | ||
1483 | fn extract_cast() { | ||
1484 | check_assist( | ||
1485 | extract_function, | ||
1486 | r" | ||
1487 | fn main() { | ||
1488 | let v = $00f32 as u32$0; | ||
1489 | }", | ||
1490 | r" | ||
1491 | fn main() { | ||
1492 | let v = fun_name(); | ||
1493 | } | ||
1494 | |||
1495 | fn $0fun_name() -> u32 { | ||
1496 | 0f32 as u32 | ||
1497 | }", | ||
1498 | ); | ||
1499 | } | ||
1500 | |||
1501 | #[test] | ||
1502 | fn return_not_applicable() { | ||
1503 | check_assist_not_applicable(extract_function, r"fn foo() { $0return$0; } "); | ||
1504 | } | ||
1505 | |||
1506 | #[test] | ||
1507 | fn method_to_freestanding() { | ||
1508 | check_assist( | ||
1509 | extract_function, | ||
1510 | r" | ||
1511 | struct S; | ||
1512 | |||
1513 | impl S { | ||
1514 | fn foo(&self) -> i32 { | ||
1515 | $01+1$0 | ||
1516 | } | ||
1517 | }", | ||
1518 | r" | ||
1519 | struct S; | ||
1520 | |||
1521 | impl S { | ||
1522 | fn foo(&self) -> i32 { | ||
1523 | fun_name() | ||
1524 | } | ||
1525 | } | ||
1526 | |||
1527 | fn $0fun_name() -> i32 { | ||
1528 | 1+1 | ||
1529 | }", | ||
1530 | ); | ||
1531 | } | ||
1532 | |||
1533 | #[test] | ||
1534 | fn method_with_reference() { | ||
1535 | check_assist( | ||
1536 | extract_function, | ||
1537 | r" | ||
1538 | struct S { f: i32 }; | ||
1539 | |||
1540 | impl S { | ||
1541 | fn foo(&self) -> i32 { | ||
1542 | $01+self.f$0 | ||
1543 | } | ||
1544 | }", | ||
1545 | r" | ||
1546 | struct S { f: i32 }; | ||
1547 | |||
1548 | impl S { | ||
1549 | fn foo(&self) -> i32 { | ||
1550 | self.fun_name() | ||
1551 | } | ||
1552 | |||
1553 | fn $0fun_name(&self) -> i32 { | ||
1554 | 1+self.f | ||
1555 | } | ||
1556 | }", | ||
1557 | ); | ||
1558 | } | ||
1559 | |||
1560 | #[test] | ||
1561 | fn method_with_mut() { | ||
1562 | check_assist( | ||
1563 | extract_function, | ||
1564 | r" | ||
1565 | struct S { f: i32 }; | ||
1566 | |||
1567 | impl S { | ||
1568 | fn foo(&mut self) { | ||
1569 | $0self.f += 1;$0 | ||
1570 | } | ||
1571 | }", | ||
1572 | r" | ||
1573 | struct S { f: i32 }; | ||
1574 | |||
1575 | impl S { | ||
1576 | fn foo(&mut self) { | ||
1577 | self.fun_name(); | ||
1578 | } | ||
1579 | |||
1580 | fn $0fun_name(&mut self) { | ||
1581 | self.f += 1; | ||
1582 | } | ||
1583 | }", | ||
1584 | ); | ||
1585 | } | ||
1586 | |||
1587 | #[test] | ||
1588 | fn variable_defined_inside_and_used_after_no_ret() { | ||
1589 | check_assist( | ||
1590 | extract_function, | ||
1591 | r" | ||
1592 | fn foo() { | ||
1593 | let n = 1; | ||
1594 | $0let k = n * n;$0 | ||
1595 | let m = k + 1; | ||
1596 | }", | ||
1597 | r" | ||
1598 | fn foo() { | ||
1599 | let n = 1; | ||
1600 | let k = fun_name(n); | ||
1601 | let m = k + 1; | ||
1602 | } | ||
1603 | |||
1604 | fn $0fun_name(n: i32) -> i32 { | ||
1605 | let k = n * n; | ||
1606 | k | ||
1607 | }", | ||
1608 | ); | ||
1609 | } | ||
1610 | |||
1611 | #[test] | ||
1612 | fn two_variables_defined_inside_and_used_after_no_ret() { | ||
1613 | check_assist( | ||
1614 | extract_function, | ||
1615 | r" | ||
1616 | fn foo() { | ||
1617 | let n = 1; | ||
1618 | $0let k = n * n; | ||
1619 | let m = k + 2;$0 | ||
1620 | let h = k + m; | ||
1621 | }", | ||
1622 | r" | ||
1623 | fn foo() { | ||
1624 | let n = 1; | ||
1625 | let (k, m) = fun_name(n); | ||
1626 | let h = k + m; | ||
1627 | } | ||
1628 | |||
1629 | fn $0fun_name(n: i32) -> (i32, i32) { | ||
1630 | let k = n * n; | ||
1631 | let m = k + 2; | ||
1632 | (k, m) | ||
1633 | }", | ||
1634 | ); | ||
1635 | } | ||
1636 | |||
1637 | #[test] | ||
1638 | fn nontrivial_patterns_define_variables() { | ||
1639 | check_assist( | ||
1640 | extract_function, | ||
1641 | r" | ||
1642 | struct Counter(i32); | ||
1643 | fn foo() { | ||
1644 | $0let Counter(n) = Counter(0);$0 | ||
1645 | let m = n; | ||
1646 | }", | ||
1647 | r" | ||
1648 | struct Counter(i32); | ||
1649 | fn foo() { | ||
1650 | let n = fun_name(); | ||
1651 | let m = n; | ||
1652 | } | ||
1653 | |||
1654 | fn $0fun_name() -> i32 { | ||
1655 | let Counter(n) = Counter(0); | ||
1656 | n | ||
1657 | }", | ||
1658 | ); | ||
1659 | } | ||
1660 | |||
1661 | #[test] | ||
1662 | fn struct_with_two_fields_pattern_define_variables() { | ||
1663 | check_assist( | ||
1664 | extract_function, | ||
1665 | r" | ||
1666 | struct Counter { n: i32, m: i32 }; | ||
1667 | fn foo() { | ||
1668 | $0let Counter { n, m: k } = Counter { n: 1, m: 2 };$0 | ||
1669 | let h = n + k; | ||
1670 | }", | ||
1671 | r" | ||
1672 | struct Counter { n: i32, m: i32 }; | ||
1673 | fn foo() { | ||
1674 | let (n, k) = fun_name(); | ||
1675 | let h = n + k; | ||
1676 | } | ||
1677 | |||
1678 | fn $0fun_name() -> (i32, i32) { | ||
1679 | let Counter { n, m: k } = Counter { n: 1, m: 2 }; | ||
1680 | (n, k) | ||
1681 | }", | ||
1682 | ); | ||
1683 | } | ||
1684 | |||
1685 | #[test] | ||
1686 | fn mut_var_from_outer_scope() { | ||
1687 | check_assist( | ||
1688 | extract_function, | ||
1689 | r" | ||
1690 | fn foo() { | ||
1691 | let mut n = 1; | ||
1692 | $0n += 1;$0 | ||
1693 | let m = n + 1; | ||
1694 | }", | ||
1695 | r" | ||
1696 | fn foo() { | ||
1697 | let mut n = 1; | ||
1698 | fun_name(&mut n); | ||
1699 | let m = n + 1; | ||
1700 | } | ||
1701 | |||
1702 | fn $0fun_name(n: &mut i32) { | ||
1703 | *n += 1; | ||
1704 | }", | ||
1705 | ); | ||
1706 | } | ||
1707 | |||
1708 | #[test] | ||
1709 | fn mut_field_from_outer_scope() { | ||
1710 | check_assist( | ||
1711 | extract_function, | ||
1712 | r" | ||
1713 | struct C { n: i32 } | ||
1714 | fn foo() { | ||
1715 | let mut c = C { n: 0 }; | ||
1716 | $0c.n += 1;$0 | ||
1717 | let m = c.n + 1; | ||
1718 | }", | ||
1719 | r" | ||
1720 | struct C { n: i32 } | ||
1721 | fn foo() { | ||
1722 | let mut c = C { n: 0 }; | ||
1723 | fun_name(&mut c); | ||
1724 | let m = c.n + 1; | ||
1725 | } | ||
1726 | |||
1727 | fn $0fun_name(c: &mut C) { | ||
1728 | c.n += 1; | ||
1729 | }", | ||
1730 | ); | ||
1731 | } | ||
1732 | |||
1733 | #[test] | ||
1734 | fn mut_nested_field_from_outer_scope() { | ||
1735 | check_assist( | ||
1736 | extract_function, | ||
1737 | r" | ||
1738 | struct P { n: i32} | ||
1739 | struct C { p: P } | ||
1740 | fn foo() { | ||
1741 | let mut c = C { p: P { n: 0 } }; | ||
1742 | let mut v = C { p: P { n: 0 } }; | ||
1743 | let u = C { p: P { n: 0 } }; | ||
1744 | $0c.p.n += u.p.n; | ||
1745 | let r = &mut v.p.n;$0 | ||
1746 | let m = c.p.n + v.p.n + u.p.n; | ||
1747 | }", | ||
1748 | r" | ||
1749 | struct P { n: i32} | ||
1750 | struct C { p: P } | ||
1751 | fn foo() { | ||
1752 | let mut c = C { p: P { n: 0 } }; | ||
1753 | let mut v = C { p: P { n: 0 } }; | ||
1754 | let u = C { p: P { n: 0 } }; | ||
1755 | fun_name(&mut c, &u, &mut v); | ||
1756 | let m = c.p.n + v.p.n + u.p.n; | ||
1757 | } | ||
1758 | |||
1759 | fn $0fun_name(c: &mut C, u: &C, v: &mut C) { | ||
1760 | c.p.n += u.p.n; | ||
1761 | let r = &mut v.p.n; | ||
1762 | }", | ||
1763 | ); | ||
1764 | } | ||
1765 | |||
1766 | #[test] | ||
1767 | fn mut_param_many_usages_stmt() { | ||
1768 | check_assist( | ||
1769 | extract_function, | ||
1770 | r" | ||
1771 | fn bar(k: i32) {} | ||
1772 | trait I: Copy { | ||
1773 | fn succ(&self) -> Self; | ||
1774 | fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v } | ||
1775 | } | ||
1776 | impl I for i32 { | ||
1777 | fn succ(&self) -> Self { *self + 1 } | ||
1778 | } | ||
1779 | fn foo() { | ||
1780 | let mut n = 1; | ||
1781 | $0n += n; | ||
1782 | bar(n); | ||
1783 | bar(n+1); | ||
1784 | bar(n*n); | ||
1785 | bar(&n); | ||
1786 | n.inc(); | ||
1787 | let v = &mut n; | ||
1788 | *v = v.succ(); | ||
1789 | n.succ();$0 | ||
1790 | let m = n + 1; | ||
1791 | }", | ||
1792 | r" | ||
1793 | fn bar(k: i32) {} | ||
1794 | trait I: Copy { | ||
1795 | fn succ(&self) -> Self; | ||
1796 | fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v } | ||
1797 | } | ||
1798 | impl I for i32 { | ||
1799 | fn succ(&self) -> Self { *self + 1 } | ||
1800 | } | ||
1801 | fn foo() { | ||
1802 | let mut n = 1; | ||
1803 | fun_name(&mut n); | ||
1804 | let m = n + 1; | ||
1805 | } | ||
1806 | |||
1807 | fn $0fun_name(n: &mut i32) { | ||
1808 | *n += *n; | ||
1809 | bar(*n); | ||
1810 | bar(*n+1); | ||
1811 | bar(*n**n); | ||
1812 | bar(&*n); | ||
1813 | n.inc(); | ||
1814 | let v = n; | ||
1815 | *v = v.succ(); | ||
1816 | n.succ(); | ||
1817 | }", | ||
1818 | ); | ||
1819 | } | ||
1820 | |||
1821 | #[test] | ||
1822 | fn mut_param_many_usages_expr() { | ||
1823 | check_assist( | ||
1824 | extract_function, | ||
1825 | r" | ||
1826 | fn bar(k: i32) {} | ||
1827 | trait I: Copy { | ||
1828 | fn succ(&self) -> Self; | ||
1829 | fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v } | ||
1830 | } | ||
1831 | impl I for i32 { | ||
1832 | fn succ(&self) -> Self { *self + 1 } | ||
1833 | } | ||
1834 | fn foo() { | ||
1835 | let mut n = 1; | ||
1836 | $0{ | ||
1837 | n += n; | ||
1838 | bar(n); | ||
1839 | bar(n+1); | ||
1840 | bar(n*n); | ||
1841 | bar(&n); | ||
1842 | n.inc(); | ||
1843 | let v = &mut n; | ||
1844 | *v = v.succ(); | ||
1845 | n.succ(); | ||
1846 | }$0 | ||
1847 | let m = n + 1; | ||
1848 | }", | ||
1849 | r" | ||
1850 | fn bar(k: i32) {} | ||
1851 | trait I: Copy { | ||
1852 | fn succ(&self) -> Self; | ||
1853 | fn inc(&mut self) -> Self { let v = self.succ(); *self = v; v } | ||
1854 | } | ||
1855 | impl I for i32 { | ||
1856 | fn succ(&self) -> Self { *self + 1 } | ||
1857 | } | ||
1858 | fn foo() { | ||
1859 | let mut n = 1; | ||
1860 | fun_name(&mut n); | ||
1861 | let m = n + 1; | ||
1862 | } | ||
1863 | |||
1864 | fn $0fun_name(n: &mut i32) { | ||
1865 | { | ||
1866 | *n += *n; | ||
1867 | bar(*n); | ||
1868 | bar(*n+1); | ||
1869 | bar(*n**n); | ||
1870 | bar(&*n); | ||
1871 | n.inc(); | ||
1872 | let v = n; | ||
1873 | *v = v.succ(); | ||
1874 | n.succ(); | ||
1875 | } | ||
1876 | }", | ||
1877 | ); | ||
1878 | } | ||
1879 | |||
1880 | #[test] | ||
1881 | fn mut_param_by_value() { | ||
1882 | check_assist( | ||
1883 | extract_function, | ||
1884 | r" | ||
1885 | fn foo() { | ||
1886 | let mut n = 1; | ||
1887 | $0n += 1;$0 | ||
1888 | }", | ||
1889 | r" | ||
1890 | fn foo() { | ||
1891 | let mut n = 1; | ||
1892 | fun_name(n); | ||
1893 | } | ||
1894 | |||
1895 | fn $0fun_name(mut n: i32) { | ||
1896 | n += 1; | ||
1897 | }", | ||
1898 | ); | ||
1899 | } | ||
1900 | |||
1901 | #[test] | ||
1902 | fn mut_param_because_of_mut_ref() { | ||
1903 | check_assist( | ||
1904 | extract_function, | ||
1905 | r" | ||
1906 | fn foo() { | ||
1907 | let mut n = 1; | ||
1908 | $0let v = &mut n; | ||
1909 | *v += 1;$0 | ||
1910 | let k = n; | ||
1911 | }", | ||
1912 | r" | ||
1913 | fn foo() { | ||
1914 | let mut n = 1; | ||
1915 | fun_name(&mut n); | ||
1916 | let k = n; | ||
1917 | } | ||
1918 | |||
1919 | fn $0fun_name(n: &mut i32) { | ||
1920 | let v = n; | ||
1921 | *v += 1; | ||
1922 | }", | ||
1923 | ); | ||
1924 | } | ||
1925 | |||
1926 | #[test] | ||
1927 | fn mut_param_by_value_because_of_mut_ref() { | ||
1928 | check_assist( | ||
1929 | extract_function, | ||
1930 | r" | ||
1931 | fn foo() { | ||
1932 | let mut n = 1; | ||
1933 | $0let v = &mut n; | ||
1934 | *v += 1;$0 | ||
1935 | }", | ||
1936 | r" | ||
1937 | fn foo() { | ||
1938 | let mut n = 1; | ||
1939 | fun_name(n); | ||
1940 | } | ||
1941 | |||
1942 | fn $0fun_name(mut n: i32) { | ||
1943 | let v = &mut n; | ||
1944 | *v += 1; | ||
1945 | }", | ||
1946 | ); | ||
1947 | } | ||
1948 | |||
1949 | #[test] | ||
1950 | fn mut_method_call() { | ||
1951 | check_assist( | ||
1952 | extract_function, | ||
1953 | r" | ||
1954 | trait I { | ||
1955 | fn inc(&mut self); | ||
1956 | } | ||
1957 | impl I for i32 { | ||
1958 | fn inc(&mut self) { *self += 1 } | ||
1959 | } | ||
1960 | fn foo() { | ||
1961 | let mut n = 1; | ||
1962 | $0n.inc();$0 | ||
1963 | }", | ||
1964 | r" | ||
1965 | trait I { | ||
1966 | fn inc(&mut self); | ||
1967 | } | ||
1968 | impl I for i32 { | ||
1969 | fn inc(&mut self) { *self += 1 } | ||
1970 | } | ||
1971 | fn foo() { | ||
1972 | let mut n = 1; | ||
1973 | fun_name(n); | ||
1974 | } | ||
1975 | |||
1976 | fn $0fun_name(mut n: i32) { | ||
1977 | n.inc(); | ||
1978 | }", | ||
1979 | ); | ||
1980 | } | ||
1981 | |||
1982 | #[test] | ||
1983 | fn shared_method_call() { | ||
1984 | check_assist( | ||
1985 | extract_function, | ||
1986 | r" | ||
1987 | trait I { | ||
1988 | fn succ(&self); | ||
1989 | } | ||
1990 | impl I for i32 { | ||
1991 | fn succ(&self) { *self + 1 } | ||
1992 | } | ||
1993 | fn foo() { | ||
1994 | let mut n = 1; | ||
1995 | $0n.succ();$0 | ||
1996 | }", | ||
1997 | r" | ||
1998 | trait I { | ||
1999 | fn succ(&self); | ||
2000 | } | ||
2001 | impl I for i32 { | ||
2002 | fn succ(&self) { *self + 1 } | ||
2003 | } | ||
2004 | fn foo() { | ||
2005 | let mut n = 1; | ||
2006 | fun_name(n); | ||
2007 | } | ||
2008 | |||
2009 | fn $0fun_name(n: i32) { | ||
2010 | n.succ(); | ||
2011 | }", | ||
2012 | ); | ||
2013 | } | ||
2014 | |||
2015 | #[test] | ||
2016 | fn mut_method_call_with_other_receiver() { | ||
2017 | check_assist( | ||
2018 | extract_function, | ||
2019 | r" | ||
2020 | trait I { | ||
2021 | fn inc(&mut self, n: i32); | ||
2022 | } | ||
2023 | impl I for i32 { | ||
2024 | fn inc(&mut self, n: i32) { *self += n } | ||
2025 | } | ||
2026 | fn foo() { | ||
2027 | let mut n = 1; | ||
2028 | $0let mut m = 2; | ||
2029 | m.inc(n);$0 | ||
2030 | }", | ||
2031 | r" | ||
2032 | trait I { | ||
2033 | fn inc(&mut self, n: i32); | ||
2034 | } | ||
2035 | impl I for i32 { | ||
2036 | fn inc(&mut self, n: i32) { *self += n } | ||
2037 | } | ||
2038 | fn foo() { | ||
2039 | let mut n = 1; | ||
2040 | fun_name(n); | ||
2041 | } | ||
2042 | |||
2043 | fn $0fun_name(n: i32) { | ||
2044 | let mut m = 2; | ||
2045 | m.inc(n); | ||
2046 | }", | ||
2047 | ); | ||
2048 | } | ||
2049 | |||
2050 | #[test] | ||
2051 | fn non_copy_without_usages_after() { | ||
2052 | check_assist( | ||
2053 | extract_function, | ||
2054 | r" | ||
2055 | struct Counter(i32); | ||
2056 | fn foo() { | ||
2057 | let c = Counter(0); | ||
2058 | $0let n = c.0;$0 | ||
2059 | }", | ||
2060 | r" | ||
2061 | struct Counter(i32); | ||
2062 | fn foo() { | ||
2063 | let c = Counter(0); | ||
2064 | fun_name(c); | ||
2065 | } | ||
2066 | |||
2067 | fn $0fun_name(c: Counter) { | ||
2068 | let n = c.0; | ||
2069 | }", | ||
2070 | ); | ||
2071 | } | ||
2072 | |||
2073 | #[test] | ||
2074 | fn non_copy_used_after() { | ||
2075 | check_assist( | ||
2076 | extract_function, | ||
2077 | r" | ||
2078 | struct Counter(i32); | ||
2079 | fn foo() { | ||
2080 | let c = Counter(0); | ||
2081 | $0let n = c.0;$0 | ||
2082 | let m = c.0; | ||
2083 | }", | ||
2084 | r" | ||
2085 | struct Counter(i32); | ||
2086 | fn foo() { | ||
2087 | let c = Counter(0); | ||
2088 | fun_name(&c); | ||
2089 | let m = c.0; | ||
2090 | } | ||
2091 | |||
2092 | fn $0fun_name(c: &Counter) { | ||
2093 | let n = c.0; | ||
2094 | }", | ||
2095 | ); | ||
2096 | } | ||
2097 | |||
2098 | #[test] | ||
2099 | fn indented_stmts() { | ||
2100 | check_assist( | ||
2101 | extract_function, | ||
2102 | r" | ||
2103 | fn foo() { | ||
2104 | if true { | ||
2105 | loop { | ||
2106 | $0let n = 1; | ||
2107 | let m = 2;$0 | ||
2108 | } | ||
2109 | } | ||
2110 | }", | ||
2111 | r" | ||
2112 | fn foo() { | ||
2113 | if true { | ||
2114 | loop { | ||
2115 | fun_name(); | ||
2116 | } | ||
2117 | } | ||
2118 | } | ||
2119 | |||
2120 | fn $0fun_name() { | ||
2121 | let n = 1; | ||
2122 | let m = 2; | ||
2123 | }", | ||
2124 | ); | ||
2125 | } | ||
2126 | |||
2127 | #[test] | ||
2128 | fn indented_stmts_inside_mod() { | ||
2129 | check_assist( | ||
2130 | extract_function, | ||
2131 | r" | ||
2132 | mod bar { | ||
2133 | fn foo() { | ||
2134 | if true { | ||
2135 | loop { | ||
2136 | $0let n = 1; | ||
2137 | let m = 2;$0 | ||
2138 | } | ||
2139 | } | ||
2140 | } | ||
2141 | }", | ||
2142 | r" | ||
2143 | mod bar { | ||
2144 | fn foo() { | ||
2145 | if true { | ||
2146 | loop { | ||
2147 | fun_name(); | ||
2148 | } | ||
2149 | } | ||
2150 | } | ||
2151 | |||
2152 | fn $0fun_name() { | ||
2153 | let n = 1; | ||
2154 | let m = 2; | ||
2155 | } | ||
2156 | }", | ||
2157 | ); | ||
2158 | } | ||
2159 | } | ||
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index e3ef04932..5c7678b53 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -151,8 +151,8 @@ fn insert_import( | |||
151 | ctx.config.insert_use.prefix_kind, | 151 | ctx.config.insert_use.prefix_kind, |
152 | ); | 152 | ); |
153 | if let Some(mut mod_path) = mod_path { | 153 | if let Some(mut mod_path) = mod_path { |
154 | mod_path.segments.pop(); | 154 | mod_path.pop_segment(); |
155 | mod_path.segments.push(variant_hir_name.clone()); | 155 | mod_path.push_segment(variant_hir_name.clone()); |
156 | let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; | 156 | let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; |
157 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); | 157 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); |
158 | } | 158 | } |
diff --git a/crates/assists/src/handlers/generate_enum_match_method.rs b/crates/assists/src/handlers/generate_enum_match_method.rs new file mode 100644 index 000000000..ee89d4208 --- /dev/null +++ b/crates/assists/src/handlers/generate_enum_match_method.rs | |||
@@ -0,0 +1,221 @@ | |||
1 | use stdx::{format_to, to_lower_snake_case}; | ||
2 | use syntax::ast::{self, AstNode, NameOwner}; | ||
3 | use syntax::{ast::VisibilityOwner, T}; | ||
4 | use test_utils::mark; | ||
5 | |||
6 | use crate::{utils::find_struct_impl, AssistContext, AssistId, AssistKind, Assists}; | ||
7 | |||
8 | // Assist: generate_enum_match_method | ||
9 | // | ||
10 | // Generate an `is_` method for an enum variant. | ||
11 | // | ||
12 | // ``` | ||
13 | // enum Version { | ||
14 | // Undefined, | ||
15 | // Minor$0, | ||
16 | // Major, | ||
17 | // } | ||
18 | // ``` | ||
19 | // -> | ||
20 | // ``` | ||
21 | // enum Version { | ||
22 | // Undefined, | ||
23 | // Minor, | ||
24 | // Major, | ||
25 | // } | ||
26 | // | ||
27 | // impl Version { | ||
28 | // /// Returns `true` if the version is [`Minor`]. | ||
29 | // fn is_minor(&self) -> bool { | ||
30 | // matches!(self, Self::Minor) | ||
31 | // } | ||
32 | // } | ||
33 | // ``` | ||
34 | pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
35 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | ||
36 | let variant_name = variant.name()?; | ||
37 | let parent_enum = variant.parent_enum(); | ||
38 | if !matches!(variant.kind(), ast::StructKind::Unit) { | ||
39 | mark::hit!(test_gen_enum_match_on_non_unit_variant_not_implemented); | ||
40 | return None; | ||
41 | } | ||
42 | |||
43 | let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); | ||
44 | let fn_name = to_lower_snake_case(&variant_name.to_string()); | ||
45 | |||
46 | // Return early if we've found an existing new fn | ||
47 | let impl_def = find_struct_impl( | ||
48 | &ctx, | ||
49 | &ast::AdtDef::Enum(parent_enum.clone()), | ||
50 | format!("is_{}", fn_name).as_str(), | ||
51 | )?; | ||
52 | |||
53 | let target = variant.syntax().text_range(); | ||
54 | acc.add( | ||
55 | AssistId("generate_enum_match_method", AssistKind::Generate), | ||
56 | "Generate an `is_` method for an enum variant", | ||
57 | target, | ||
58 | |builder| { | ||
59 | let mut buf = String::with_capacity(512); | ||
60 | |||
61 | if impl_def.is_some() { | ||
62 | buf.push('\n'); | ||
63 | } | ||
64 | |||
65 | let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); | ||
66 | |||
67 | format_to!( | ||
68 | buf, | ||
69 | " /// Returns `true` if the {} is [`{}`]. | ||
70 | {}fn is_{}(&self) -> bool {{ | ||
71 | matches!(self, Self::{}) | ||
72 | }}", | ||
73 | enum_lowercase_name, | ||
74 | variant_name, | ||
75 | vis, | ||
76 | fn_name, | ||
77 | variant_name | ||
78 | ); | ||
79 | |||
80 | let start_offset = impl_def | ||
81 | .and_then(|impl_def| { | ||
82 | buf.push('\n'); | ||
83 | let start = impl_def | ||
84 | .syntax() | ||
85 | .descendants_with_tokens() | ||
86 | .find(|t| t.kind() == T!['{'])? | ||
87 | .text_range() | ||
88 | .end(); | ||
89 | |||
90 | Some(start) | ||
91 | }) | ||
92 | .unwrap_or_else(|| { | ||
93 | buf = generate_impl_text(&parent_enum, &buf); | ||
94 | parent_enum.syntax().text_range().end() | ||
95 | }); | ||
96 | |||
97 | builder.insert(start_offset, buf); | ||
98 | }, | ||
99 | ) | ||
100 | } | ||
101 | |||
102 | // Generates the surrounding `impl Type { <code> }` including type and lifetime | ||
103 | // parameters | ||
104 | fn generate_impl_text(strukt: &ast::Enum, code: &str) -> String { | ||
105 | let mut buf = String::with_capacity(code.len()); | ||
106 | buf.push_str("\n\nimpl "); | ||
107 | buf.push_str(strukt.name().unwrap().text()); | ||
108 | format_to!(buf, " {{\n{}\n}}", code); | ||
109 | buf | ||
110 | } | ||
111 | |||
112 | #[cfg(test)] | ||
113 | mod tests { | ||
114 | use test_utils::mark; | ||
115 | |||
116 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
117 | |||
118 | use super::*; | ||
119 | |||
120 | fn check_not_applicable(ra_fixture: &str) { | ||
121 | check_assist_not_applicable(generate_enum_match_method, ra_fixture) | ||
122 | } | ||
123 | |||
124 | #[test] | ||
125 | fn test_generate_enum_match_from_variant() { | ||
126 | check_assist( | ||
127 | generate_enum_match_method, | ||
128 | r#" | ||
129 | enum Variant { | ||
130 | Undefined, | ||
131 | Minor$0, | ||
132 | Major, | ||
133 | }"#, | ||
134 | r#"enum Variant { | ||
135 | Undefined, | ||
136 | Minor, | ||
137 | Major, | ||
138 | } | ||
139 | |||
140 | impl Variant { | ||
141 | /// Returns `true` if the variant is [`Minor`]. | ||
142 | fn is_minor(&self) -> bool { | ||
143 | matches!(self, Self::Minor) | ||
144 | } | ||
145 | }"#, | ||
146 | ); | ||
147 | } | ||
148 | |||
149 | #[test] | ||
150 | fn test_generate_enum_match_already_implemented() { | ||
151 | check_not_applicable( | ||
152 | r#" | ||
153 | enum Variant { | ||
154 | Undefined, | ||
155 | Minor$0, | ||
156 | Major, | ||
157 | } | ||
158 | |||
159 | impl Variant { | ||
160 | fn is_minor(&self) -> bool { | ||
161 | matches!(self, Self::Minor) | ||
162 | } | ||
163 | }"#, | ||
164 | ); | ||
165 | } | ||
166 | |||
167 | #[test] | ||
168 | fn test_add_from_impl_no_element() { | ||
169 | mark::check!(test_gen_enum_match_on_non_unit_variant_not_implemented); | ||
170 | check_not_applicable( | ||
171 | r#" | ||
172 | enum Variant { | ||
173 | Undefined, | ||
174 | Minor(u32)$0, | ||
175 | Major, | ||
176 | }"#, | ||
177 | ); | ||
178 | } | ||
179 | |||
180 | #[test] | ||
181 | fn test_generate_enum_match_from_variant_with_one_variant() { | ||
182 | check_assist( | ||
183 | generate_enum_match_method, | ||
184 | r#"enum Variant { Undefi$0ned }"#, | ||
185 | r#" | ||
186 | enum Variant { Undefined } | ||
187 | |||
188 | impl Variant { | ||
189 | /// Returns `true` if the variant is [`Undefined`]. | ||
190 | fn is_undefined(&self) -> bool { | ||
191 | matches!(self, Self::Undefined) | ||
192 | } | ||
193 | }"#, | ||
194 | ); | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn test_generate_enum_match_from_variant_with_visibility_marker() { | ||
199 | check_assist( | ||
200 | generate_enum_match_method, | ||
201 | r#" | ||
202 | pub(crate) enum Variant { | ||
203 | Undefined, | ||
204 | Minor$0, | ||
205 | Major, | ||
206 | }"#, | ||
207 | r#"pub(crate) enum Variant { | ||
208 | Undefined, | ||
209 | Minor, | ||
210 | Major, | ||
211 | } | ||
212 | |||
213 | impl Variant { | ||
214 | /// Returns `true` if the variant is [`Minor`]. | ||
215 | pub(crate) fn is_minor(&self) -> bool { | ||
216 | matches!(self, Self::Minor) | ||
217 | } | ||
218 | }"#, | ||
219 | ); | ||
220 | } | ||
221 | } | ||
diff --git a/crates/assists/src/handlers/generate_new.rs b/crates/assists/src/handlers/generate_new.rs index b7390855a..84832273f 100644 --- a/crates/assists/src/handlers/generate_new.rs +++ b/crates/assists/src/handlers/generate_new.rs | |||
@@ -1,4 +1,3 @@ | |||
1 | use hir::Adt; | ||
2 | use itertools::Itertools; | 1 | use itertools::Itertools; |
3 | use stdx::format_to; | 2 | use stdx::format_to; |
4 | use syntax::{ | 3 | use syntax::{ |
@@ -6,7 +5,7 @@ use syntax::{ | |||
6 | SmolStr, T, | 5 | SmolStr, T, |
7 | }; | 6 | }; |
8 | 7 | ||
9 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 8 | use crate::{utils::find_struct_impl, AssistContext, AssistId, AssistKind, Assists}; |
10 | 9 | ||
11 | // Assist: generate_new | 10 | // Assist: generate_new |
12 | // | 11 | // |
@@ -38,7 +37,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
38 | }; | 37 | }; |
39 | 38 | ||
40 | // Return early if we've found an existing new fn | 39 | // Return early if we've found an existing new fn |
41 | let impl_def = find_struct_impl(&ctx, &strukt)?; | 40 | let impl_def = find_struct_impl(&ctx, &ast::AdtDef::Struct(strukt.clone()), "new")?; |
42 | 41 | ||
43 | let target = strukt.syntax().text_range(); | 42 | let target = strukt.syntax().text_range(); |
44 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { | 43 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { |
@@ -111,65 +110,6 @@ fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String { | |||
111 | buf | 110 | buf |
112 | } | 111 | } |
113 | 112 | ||
114 | // Uses a syntax-driven approach to find any impl blocks for the struct that | ||
115 | // exist within the module/file | ||
116 | // | ||
117 | // Returns `None` if we've found an existing `new` fn | ||
118 | // | ||
119 | // FIXME: change the new fn checking to a more semantic approach when that's more | ||
120 | // viable (e.g. we process proc macros, etc) | ||
121 | fn find_struct_impl(ctx: &AssistContext, strukt: &ast::Struct) -> Option<Option<ast::Impl>> { | ||
122 | let db = ctx.db(); | ||
123 | let module = strukt.syntax().ancestors().find(|node| { | ||
124 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) | ||
125 | })?; | ||
126 | |||
127 | let struct_def = ctx.sema.to_def(strukt)?; | ||
128 | |||
129 | let block = module.descendants().filter_map(ast::Impl::cast).find_map(|impl_blk| { | ||
130 | let blk = ctx.sema.to_def(&impl_blk)?; | ||
131 | |||
132 | // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}` | ||
133 | // (we currently use the wrong type parameter) | ||
134 | // also we wouldn't want to use e.g. `impl S<u32>` | ||
135 | let same_ty = match blk.target_ty(db).as_adt() { | ||
136 | Some(def) => def == Adt::Struct(struct_def), | ||
137 | None => false, | ||
138 | }; | ||
139 | let not_trait_impl = blk.target_trait(db).is_none(); | ||
140 | |||
141 | if !(same_ty && not_trait_impl) { | ||
142 | None | ||
143 | } else { | ||
144 | Some(impl_blk) | ||
145 | } | ||
146 | }); | ||
147 | |||
148 | if let Some(ref impl_blk) = block { | ||
149 | if has_new_fn(impl_blk) { | ||
150 | return None; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | Some(block) | ||
155 | } | ||
156 | |||
157 | fn has_new_fn(imp: &ast::Impl) -> bool { | ||
158 | if let Some(il) = imp.assoc_item_list() { | ||
159 | for item in il.assoc_items() { | ||
160 | if let ast::AssocItem::Fn(f) = item { | ||
161 | if let Some(name) = f.name() { | ||
162 | if name.text().eq_ignore_ascii_case("new") { | ||
163 | return true; | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | |||
170 | false | ||
171 | } | ||
172 | |||
173 | #[cfg(test)] | 113 | #[cfg(test)] |
174 | mod tests { | 114 | mod tests { |
175 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 115 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 559b9651e..83fbf6986 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -117,6 +117,7 @@ mod handlers { | |||
117 | mod convert_integer_literal; | 117 | mod convert_integer_literal; |
118 | mod early_return; | 118 | mod early_return; |
119 | mod expand_glob_import; | 119 | mod expand_glob_import; |
120 | mod extract_function; | ||
120 | mod extract_struct_from_enum_variant; | 121 | mod extract_struct_from_enum_variant; |
121 | mod extract_variable; | 122 | mod extract_variable; |
122 | mod fill_match_arms; | 123 | mod fill_match_arms; |
@@ -126,6 +127,7 @@ mod handlers { | |||
126 | mod flip_trait_bound; | 127 | mod flip_trait_bound; |
127 | mod generate_default_from_enum_variant; | 128 | mod generate_default_from_enum_variant; |
128 | mod generate_derive; | 129 | mod generate_derive; |
130 | mod generate_enum_match_method; | ||
129 | mod generate_from_impl_for_enum; | 131 | mod generate_from_impl_for_enum; |
130 | mod generate_function; | 132 | mod generate_function; |
131 | mod generate_impl; | 133 | mod generate_impl; |
@@ -174,6 +176,7 @@ mod handlers { | |||
174 | early_return::convert_to_guarded_return, | 176 | early_return::convert_to_guarded_return, |
175 | expand_glob_import::expand_glob_import, | 177 | expand_glob_import::expand_glob_import, |
176 | move_module_to_file::move_module_to_file, | 178 | move_module_to_file::move_module_to_file, |
179 | extract_function::extract_function, | ||
177 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, | 180 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, |
178 | extract_variable::extract_variable, | 181 | extract_variable::extract_variable, |
179 | fill_match_arms::fill_match_arms, | 182 | fill_match_arms::fill_match_arms, |
@@ -183,6 +186,7 @@ mod handlers { | |||
183 | flip_trait_bound::flip_trait_bound, | 186 | flip_trait_bound::flip_trait_bound, |
184 | generate_default_from_enum_variant::generate_default_from_enum_variant, | 187 | generate_default_from_enum_variant::generate_default_from_enum_variant, |
185 | generate_derive::generate_derive, | 188 | generate_derive::generate_derive, |
189 | generate_enum_match_method::generate_enum_match_method, | ||
186 | generate_from_impl_for_enum::generate_from_impl_for_enum, | 190 | generate_from_impl_for_enum::generate_from_impl_for_enum, |
187 | generate_function::generate_function, | 191 | generate_function::generate_function, |
188 | generate_impl::generate_impl, | 192 | generate_impl::generate_impl, |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index 9aa807f10..0dbb05f2a 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -257,6 +257,33 @@ fn qux(bar: Bar, baz: Baz) {} | |||
257 | } | 257 | } |
258 | 258 | ||
259 | #[test] | 259 | #[test] |
260 | fn doctest_extract_function() { | ||
261 | check_doc_test( | ||
262 | "extract_function", | ||
263 | r#####" | ||
264 | fn main() { | ||
265 | let n = 1; | ||
266 | $0let m = n + 2; | ||
267 | let k = m + n;$0 | ||
268 | let g = 3; | ||
269 | } | ||
270 | "#####, | ||
271 | r#####" | ||
272 | fn main() { | ||
273 | let n = 1; | ||
274 | fun_name(n); | ||
275 | let g = 3; | ||
276 | } | ||
277 | |||
278 | fn $0fun_name(n: i32) { | ||
279 | let m = n + 2; | ||
280 | let k = m + n; | ||
281 | } | ||
282 | "#####, | ||
283 | ) | ||
284 | } | ||
285 | |||
286 | #[test] | ||
260 | fn doctest_extract_struct_from_enum_variant() { | 287 | fn doctest_extract_struct_from_enum_variant() { |
261 | check_doc_test( | 288 | check_doc_test( |
262 | "extract_struct_from_enum_variant", | 289 | "extract_struct_from_enum_variant", |
@@ -433,6 +460,34 @@ struct Point { | |||
433 | } | 460 | } |
434 | 461 | ||
435 | #[test] | 462 | #[test] |
463 | fn doctest_generate_enum_match_method() { | ||
464 | check_doc_test( | ||
465 | "generate_enum_match_method", | ||
466 | r#####" | ||
467 | enum Version { | ||
468 | Undefined, | ||
469 | Minor$0, | ||
470 | Major, | ||
471 | } | ||
472 | "#####, | ||
473 | r#####" | ||
474 | enum Version { | ||
475 | Undefined, | ||
476 | Minor, | ||
477 | Major, | ||
478 | } | ||
479 | |||
480 | impl Version { | ||
481 | /// Returns `true` if the version is [`Minor`]. | ||
482 | fn is_minor(&self) -> bool { | ||
483 | matches!(self, Self::Minor) | ||
484 | } | ||
485 | } | ||
486 | "#####, | ||
487 | ) | ||
488 | } | ||
489 | |||
490 | #[test] | ||
436 | fn doctest_generate_from_impl_for_enum() { | 491 | fn doctest_generate_from_impl_for_enum() { |
437 | check_doc_test( | 492 | check_doc_test( |
438 | "generate_from_impl_for_enum", | 493 | "generate_from_impl_for_enum", |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 4e762e18b..3842558d8 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::ops; | 3 | use std::ops; |
4 | 4 | ||
5 | use hir::HasSource; | 5 | use hir::{Adt, HasSource}; |
6 | use ide_db::{helpers::SnippetCap, RootDatabase}; | 6 | use ide_db::{helpers::SnippetCap, RootDatabase}; |
7 | use itertools::Itertools; | 7 | use itertools::Itertools; |
8 | use syntax::{ | 8 | use syntax::{ |
@@ -15,7 +15,10 @@ use syntax::{ | |||
15 | SyntaxNode, TextSize, T, | 15 | SyntaxNode, TextSize, T, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | use crate::ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}; | 18 | use crate::{ |
19 | assist_context::AssistContext, | ||
20 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | ||
21 | }; | ||
19 | 22 | ||
20 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { | 23 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { |
21 | extract_trivial_expression(&block) | 24 | extract_trivial_expression(&block) |
@@ -267,3 +270,71 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool { | |||
267 | 270 | ||
268 | pat_head == var_head | 271 | pat_head == var_head |
269 | } | 272 | } |
273 | |||
274 | // Uses a syntax-driven approach to find any impl blocks for the struct that | ||
275 | // exist within the module/file | ||
276 | // | ||
277 | // Returns `None` if we've found an existing `new` fn | ||
278 | // | ||
279 | // FIXME: change the new fn checking to a more semantic approach when that's more | ||
280 | // viable (e.g. we process proc macros, etc) | ||
281 | pub(crate) fn find_struct_impl( | ||
282 | ctx: &AssistContext, | ||
283 | strukt: &ast::AdtDef, | ||
284 | name: &str, | ||
285 | ) -> Option<Option<ast::Impl>> { | ||
286 | let db = ctx.db(); | ||
287 | let module = strukt.syntax().ancestors().find(|node| { | ||
288 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) | ||
289 | })?; | ||
290 | |||
291 | let struct_def = match strukt { | ||
292 | ast::AdtDef::Enum(e) => Adt::Enum(ctx.sema.to_def(e)?), | ||
293 | ast::AdtDef::Struct(s) => Adt::Struct(ctx.sema.to_def(s)?), | ||
294 | ast::AdtDef::Union(u) => Adt::Union(ctx.sema.to_def(u)?), | ||
295 | }; | ||
296 | |||
297 | let block = module.descendants().filter_map(ast::Impl::cast).find_map(|impl_blk| { | ||
298 | let blk = ctx.sema.to_def(&impl_blk)?; | ||
299 | |||
300 | // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}` | ||
301 | // (we currently use the wrong type parameter) | ||
302 | // also we wouldn't want to use e.g. `impl S<u32>` | ||
303 | |||
304 | let same_ty = match blk.target_ty(db).as_adt() { | ||
305 | Some(def) => def == struct_def, | ||
306 | None => false, | ||
307 | }; | ||
308 | let not_trait_impl = blk.target_trait(db).is_none(); | ||
309 | |||
310 | if !(same_ty && not_trait_impl) { | ||
311 | None | ||
312 | } else { | ||
313 | Some(impl_blk) | ||
314 | } | ||
315 | }); | ||
316 | |||
317 | if let Some(ref impl_blk) = block { | ||
318 | if has_fn(impl_blk, name) { | ||
319 | return None; | ||
320 | } | ||
321 | } | ||
322 | |||
323 | Some(block) | ||
324 | } | ||
325 | |||
326 | fn has_fn(imp: &ast::Impl, rhs_name: &str) -> bool { | ||
327 | if let Some(il) = imp.assoc_item_list() { | ||
328 | for item in il.assoc_items() { | ||
329 | if let ast::AssocItem::Fn(f) = item { | ||
330 | if let Some(name) = f.name() { | ||
331 | if name.text().eq_ignore_ascii_case(rhs_name) { | ||
332 | return true; | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | } | ||
337 | } | ||
338 | |||
339 | false | ||
340 | } | ||
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs index 9c6a5a40c..c9f928483 100644 --- a/crates/completion/src/completions/flyimport.rs +++ b/crates/completion/src/completions/flyimport.rs | |||
@@ -175,7 +175,7 @@ fn compute_fuzzy_completion_order_key( | |||
175 | user_input_lowercased: &str, | 175 | user_input_lowercased: &str, |
176 | ) -> usize { | 176 | ) -> usize { |
177 | mark::hit!(certain_fuzzy_order_test); | 177 | mark::hit!(certain_fuzzy_order_test); |
178 | let proposed_import_name = match proposed_mod_path.segments.last() { | 178 | let proposed_import_name = match proposed_mod_path.segments().last() { |
179 | Some(name) => name.to_string().to_lowercase(), | 179 | Some(name) => name.to_string().to_lowercase(), |
180 | None => return usize::MAX, | 180 | None => return usize::MAX, |
181 | }; | 181 | }; |
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 5d62fab97..e2482f959 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -63,7 +63,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T | |||
63 | if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { | 63 | if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { |
64 | // Variants with trivial paths are already added by the existing completion logic, | 64 | // Variants with trivial paths are already added by the existing completion logic, |
65 | // so we should avoid adding these twice | 65 | // so we should avoid adding these twice |
66 | if path.segments.len() > 1 { | 66 | if path.segments().len() > 1 { |
67 | acc.add_qualified_enum_variant(ctx, variant, path); | 67 | acc.add_qualified_enum_variant(ctx, variant, path); |
68 | } | 68 | } |
69 | } | 69 | } |
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 8ec4ac65e..884711f11 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs | |||
@@ -332,9 +332,9 @@ impl Builder { | |||
332 | label = format!("{} ({})", label, import_to_add.import_path); | 332 | label = format!("{} ({})", label, import_to_add.import_path); |
333 | } else { | 333 | } else { |
334 | let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); | 334 | let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); |
335 | let _ = import_path_without_last_segment.segments.pop(); | 335 | let _ = import_path_without_last_segment.pop_segment(); |
336 | 336 | ||
337 | if !import_path_without_last_segment.segments.is_empty() { | 337 | if !import_path_without_last_segment.segments().is_empty() { |
338 | lookup = lookup.or_else(|| Some(label.clone())); | 338 | lookup = lookup.or_else(|| Some(label.clone())); |
339 | insert_text = insert_text.or_else(|| Some(label.clone())); | 339 | insert_text = insert_text.or_else(|| Some(label.clone())); |
340 | label = format!("{}::{}", import_path_without_last_segment, label); | 340 | label = format!("{}::{}", import_path_without_last_segment, label); |
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index e11b881ca..eddaaa6f3 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs | |||
@@ -57,7 +57,7 @@ pub(crate) fn render_resolution_with_import<'a>( | |||
57 | ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), | 57 | ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), |
58 | ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), | 58 | ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), |
59 | ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), | 59 | ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), |
60 | _ => import_edit.import_path.segments.last()?.to_string(), | 60 | _ => import_edit.import_path.segments().last()?.to_string(), |
61 | }; | 61 | }; |
62 | Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { | 62 | Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { |
63 | item.completion_kind = CompletionKind::Magic; | 63 | item.completion_kind = CompletionKind::Magic; |
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index adcddebd1..9214193b4 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs | |||
@@ -45,8 +45,8 @@ impl<'a> EnumRender<'a> { | |||
45 | let (qualified_name, short_qualified_name) = match &path { | 45 | let (qualified_name, short_qualified_name) = match &path { |
46 | Some(path) => { | 46 | Some(path) => { |
47 | let full = path.to_string(); | 47 | let full = path.to_string(); |
48 | let short = | 48 | let segments = path.segments(); |
49 | path.segments[path.segments.len().saturating_sub(2)..].iter().join("::"); | 49 | let short = segments[segments.len().saturating_sub(2)..].iter().join("::"); |
50 | (full, short) | 50 | (full, short) |
51 | } | 51 | } |
52 | None => (name.to_string(), name.to_string()), | 52 | None => (name.to_string(), name.to_string()), |
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs index 06f0b9b18..ed36c3109 100644 --- a/crates/hir_def/src/adt.rs +++ b/crates/hir_def/src/adt.rs | |||
@@ -351,7 +351,7 @@ fn lower_field( | |||
351 | ) -> FieldData { | 351 | ) -> FieldData { |
352 | FieldData { | 352 | FieldData { |
353 | name: field.name.clone(), | 353 | name: field.name.clone(), |
354 | type_ref: field.type_ref.clone(), | 354 | type_ref: item_tree[field.type_ref].clone(), |
355 | visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(), | 355 | visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(), |
356 | } | 356 | } |
357 | } | 357 | } |
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index b9ecf22fa..9a432f7d1 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -33,7 +33,7 @@ use crate::{ | |||
33 | nameres::DefMap, | 33 | nameres::DefMap, |
34 | path::{ModPath, Path}, | 34 | path::{ModPath, Path}, |
35 | src::HasSource, | 35 | src::HasSource, |
36 | AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, | 36 | AsMacroCall, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId, |
37 | }; | 37 | }; |
38 | 38 | ||
39 | /// A subset of Expander that only deals with cfg attributes. We only need it to | 39 | /// A subset of Expander that only deals with cfg attributes. We only need it to |
@@ -46,10 +46,10 @@ pub(crate) struct CfgExpander { | |||
46 | 46 | ||
47 | pub(crate) struct Expander { | 47 | pub(crate) struct Expander { |
48 | cfg_expander: CfgExpander, | 48 | cfg_expander: CfgExpander, |
49 | crate_def_map: Arc<DefMap>, | 49 | def_map: Arc<DefMap>, |
50 | current_file_id: HirFileId, | 50 | current_file_id: HirFileId, |
51 | ast_id_map: Arc<AstIdMap>, | 51 | ast_id_map: Arc<AstIdMap>, |
52 | module: ModuleId, | 52 | module: LocalModuleId, |
53 | recursion_limit: usize, | 53 | recursion_limit: usize, |
54 | } | 54 | } |
55 | 55 | ||
@@ -91,10 +91,10 @@ impl Expander { | |||
91 | let ast_id_map = db.ast_id_map(current_file_id); | 91 | let ast_id_map = db.ast_id_map(current_file_id); |
92 | Expander { | 92 | Expander { |
93 | cfg_expander, | 93 | cfg_expander, |
94 | crate_def_map, | 94 | def_map: crate_def_map, |
95 | current_file_id, | 95 | current_file_id, |
96 | ast_id_map, | 96 | ast_id_map, |
97 | module, | 97 | module: module.local_id, |
98 | recursion_limit: 0, | 98 | recursion_limit: 0, |
99 | } | 99 | } |
100 | } | 100 | } |
@@ -102,7 +102,6 @@ impl Expander { | |||
102 | pub(crate) fn enter_expand<T: ast::AstNode>( | 102 | pub(crate) fn enter_expand<T: ast::AstNode>( |
103 | &mut self, | 103 | &mut self, |
104 | db: &dyn DefDatabase, | 104 | db: &dyn DefDatabase, |
105 | local_scope: Option<&ItemScope>, | ||
106 | macro_call: ast::MacroCall, | 105 | macro_call: ast::MacroCall, |
107 | ) -> ExpandResult<Option<(Mark, T)>> { | 106 | ) -> ExpandResult<Option<(Mark, T)>> { |
108 | if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { | 107 | if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { |
@@ -112,18 +111,12 @@ impl Expander { | |||
112 | 111 | ||
113 | let macro_call = InFile::new(self.current_file_id, ¯o_call); | 112 | let macro_call = InFile::new(self.current_file_id, ¯o_call); |
114 | 113 | ||
115 | let resolver = |path: ModPath| -> Option<MacroDefId> { | 114 | let resolver = |
116 | if let Some(local_scope) = local_scope { | 115 | |path: ModPath| -> Option<MacroDefId> { self.resolve_path_as_macro(db, &path) }; |
117 | if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { | ||
118 | return Some(def); | ||
119 | } | ||
120 | } | ||
121 | self.resolve_path_as_macro(db, &path) | ||
122 | }; | ||
123 | 116 | ||
124 | let mut err = None; | 117 | let mut err = None; |
125 | let call_id = | 118 | let call_id = |
126 | macro_call.as_call_id_with_errors(db, self.crate_def_map.krate(), resolver, &mut |e| { | 119 | macro_call.as_call_id_with_errors(db, self.def_map.krate(), resolver, &mut |e| { |
127 | err.get_or_insert(e); | 120 | err.get_or_insert(e); |
128 | }); | 121 | }); |
129 | let call_id = match call_id { | 122 | let call_id = match call_id { |
@@ -204,10 +197,7 @@ impl Expander { | |||
204 | } | 197 | } |
205 | 198 | ||
206 | fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> { | 199 | fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> { |
207 | self.crate_def_map | 200 | self.def_map.resolve_path(db, self.module, path, BuiltinShadowMode::Other).0.take_macros() |
208 | .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other) | ||
209 | .0 | ||
210 | .take_macros() | ||
211 | } | 201 | } |
212 | 202 | ||
213 | fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> { | 203 | fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> { |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 209965fca..28b11cdde 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` | 1 | //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` |
2 | //! representation. | 2 | //! representation. |
3 | 3 | ||
4 | use std::{any::type_name, sync::Arc}; | 4 | use std::{any::type_name, mem, sync::Arc}; |
5 | 5 | ||
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{ | 7 | use hir_expand::{ |
@@ -36,8 +36,8 @@ use crate::{ | |||
36 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, | 36 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, |
37 | path::{GenericArgs, Path}, | 37 | path::{GenericArgs, Path}, |
38 | type_ref::{Mutability, Rawness, TypeRef}, | 38 | type_ref::{Mutability, Rawness, TypeRef}, |
39 | AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, | 39 | AdtId, BlockLoc, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, |
40 | StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, | 40 | ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, |
41 | }; | 41 | }; |
42 | 42 | ||
43 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; | 43 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; |
@@ -152,8 +152,8 @@ impl ExprCollector<'_> { | |||
152 | fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { | 152 | fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { |
153 | self.make_expr(expr, Err(SyntheticSyntax)) | 153 | self.make_expr(expr, Err(SyntheticSyntax)) |
154 | } | 154 | } |
155 | fn empty_block(&mut self) -> ExprId { | 155 | fn unit(&mut self) -> ExprId { |
156 | self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None, label: None }) | 156 | self.alloc_expr_desugared(Expr::Tuple { exprs: Vec::new() }) |
157 | } | 157 | } |
158 | fn missing_expr(&mut self) -> ExprId { | 158 | fn missing_expr(&mut self) -> ExprId { |
159 | self.alloc_expr_desugared(Expr::Missing) | 159 | self.alloc_expr_desugared(Expr::Missing) |
@@ -222,7 +222,7 @@ impl ExprCollector<'_> { | |||
222 | MatchArm { pat, expr: then_branch, guard: None }, | 222 | MatchArm { pat, expr: then_branch, guard: None }, |
223 | MatchArm { | 223 | MatchArm { |
224 | pat: placeholder_pat, | 224 | pat: placeholder_pat, |
225 | expr: else_branch.unwrap_or_else(|| self.empty_block()), | 225 | expr: else_branch.unwrap_or_else(|| self.unit()), |
226 | guard: None, | 226 | guard: None, |
227 | }, | 227 | }, |
228 | ]; | 228 | ]; |
@@ -561,7 +561,7 @@ impl ExprCollector<'_> { | |||
561 | let outer_file = self.expander.current_file_id; | 561 | let outer_file = self.expander.current_file_id; |
562 | 562 | ||
563 | let macro_call = self.expander.to_source(AstPtr::new(&e)); | 563 | let macro_call = self.expander.to_source(AstPtr::new(&e)); |
564 | let res = self.expander.enter_expand(self.db, Some(&self.body.item_scope), e); | 564 | let res = self.expander.enter_expand(self.db, e); |
565 | 565 | ||
566 | match &res.err { | 566 | match &res.err { |
567 | Some(ExpandError::UnresolvedProcMacro) => { | 567 | Some(ExpandError::UnresolvedProcMacro) => { |
@@ -697,12 +697,30 @@ impl ExprCollector<'_> { | |||
697 | } | 697 | } |
698 | 698 | ||
699 | fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { | 699 | fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { |
700 | let syntax_node_ptr = AstPtr::new(&block.clone().into()); | 700 | let ast_id = self.expander.ast_id(&block); |
701 | let block_loc = | ||
702 | BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) }; | ||
703 | let block_id = self.db.intern_block(block_loc); | ||
704 | let opt_def_map = self.db.block_def_map(block_id); | ||
705 | let has_def_map = opt_def_map.is_some(); | ||
706 | let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone()); | ||
707 | let module = if has_def_map { def_map.root() } else { self.expander.module }; | ||
708 | let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); | ||
709 | let prev_local_module = mem::replace(&mut self.expander.module, module); | ||
710 | |||
701 | self.collect_stmts_items(block.statements()); | 711 | self.collect_stmts_items(block.statements()); |
702 | let statements = | 712 | let statements = |
703 | block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); | 713 | block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); |
704 | let tail = block.tail_expr().map(|e| self.collect_expr(e)); | 714 | let tail = block.tail_expr().map(|e| self.collect_expr(e)); |
705 | self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr) | 715 | let syntax_node_ptr = AstPtr::new(&block.clone().into()); |
716 | let expr_id = self.alloc_expr( | ||
717 | Expr::Block { id: block_id, statements, tail, label: None }, | ||
718 | syntax_node_ptr, | ||
719 | ); | ||
720 | |||
721 | self.expander.def_map = prev_def_map; | ||
722 | self.expander.module = prev_local_module; | ||
723 | expr_id | ||
706 | } | 724 | } |
707 | 725 | ||
708 | fn collect_stmts_items(&mut self, stmts: ast::AstChildren<ast::Stmt>) { | 726 | fn collect_stmts_items(&mut self, stmts: ast::AstChildren<ast::Stmt>) { |
@@ -794,7 +812,7 @@ impl ExprCollector<'_> { | |||
794 | } | 812 | } |
795 | Either::Right(e) => { | 813 | Either::Right(e) => { |
796 | let mac = MacroDefId { | 814 | let mac = MacroDefId { |
797 | krate: self.expander.module.krate, | 815 | krate: self.expander.def_map.krate(), |
798 | ast_id: Some(self.expander.ast_id(&e)), | 816 | ast_id: Some(self.expander.ast_id(&e)), |
799 | kind: MacroDefKind::Declarative, | 817 | kind: MacroDefKind::Declarative, |
800 | local_inner: false, | 818 | local_inner: false, |
@@ -832,9 +850,9 @@ impl ExprCollector<'_> { | |||
832 | if annotation == BindingAnnotation::Unannotated && subpat.is_none() { | 850 | if annotation == BindingAnnotation::Unannotated && subpat.is_none() { |
833 | // This could also be a single-segment path pattern. To | 851 | // This could also be a single-segment path pattern. To |
834 | // decide that, we need to try resolving the name. | 852 | // decide that, we need to try resolving the name. |
835 | let (resolved, _) = self.expander.crate_def_map.resolve_path( | 853 | let (resolved, _) = self.expander.def_map.resolve_path( |
836 | self.db, | 854 | self.db, |
837 | self.expander.module.local_id, | 855 | self.expander.module, |
838 | &name.clone().into(), | 856 | &name.clone().into(), |
839 | BuiltinShadowMode::Other, | 857 | BuiltinShadowMode::Other, |
840 | ); | 858 | ); |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index 2e5d0a01e..a92134ba7 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | use base_db::{fixture::WithFixture, SourceDatabase}; | 1 | mod block; |
2 | |||
3 | use base_db::{fixture::WithFixture, FilePosition, SourceDatabase}; | ||
4 | use expect_test::Expect; | ||
2 | use test_utils::mark; | 5 | use test_utils::mark; |
3 | 6 | ||
4 | use crate::{test_db::TestDB, ModuleDefId}; | 7 | use crate::{test_db::TestDB, BlockId, ModuleDefId}; |
5 | 8 | ||
6 | use super::*; | 9 | use super::*; |
7 | 10 | ||
@@ -31,6 +34,114 @@ fn check_diagnostics(ra_fixture: &str) { | |||
31 | db.check_diagnostics(); | 34 | db.check_diagnostics(); |
32 | } | 35 | } |
33 | 36 | ||
37 | fn block_def_map_at(ra_fixture: &str) -> String { | ||
38 | let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); | ||
39 | |||
40 | let krate = db.crate_graph().iter().next().unwrap(); | ||
41 | let def_map = db.crate_def_map(krate); | ||
42 | |||
43 | let mut block = | ||
44 | block_at_pos(&db, &def_map, position).expect("couldn't find enclosing function or block"); | ||
45 | loop { | ||
46 | let def_map = db.block_def_map(block).unwrap_or_else(|| def_map.clone()); | ||
47 | let new_block = block_at_pos(&db, &def_map, position); | ||
48 | match new_block { | ||
49 | Some(new_block) => { | ||
50 | assert_ne!(block, new_block); | ||
51 | block = new_block; | ||
52 | } | ||
53 | None => { | ||
54 | return def_map.dump(&db); | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
60 | fn block_at_pos(db: &dyn DefDatabase, def_map: &DefMap, position: FilePosition) -> Option<BlockId> { | ||
61 | // Find the smallest (innermost) function containing the cursor. | ||
62 | let mut size = None; | ||
63 | let mut fn_def = None; | ||
64 | for (_, module) in def_map.modules() { | ||
65 | let file_id = module.definition_source(db).file_id; | ||
66 | if file_id != position.file_id.into() { | ||
67 | continue; | ||
68 | } | ||
69 | let root = db.parse_or_expand(file_id).unwrap(); | ||
70 | let ast_map = db.ast_id_map(file_id); | ||
71 | let item_tree = db.item_tree(file_id); | ||
72 | for decl in module.scope.declarations() { | ||
73 | if let ModuleDefId::FunctionId(it) = decl { | ||
74 | let ast = ast_map.get(item_tree[it.lookup(db).id.value].ast_id).to_node(&root); | ||
75 | let range = ast.syntax().text_range(); | ||
76 | |||
77 | if !range.contains(position.offset) { | ||
78 | continue; | ||
79 | } | ||
80 | |||
81 | let new_size = match size { | ||
82 | None => range.len(), | ||
83 | Some(size) => { | ||
84 | if range.len() < size { | ||
85 | range.len() | ||
86 | } else { | ||
87 | size | ||
88 | } | ||
89 | } | ||
90 | }; | ||
91 | if size != Some(new_size) { | ||
92 | size = Some(new_size); | ||
93 | fn_def = Some(it); | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | let (body, source_map) = db.body_with_source_map(fn_def?.into()); | ||
100 | |||
101 | // Now find the smallest encompassing block expression in the function body. | ||
102 | let mut size = None; | ||
103 | let mut block_id = None; | ||
104 | for (expr_id, expr) in body.exprs.iter() { | ||
105 | if let Expr::Block { id, .. } = expr { | ||
106 | if let Ok(ast) = source_map.expr_syntax(expr_id) { | ||
107 | if ast.file_id != position.file_id.into() { | ||
108 | continue; | ||
109 | } | ||
110 | |||
111 | let root = db.parse_or_expand(ast.file_id).unwrap(); | ||
112 | let ast = ast.value.to_node(&root); | ||
113 | let range = ast.syntax().text_range(); | ||
114 | |||
115 | if !range.contains(position.offset) { | ||
116 | continue; | ||
117 | } | ||
118 | |||
119 | let new_size = match size { | ||
120 | None => range.len(), | ||
121 | Some(size) => { | ||
122 | if range.len() < size { | ||
123 | range.len() | ||
124 | } else { | ||
125 | size | ||
126 | } | ||
127 | } | ||
128 | }; | ||
129 | if size != Some(new_size) { | ||
130 | size = Some(new_size); | ||
131 | block_id = Some(*id); | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | Some(block_id.expect("can't find block containing cursor")) | ||
138 | } | ||
139 | |||
140 | fn check_at(ra_fixture: &str, expect: Expect) { | ||
141 | let actual = block_def_map_at(ra_fixture); | ||
142 | expect.assert_eq(&actual); | ||
143 | } | ||
144 | |||
34 | #[test] | 145 | #[test] |
35 | fn your_stack_belongs_to_me() { | 146 | fn your_stack_belongs_to_me() { |
36 | mark::check!(your_stack_belongs_to_me); | 147 | mark::check!(your_stack_belongs_to_me); |
diff --git a/crates/hir_def/src/nameres/tests/block.rs b/crates/hir_def/src/body/tests/block.rs index 6cc659513..062560a70 100644 --- a/crates/hir_def/src/nameres/tests/block.rs +++ b/crates/hir_def/src/body/tests/block.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | use super::*; | 1 | use super::*; |
2 | use expect_test::expect; | ||
2 | 3 | ||
3 | #[test] | 4 | #[test] |
4 | fn inner_item_smoke() { | 5 | fn inner_item_smoke() { |
@@ -13,6 +14,7 @@ fn outer() { | |||
13 | expect![[r#" | 14 | expect![[r#" |
14 | block scope | 15 | block scope |
15 | inner: v | 16 | inner: v |
17 | |||
16 | crate | 18 | crate |
17 | inner: t | 19 | inner: t |
18 | outer: v | 20 | outer: v |
@@ -37,6 +39,7 @@ fn outer() { | |||
37 | CrateStruct: t v | 39 | CrateStruct: t v |
38 | SelfStruct: t v | 40 | SelfStruct: t v |
39 | Struct: t v | 41 | Struct: t v |
42 | |||
40 | crate | 43 | crate |
41 | Struct: t v | 44 | Struct: t v |
42 | outer: v | 45 | outer: v |
@@ -61,6 +64,7 @@ fn outer() { | |||
61 | block scope | 64 | block scope |
62 | imported: t v | 65 | imported: t v |
63 | name: v | 66 | name: v |
67 | |||
64 | crate | 68 | crate |
65 | name: t | 69 | name: t |
66 | outer: v | 70 | outer: v |
@@ -87,9 +91,11 @@ fn outer() { | |||
87 | inner1: t | 91 | inner1: t |
88 | inner2: v | 92 | inner2: v |
89 | outer: v | 93 | outer: v |
94 | |||
90 | block scope | 95 | block scope |
91 | inner: v | 96 | inner: v |
92 | inner1: t | 97 | inner1: t |
98 | |||
93 | crate | 99 | crate |
94 | outer: v | 100 | outer: v |
95 | "#]], | 101 | "#]], |
@@ -112,6 +118,7 @@ struct Struct {} | |||
112 | expect![[r#" | 118 | expect![[r#" |
113 | block scope | 119 | block scope |
114 | Struct: t | 120 | Struct: t |
121 | |||
115 | crate | 122 | crate |
116 | Struct: t | 123 | Struct: t |
117 | module: t | 124 | module: t |
@@ -142,6 +149,7 @@ fn f() { | |||
142 | expect![[r#" | 149 | expect![[r#" |
143 | block scope | 150 | block scope |
144 | Hit: t | 151 | Hit: t |
152 | |||
145 | crate | 153 | crate |
146 | f: v | 154 | f: v |
147 | "#]], | 155 | "#]], |
@@ -176,11 +184,47 @@ pub mod mark { | |||
176 | expect![[r#" | 184 | expect![[r#" |
177 | block scope | 185 | block scope |
178 | Hit: t | 186 | Hit: t |
187 | |||
179 | block scope | 188 | block scope |
180 | nested: v | 189 | nested: v |
190 | |||
181 | crate | 191 | crate |
182 | f: v | 192 | f: v |
183 | mark: t | 193 | mark: t |
184 | "#]], | 194 | "#]], |
185 | ); | 195 | ); |
186 | } | 196 | } |
197 | |||
198 | #[test] | ||
199 | fn macro_resolve_legacy() { | ||
200 | check_at( | ||
201 | r#" | ||
202 | //- /lib.rs | ||
203 | mod module; | ||
204 | |||
205 | //- /module.rs | ||
206 | macro_rules! m { | ||
207 | () => { | ||
208 | struct Def {} | ||
209 | }; | ||
210 | } | ||
211 | |||
212 | fn f() { | ||
213 | { | ||
214 | m!(); | ||
215 | $0 | ||
216 | } | ||
217 | } | ||
218 | "#, | ||
219 | expect![[r#" | ||
220 | block scope | ||
221 | Def: t | ||
222 | |||
223 | crate | ||
224 | module: t | ||
225 | |||
226 | crate::module | ||
227 | f: v | ||
228 | "#]], | ||
229 | ) | ||
230 | } | ||
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index e7b7724f7..42fcca386 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -41,8 +41,8 @@ impl FunctionData { | |||
41 | 41 | ||
42 | Arc::new(FunctionData { | 42 | Arc::new(FunctionData { |
43 | name: func.name.clone(), | 43 | name: func.name.clone(), |
44 | params: func.params.to_vec(), | 44 | params: func.params.iter().map(|id| item_tree[*id].clone()).collect(), |
45 | ret_type: func.ret_type.clone(), | 45 | ret_type: item_tree[func.ret_type].clone(), |
46 | attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()).clone(), | 46 | attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()).clone(), |
47 | has_self_param: func.has_self_param, | 47 | has_self_param: func.has_self_param, |
48 | has_body: func.has_body, | 48 | has_body: func.has_body, |
@@ -75,7 +75,7 @@ impl TypeAliasData { | |||
75 | 75 | ||
76 | Arc::new(TypeAliasData { | 76 | Arc::new(TypeAliasData { |
77 | name: typ.name.clone(), | 77 | name: typ.name.clone(), |
78 | type_ref: typ.type_ref.clone(), | 78 | type_ref: typ.type_ref.map(|id| item_tree[id].clone()), |
79 | visibility: item_tree[typ.visibility].clone(), | 79 | visibility: item_tree[typ.visibility].clone(), |
80 | is_extern: typ.is_extern, | 80 | is_extern: typ.is_extern, |
81 | bounds: typ.bounds.to_vec(), | 81 | bounds: typ.bounds.to_vec(), |
@@ -144,8 +144,8 @@ impl ImplData { | |||
144 | 144 | ||
145 | let item_tree = db.item_tree(impl_loc.id.file_id); | 145 | let item_tree = db.item_tree(impl_loc.id.file_id); |
146 | let impl_def = &item_tree[impl_loc.id.value]; | 146 | let impl_def = &item_tree[impl_loc.id.value]; |
147 | let target_trait = impl_def.target_trait.clone(); | 147 | let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); |
148 | let target_type = impl_def.target_type.clone(); | 148 | let target_type = item_tree[impl_def.target_type].clone(); |
149 | let is_negative = impl_def.is_negative; | 149 | let is_negative = impl_def.is_negative; |
150 | let module_id = impl_loc.container.module(db); | 150 | let module_id = impl_loc.container.module(db); |
151 | let container = AssocContainerId::ImplId(id); | 151 | let container = AssocContainerId::ImplId(id); |
@@ -182,7 +182,7 @@ impl ConstData { | |||
182 | 182 | ||
183 | Arc::new(ConstData { | 183 | Arc::new(ConstData { |
184 | name: konst.name.clone(), | 184 | name: konst.name.clone(), |
185 | type_ref: konst.type_ref.clone(), | 185 | type_ref: item_tree[konst.type_ref].clone(), |
186 | visibility: item_tree[konst.visibility].clone(), | 186 | visibility: item_tree[konst.visibility].clone(), |
187 | }) | 187 | }) |
188 | } | 188 | } |
@@ -205,7 +205,7 @@ impl StaticData { | |||
205 | 205 | ||
206 | Arc::new(StaticData { | 206 | Arc::new(StaticData { |
207 | name: Some(statik.name.clone()), | 207 | name: Some(statik.name.clone()), |
208 | type_ref: statik.type_ref.clone(), | 208 | type_ref: item_tree[statik.type_ref].clone(), |
209 | visibility: item_tree[statik.visibility].clone(), | 209 | visibility: item_tree[statik.visibility].clone(), |
210 | mutable: statik.mutable, | 210 | mutable: statik.mutable, |
211 | is_extern: statik.is_extern, | 211 | is_extern: statik.is_extern, |
@@ -262,7 +262,7 @@ fn collect_items( | |||
262 | let root = db.parse_or_expand(file_id).unwrap(); | 262 | let root = db.parse_or_expand(file_id).unwrap(); |
263 | let call = ast_id_map.get(call.ast_id).to_node(&root); | 263 | let call = ast_id_map.get(call.ast_id).to_node(&root); |
264 | 264 | ||
265 | if let Some((mark, mac)) = expander.enter_expand(db, None, call).value { | 265 | if let Some((mark, mac)) = expander.enter_expand(db, call).value { |
266 | let src: InFile<ast::MacroItems> = expander.to_source(mac); | 266 | let src: InFile<ast::MacroItems> = expander.to_source(mac); |
267 | let item_tree = db.item_tree(src.file_id); | 267 | let item_tree = db.item_tree(src.file_id); |
268 | let iter = | 268 | let iter = |
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index aef7e1f6c..6c01f1ed0 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -58,8 +58,23 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
58 | #[salsa::invoke(DefMap::crate_def_map_query)] | 58 | #[salsa::invoke(DefMap::crate_def_map_query)] |
59 | fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>; | 59 | fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>; |
60 | 60 | ||
61 | /// Computes the block-level `DefMap`, returning `None` when `block` doesn't contain any inner | ||
62 | /// items directly. | ||
63 | /// | ||
64 | /// For example: | ||
65 | /// | ||
66 | /// ``` | ||
67 | /// fn f() { // (0) | ||
68 | /// { // (1) | ||
69 | /// fn inner() {} | ||
70 | /// } | ||
71 | /// } | ||
72 | /// ``` | ||
73 | /// | ||
74 | /// The `block_def_map` for block 0 would return `None`, while `block_def_map` of block 1 would | ||
75 | /// return a `DefMap` containing `inner`. | ||
61 | #[salsa::invoke(DefMap::block_def_map_query)] | 76 | #[salsa::invoke(DefMap::block_def_map_query)] |
62 | fn block_def_map(&self, block: BlockId) -> Arc<DefMap>; | 77 | fn block_def_map(&self, block: BlockId) -> Option<Arc<DefMap>>; |
63 | 78 | ||
64 | #[salsa::invoke(StructData::struct_data_query)] | 79 | #[salsa::invoke(StructData::struct_data_query)] |
65 | fn struct_data(&self, id: StructId) -> Arc<StructData>; | 80 | fn struct_data(&self, id: StructId) -> Arc<StructData>; |
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs index 5be838f4a..4d72eaeaf 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs | |||
@@ -20,6 +20,7 @@ use crate::{ | |||
20 | builtin_type::{BuiltinFloat, BuiltinInt}, | 20 | builtin_type::{BuiltinFloat, BuiltinInt}, |
21 | path::{GenericArgs, Path}, | 21 | path::{GenericArgs, Path}, |
22 | type_ref::{Mutability, Rawness, TypeRef}, | 22 | type_ref::{Mutability, Rawness, TypeRef}, |
23 | BlockId, | ||
23 | }; | 24 | }; |
24 | 25 | ||
25 | pub type ExprId = Idx<Expr>; | 26 | pub type ExprId = Idx<Expr>; |
@@ -56,6 +57,7 @@ pub enum Expr { | |||
56 | else_branch: Option<ExprId>, | 57 | else_branch: Option<ExprId>, |
57 | }, | 58 | }, |
58 | Block { | 59 | Block { |
60 | id: BlockId, | ||
59 | statements: Vec<Statement>, | 61 | statements: Vec<Statement>, |
60 | tail: Option<ExprId>, | 62 | tail: Option<ExprId>, |
61 | label: Option<LabelId>, | 63 | label: Option<LabelId>, |
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index 94a1d567d..aa2c6e04e 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -36,13 +36,13 @@ const MAX_PATH_LEN: usize = 15; | |||
36 | 36 | ||
37 | impl ModPath { | 37 | impl ModPath { |
38 | fn starts_with_std(&self) -> bool { | 38 | fn starts_with_std(&self) -> bool { |
39 | self.segments.first() == Some(&known::std) | 39 | self.segments().first() == Some(&known::std) |
40 | } | 40 | } |
41 | 41 | ||
42 | // When std library is present, paths starting with `std::` | 42 | // When std library is present, paths starting with `std::` |
43 | // should be preferred over paths starting with `core::` and `alloc::` | 43 | // should be preferred over paths starting with `core::` and `alloc::` |
44 | fn can_start_with_std(&self) -> bool { | 44 | fn can_start_with_std(&self) -> bool { |
45 | let first_segment = self.segments.first(); | 45 | let first_segment = self.segments().first(); |
46 | first_segment == Some(&known::alloc) || first_segment == Some(&known::core) | 46 | first_segment == Some(&known::alloc) || first_segment == Some(&known::core) |
47 | } | 47 | } |
48 | } | 48 | } |
@@ -157,7 +157,7 @@ fn find_path_inner( | |||
157 | if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { | 157 | if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { |
158 | if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) { | 158 | if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) { |
159 | let data = db.enum_data(variant.parent); | 159 | let data = db.enum_data(variant.parent); |
160 | path.segments.push(data.variants[variant.local_id].name.clone()); | 160 | path.push_segment(data.variants[variant.local_id].name.clone()); |
161 | return Some(path); | 161 | return Some(path); |
162 | } | 162 | } |
163 | // If this doesn't work, it seems we have no way of referring to the | 163 | // If this doesn't work, it seems we have no way of referring to the |
@@ -186,7 +186,7 @@ fn find_path_inner( | |||
186 | best_path_len - 1, | 186 | best_path_len - 1, |
187 | prefixed, | 187 | prefixed, |
188 | ) { | 188 | ) { |
189 | path.segments.push(name); | 189 | path.push_segment(name); |
190 | 190 | ||
191 | let new_path = if let Some(best_path) = best_path { | 191 | let new_path = if let Some(best_path) = best_path { |
192 | select_best_path(best_path, path, prefer_no_std) | 192 | select_best_path(best_path, path, prefer_no_std) |
@@ -215,7 +215,7 @@ fn find_path_inner( | |||
215 | prefixed, | 215 | prefixed, |
216 | )?; | 216 | )?; |
217 | mark::hit!(partially_imported); | 217 | mark::hit!(partially_imported); |
218 | path.segments.push(info.path.segments.last().unwrap().clone()); | 218 | path.push_segment(info.path.segments.last().unwrap().clone()); |
219 | Some(path) | 219 | Some(path) |
220 | }) | 220 | }) |
221 | }); | 221 | }); |
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index 2750e1c91..ee46c3330 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs | |||
@@ -8,6 +8,7 @@ use hir_expand::name::Name; | |||
8 | use hir_expand::MacroDefKind; | 8 | use hir_expand::MacroDefKind; |
9 | use once_cell::sync::Lazy; | 9 | use once_cell::sync::Lazy; |
10 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
11 | use stdx::format_to; | ||
11 | use test_utils::mark; | 12 | use test_utils::mark; |
12 | 13 | ||
13 | use crate::{ | 14 | use crate::{ |
@@ -292,6 +293,30 @@ impl ItemScope { | |||
292 | *vis = Visibility::Module(this_module); | 293 | *vis = Visibility::Module(this_module); |
293 | } | 294 | } |
294 | } | 295 | } |
296 | |||
297 | pub(crate) fn dump(&self, buf: &mut String) { | ||
298 | let mut entries: Vec<_> = self.resolutions().collect(); | ||
299 | entries.sort_by_key(|(name, _)| name.clone()); | ||
300 | |||
301 | for (name, def) in entries { | ||
302 | format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string())); | ||
303 | |||
304 | if def.types.is_some() { | ||
305 | buf.push_str(" t"); | ||
306 | } | ||
307 | if def.values.is_some() { | ||
308 | buf.push_str(" v"); | ||
309 | } | ||
310 | if def.macros.is_some() { | ||
311 | buf.push_str(" m"); | ||
312 | } | ||
313 | if def.is_none() { | ||
314 | buf.push_str(" _"); | ||
315 | } | ||
316 | |||
317 | buf.push('\n'); | ||
318 | } | ||
319 | } | ||
295 | } | 320 | } |
296 | 321 | ||
297 | impl PerNs { | 322 | impl PerNs { |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 42d9f0947..3233b1957 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -24,7 +24,7 @@ use la_arena::{Arena, Idx, RawIdx}; | |||
24 | use profile::Count; | 24 | use profile::Count; |
25 | use rustc_hash::FxHashMap; | 25 | use rustc_hash::FxHashMap; |
26 | use smallvec::SmallVec; | 26 | use smallvec::SmallVec; |
27 | use syntax::{ast, match_ast}; | 27 | use syntax::{ast, match_ast, SyntaxKind}; |
28 | use test_utils::mark; | 28 | use test_utils::mark; |
29 | 29 | ||
30 | use crate::{ | 30 | use crate::{ |
@@ -80,6 +80,10 @@ impl ItemTree { | |||
80 | pub(crate) fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { | 80 | pub(crate) fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { |
81 | let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id)); | 81 | let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id)); |
82 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { | 82 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { |
83 | if node.kind() == SyntaxKind::ERROR { | ||
84 | // FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic | ||
85 | return Default::default(); | ||
86 | } | ||
83 | node | 87 | node |
84 | } else { | 88 | } else { |
85 | return Default::default(); | 89 | return Default::default(); |
@@ -142,6 +146,7 @@ impl ItemTree { | |||
142 | macro_defs, | 146 | macro_defs, |
143 | vis, | 147 | vis, |
144 | generics, | 148 | generics, |
149 | type_refs, | ||
145 | inner_items, | 150 | inner_items, |
146 | } = &mut **data; | 151 | } = &mut **data; |
147 | 152 | ||
@@ -165,6 +170,8 @@ impl ItemTree { | |||
165 | 170 | ||
166 | vis.arena.shrink_to_fit(); | 171 | vis.arena.shrink_to_fit(); |
167 | generics.arena.shrink_to_fit(); | 172 | generics.arena.shrink_to_fit(); |
173 | type_refs.arena.shrink_to_fit(); | ||
174 | type_refs.map.shrink_to_fit(); | ||
168 | 175 | ||
169 | inner_items.shrink_to_fit(); | 176 | inner_items.shrink_to_fit(); |
170 | } | 177 | } |
@@ -233,7 +240,7 @@ impl ItemVisibilities { | |||
233 | fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId { | 240 | fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId { |
234 | match &vis { | 241 | match &vis { |
235 | RawVisibility::Public => RawVisibilityId::PUB, | 242 | RawVisibility::Public => RawVisibilityId::PUB, |
236 | RawVisibility::Module(path) if path.segments.is_empty() => match &path.kind { | 243 | RawVisibility::Module(path) if path.segments().is_empty() => match &path.kind { |
237 | PathKind::Super(0) => RawVisibilityId::PRIV, | 244 | PathKind::Super(0) => RawVisibilityId::PRIV, |
238 | PathKind::Crate => RawVisibilityId::PUB_CRATE, | 245 | PathKind::Crate => RawVisibilityId::PUB_CRATE, |
239 | _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), | 246 | _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), |
@@ -244,10 +251,8 @@ impl ItemVisibilities { | |||
244 | } | 251 | } |
245 | 252 | ||
246 | static VIS_PUB: RawVisibility = RawVisibility::Public; | 253 | static VIS_PUB: RawVisibility = RawVisibility::Public; |
247 | static VIS_PRIV: RawVisibility = | 254 | static VIS_PRIV: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Super(0))); |
248 | RawVisibility::Module(ModPath { kind: PathKind::Super(0), segments: Vec::new() }); | 255 | static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Crate)); |
249 | static VIS_PUB_CRATE: RawVisibility = | ||
250 | RawVisibility::Module(ModPath { kind: PathKind::Crate, segments: Vec::new() }); | ||
251 | 256 | ||
252 | #[derive(Default, Debug, Eq, PartialEq)] | 257 | #[derive(Default, Debug, Eq, PartialEq)] |
253 | struct GenericParamsStorage { | 258 | struct GenericParamsStorage { |
@@ -275,6 +280,32 @@ static EMPTY_GENERICS: GenericParams = GenericParams { | |||
275 | where_predicates: Vec::new(), | 280 | where_predicates: Vec::new(), |
276 | }; | 281 | }; |
277 | 282 | ||
283 | /// `TypeRef` interner. | ||
284 | #[derive(Default, Debug, Eq, PartialEq)] | ||
285 | struct TypeRefStorage { | ||
286 | arena: Arena<Arc<TypeRef>>, | ||
287 | map: FxHashMap<Arc<TypeRef>, Idx<Arc<TypeRef>>>, | ||
288 | } | ||
289 | |||
290 | impl TypeRefStorage { | ||
291 | // Note: We lie about the `Idx<TypeRef>` to hide the interner details. | ||
292 | |||
293 | fn intern(&mut self, ty: TypeRef) -> Idx<TypeRef> { | ||
294 | if let Some(id) = self.map.get(&ty) { | ||
295 | return Idx::from_raw(id.into_raw()); | ||
296 | } | ||
297 | |||
298 | let ty = Arc::new(ty); | ||
299 | let idx = self.arena.alloc(ty.clone()); | ||
300 | self.map.insert(ty, idx); | ||
301 | Idx::from_raw(idx.into_raw()) | ||
302 | } | ||
303 | |||
304 | fn lookup(&self, id: Idx<TypeRef>) -> &TypeRef { | ||
305 | &self.arena[Idx::from_raw(id.into_raw())] | ||
306 | } | ||
307 | } | ||
308 | |||
278 | #[derive(Default, Debug, Eq, PartialEq)] | 309 | #[derive(Default, Debug, Eq, PartialEq)] |
279 | struct ItemTreeData { | 310 | struct ItemTreeData { |
280 | imports: Arena<Import>, | 311 | imports: Arena<Import>, |
@@ -297,6 +328,7 @@ struct ItemTreeData { | |||
297 | 328 | ||
298 | vis: ItemVisibilities, | 329 | vis: ItemVisibilities, |
299 | generics: GenericParamsStorage, | 330 | generics: GenericParamsStorage, |
331 | type_refs: TypeRefStorage, | ||
300 | 332 | ||
301 | inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>, | 333 | inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>, |
302 | } | 334 | } |
@@ -485,6 +517,14 @@ impl Index<GenericParamsId> for ItemTree { | |||
485 | } | 517 | } |
486 | } | 518 | } |
487 | 519 | ||
520 | impl Index<Idx<TypeRef>> for ItemTree { | ||
521 | type Output = TypeRef; | ||
522 | |||
523 | fn index(&self, id: Idx<TypeRef>) -> &Self::Output { | ||
524 | self.data().type_refs.lookup(id) | ||
525 | } | ||
526 | } | ||
527 | |||
488 | impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree { | 528 | impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree { |
489 | type Output = N; | 529 | type Output = N; |
490 | fn index(&self, id: FileItemTreeId<N>) -> &N { | 530 | fn index(&self, id: FileItemTreeId<N>) -> &N { |
@@ -528,9 +568,9 @@ pub struct Function { | |||
528 | /// Whether the function is located in an `extern` block (*not* whether it is an | 568 | /// Whether the function is located in an `extern` block (*not* whether it is an |
529 | /// `extern "abi" fn`). | 569 | /// `extern "abi" fn`). |
530 | pub is_extern: bool, | 570 | pub is_extern: bool, |
531 | pub params: Box<[TypeRef]>, | 571 | pub params: Box<[Idx<TypeRef>]>, |
532 | pub is_varargs: bool, | 572 | pub is_varargs: bool, |
533 | pub ret_type: TypeRef, | 573 | pub ret_type: Idx<TypeRef>, |
534 | pub ast_id: FileAstId<ast::Fn>, | 574 | pub ast_id: FileAstId<ast::Fn>, |
535 | } | 575 | } |
536 | 576 | ||
@@ -577,7 +617,7 @@ pub struct Const { | |||
577 | /// const _: () = (); | 617 | /// const _: () = (); |
578 | pub name: Option<Name>, | 618 | pub name: Option<Name>, |
579 | pub visibility: RawVisibilityId, | 619 | pub visibility: RawVisibilityId, |
580 | pub type_ref: TypeRef, | 620 | pub type_ref: Idx<TypeRef>, |
581 | pub ast_id: FileAstId<ast::Const>, | 621 | pub ast_id: FileAstId<ast::Const>, |
582 | } | 622 | } |
583 | 623 | ||
@@ -588,7 +628,7 @@ pub struct Static { | |||
588 | pub mutable: bool, | 628 | pub mutable: bool, |
589 | /// Whether the static is in an `extern` block. | 629 | /// Whether the static is in an `extern` block. |
590 | pub is_extern: bool, | 630 | pub is_extern: bool, |
591 | pub type_ref: TypeRef, | 631 | pub type_ref: Idx<TypeRef>, |
592 | pub ast_id: FileAstId<ast::Static>, | 632 | pub ast_id: FileAstId<ast::Static>, |
593 | } | 633 | } |
594 | 634 | ||
@@ -605,8 +645,8 @@ pub struct Trait { | |||
605 | #[derive(Debug, Clone, Eq, PartialEq)] | 645 | #[derive(Debug, Clone, Eq, PartialEq)] |
606 | pub struct Impl { | 646 | pub struct Impl { |
607 | pub generic_params: GenericParamsId, | 647 | pub generic_params: GenericParamsId, |
608 | pub target_trait: Option<TypeRef>, | 648 | pub target_trait: Option<Idx<TypeRef>>, |
609 | pub target_type: TypeRef, | 649 | pub target_type: Idx<TypeRef>, |
610 | pub is_negative: bool, | 650 | pub is_negative: bool, |
611 | pub items: Box<[AssocItem]>, | 651 | pub items: Box<[AssocItem]>, |
612 | pub ast_id: FileAstId<ast::Impl>, | 652 | pub ast_id: FileAstId<ast::Impl>, |
@@ -619,7 +659,7 @@ pub struct TypeAlias { | |||
619 | /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`. | 659 | /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`. |
620 | pub bounds: Box<[TypeBound]>, | 660 | pub bounds: Box<[TypeBound]>, |
621 | pub generic_params: GenericParamsId, | 661 | pub generic_params: GenericParamsId, |
622 | pub type_ref: Option<TypeRef>, | 662 | pub type_ref: Option<Idx<TypeRef>>, |
623 | pub is_extern: bool, | 663 | pub is_extern: bool, |
624 | pub ast_id: FileAstId<ast::TypeAlias>, | 664 | pub ast_id: FileAstId<ast::TypeAlias>, |
625 | } | 665 | } |
@@ -802,6 +842,6 @@ pub enum Fields { | |||
802 | #[derive(Debug, Clone, PartialEq, Eq)] | 842 | #[derive(Debug, Clone, PartialEq, Eq)] |
803 | pub struct Field { | 843 | pub struct Field { |
804 | pub name: Name, | 844 | pub name: Name, |
805 | pub type_ref: TypeRef, | 845 | pub type_ref: Idx<TypeRef>, |
806 | pub visibility: RawVisibilityId, | 846 | pub visibility: RawVisibilityId, |
807 | } | 847 | } |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index acc001add..8f2f0b340 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -183,6 +183,7 @@ impl Ctx { | |||
183 | block_stack.push(self.source_ast_id_map.ast_id(&block)); | 183 | block_stack.push(self.source_ast_id_map.ast_id(&block)); |
184 | }, | 184 | }, |
185 | ast::Item(item) => { | 185 | ast::Item(item) => { |
186 | // FIXME: This triggers for macro calls in expression position | ||
186 | let mod_items = self.lower_mod_item(&item, true); | 187 | let mod_items = self.lower_mod_item(&item, true); |
187 | let current_block = block_stack.last(); | 188 | let current_block = block_stack.last(); |
188 | if let (Some(mod_items), Some(block)) = (mod_items, current_block) { | 189 | if let (Some(mod_items), Some(block)) = (mod_items, current_block) { |
@@ -363,6 +364,7 @@ impl Ctx { | |||
363 | params.push(type_ref); | 364 | params.push(type_ref); |
364 | } | 365 | } |
365 | } | 366 | } |
367 | let params = params.into_iter().map(|param| self.data().type_refs.intern(param)).collect(); | ||
366 | 368 | ||
367 | let mut is_varargs = false; | 369 | let mut is_varargs = false; |
368 | if let Some(params) = func.param_list() { | 370 | if let Some(params) = func.param_list() { |
@@ -384,6 +386,8 @@ impl Ctx { | |||
384 | ret_type | 386 | ret_type |
385 | }; | 387 | }; |
386 | 388 | ||
389 | let ret_type = self.data().type_refs.intern(ret_type); | ||
390 | |||
387 | let has_body = func.body().is_some(); | 391 | let has_body = func.body().is_some(); |
388 | 392 | ||
389 | let ast_id = self.source_ast_id_map.ast_id(func); | 393 | let ast_id = self.source_ast_id_map.ast_id(func); |
@@ -395,7 +399,7 @@ impl Ctx { | |||
395 | has_body, | 399 | has_body, |
396 | is_unsafe: func.unsafe_token().is_some(), | 400 | is_unsafe: func.unsafe_token().is_some(), |
397 | is_extern: false, | 401 | is_extern: false, |
398 | params: params.into_boxed_slice(), | 402 | params, |
399 | is_varargs, | 403 | is_varargs, |
400 | ret_type, | 404 | ret_type, |
401 | ast_id, | 405 | ast_id, |
@@ -656,6 +660,7 @@ impl Ctx { | |||
656 | generics.fill(&self.body_ctx, sm, node); | 660 | generics.fill(&self.body_ctx, sm, node); |
657 | // lower `impl Trait` in arguments | 661 | // lower `impl Trait` in arguments |
658 | for param in &*func.params { | 662 | for param in &*func.params { |
663 | let param = self.data().type_refs.lookup(*param); | ||
659 | generics.fill_implicit_impl_trait_args(param); | 664 | generics.fill_implicit_impl_trait_args(param); |
660 | } | 665 | } |
661 | } | 666 | } |
@@ -708,11 +713,15 @@ impl Ctx { | |||
708 | self.data().vis.alloc(vis) | 713 | self.data().vis.alloc(vis) |
709 | } | 714 | } |
710 | 715 | ||
711 | fn lower_type_ref(&self, type_ref: &ast::Type) -> TypeRef { | 716 | fn lower_type_ref(&mut self, type_ref: &ast::Type) -> Idx<TypeRef> { |
712 | TypeRef::from_ast(&self.body_ctx, type_ref.clone()) | 717 | let tyref = TypeRef::from_ast(&self.body_ctx, type_ref.clone()); |
718 | self.data().type_refs.intern(tyref) | ||
713 | } | 719 | } |
714 | fn lower_type_ref_opt(&self, type_ref: Option<ast::Type>) -> TypeRef { | 720 | fn lower_type_ref_opt(&mut self, type_ref: Option<ast::Type>) -> Idx<TypeRef> { |
715 | type_ref.map(|ty| self.lower_type_ref(&ty)).unwrap_or(TypeRef::Error) | 721 | match type_ref.map(|ty| self.lower_type_ref(&ty)) { |
722 | Some(it) => it, | ||
723 | None => self.data().type_refs.intern(TypeRef::Error), | ||
724 | } | ||
716 | } | 725 | } |
717 | 726 | ||
718 | /// Forces the visibility `vis` to be used for all items lowered during execution of `f`. | 727 | /// Forces the visibility `vis` to be used for all items lowered during execution of `f`. |
@@ -741,7 +750,8 @@ impl Ctx { | |||
741 | 750 | ||
742 | fn desugar_future_path(orig: TypeRef) -> Path { | 751 | fn desugar_future_path(orig: TypeRef) -> Path { |
743 | let path = path![core::future::Future]; | 752 | let path = path![core::future::Future]; |
744 | let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); | 753 | let mut generic_args: Vec<_> = |
754 | std::iter::repeat(None).take(path.segments().len() - 1).collect(); | ||
745 | let mut last = GenericArgs::empty(); | 755 | let mut last = GenericArgs::empty(); |
746 | let binding = | 756 | let binding = |
747 | AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() }; | 757 | AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() }; |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 42b50b5b7..b50923747 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -81,7 +81,13 @@ pub struct ModuleId { | |||
81 | impl ModuleId { | 81 | impl ModuleId { |
82 | pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> { | 82 | pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> { |
83 | match self.block { | 83 | match self.block { |
84 | Some(block) => db.block_def_map(block), | 84 | Some(block) => { |
85 | db.block_def_map(block).unwrap_or_else(|| { | ||
86 | // NOTE: This should be unreachable - all `ModuleId`s come from their `DefMap`s, | ||
87 | // so the `DefMap` here must exist. | ||
88 | panic!("no `block_def_map` for `ModuleId` {:?}", self); | ||
89 | }) | ||
90 | } | ||
85 | None => db.crate_def_map(self.krate), | 91 | None => db.crate_def_map(self.krate), |
86 | } | 92 | } |
87 | } | 93 | } |
@@ -239,6 +245,7 @@ pub struct BlockId(salsa::InternId); | |||
239 | #[derive(Debug, Hash, PartialEq, Eq, Clone)] | 245 | #[derive(Debug, Hash, PartialEq, Eq, Clone)] |
240 | pub struct BlockLoc { | 246 | pub struct BlockLoc { |
241 | ast_id: AstId<ast::BlockExpr>, | 247 | ast_id: AstId<ast::BlockExpr>, |
248 | /// The containing module. | ||
242 | module: ModuleId, | 249 | module: ModuleId, |
243 | } | 250 | } |
244 | impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block); | 251 | impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block); |
@@ -655,7 +662,7 @@ impl AsMacroCall for AstIdWithPath<ast::Item> { | |||
655 | def.as_lazy_macro( | 662 | def.as_lazy_macro( |
656 | db.upcast(), | 663 | db.upcast(), |
657 | krate, | 664 | krate, |
658 | MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), | 665 | MacroCallKind::Attr(self.ast_id, self.path.segments().last()?.to_string()), |
659 | ) | 666 | ) |
660 | .into(), | 667 | .into(), |
661 | ) | 668 | ) |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 0a15fc470..ad2e9bcac 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -73,7 +73,15 @@ use crate::{ | |||
73 | AstId, BlockId, BlockLoc, LocalModuleId, ModuleDefId, ModuleId, | 73 | AstId, BlockId, BlockLoc, LocalModuleId, ModuleDefId, ModuleId, |
74 | }; | 74 | }; |
75 | 75 | ||
76 | /// Contains all top-level defs from a macro-expanded crate | 76 | /// Contains the results of (early) name resolution. |
77 | /// | ||
78 | /// A `DefMap` stores the module tree and the definitions that are in scope in every module after | ||
79 | /// item-level macros have been expanded. | ||
80 | /// | ||
81 | /// Every crate has a primary `DefMap` whose root is the crate's main file (`main.rs`/`lib.rs`), | ||
82 | /// computed by the `crate_def_map` query. Additionally, every block expression introduces the | ||
83 | /// opportunity to write arbitrary item and module hierarchies, and thus gets its own `DefMap` that | ||
84 | /// is computed by the `block_def_map` query. | ||
77 | #[derive(Debug, PartialEq, Eq)] | 85 | #[derive(Debug, PartialEq, Eq)] |
78 | pub struct DefMap { | 86 | pub struct DefMap { |
79 | _c: Count<Self>, | 87 | _c: Count<Self>, |
@@ -91,11 +99,13 @@ pub struct DefMap { | |||
91 | diagnostics: Vec<DefDiagnostic>, | 99 | diagnostics: Vec<DefDiagnostic>, |
92 | } | 100 | } |
93 | 101 | ||
94 | #[derive(Debug, PartialEq, Eq)] | 102 | /// For `DefMap`s computed for a block expression, this stores its location in the parent map. |
103 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
95 | struct BlockInfo { | 104 | struct BlockInfo { |
105 | /// The `BlockId` this `DefMap` was created from. | ||
96 | block: BlockId, | 106 | block: BlockId, |
97 | parent: Arc<DefMap>, | 107 | /// The containing module. |
98 | parent_module: LocalModuleId, | 108 | parent: ModuleId, |
99 | } | 109 | } |
100 | 110 | ||
101 | impl std::ops::Index<LocalModuleId> for DefMap { | 111 | impl std::ops::Index<LocalModuleId> for DefMap { |
@@ -197,21 +207,25 @@ impl DefMap { | |||
197 | Arc::new(def_map) | 207 | Arc::new(def_map) |
198 | } | 208 | } |
199 | 209 | ||
200 | pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> { | 210 | pub(crate) fn block_def_map_query( |
211 | db: &dyn DefDatabase, | ||
212 | block_id: BlockId, | ||
213 | ) -> Option<Arc<DefMap>> { | ||
201 | let block: BlockLoc = db.lookup_intern_block(block_id); | 214 | let block: BlockLoc = db.lookup_intern_block(block_id); |
202 | let parent = block.module.def_map(db); | ||
203 | 215 | ||
204 | // FIXME: It would be good to just return the parent map when the block has no items, but | 216 | let item_tree = db.item_tree(block.ast_id.file_id); |
205 | // we rely on `def_map.block` in a few places, which is `Some` for the inner `DefMap`. | 217 | if item_tree.inner_items_of_block(block.ast_id.value).is_empty() { |
218 | return None; | ||
219 | } | ||
206 | 220 | ||
207 | let block_info = | 221 | let block_info = BlockInfo { block: block_id, parent: block.module }; |
208 | BlockInfo { block: block_id, parent, parent_module: block.module.local_id }; | ||
209 | 222 | ||
210 | let mut def_map = DefMap::empty(block.module.krate, block_info.parent.edition); | 223 | let parent_map = block.module.def_map(db); |
224 | let mut def_map = DefMap::empty(block.module.krate, parent_map.edition); | ||
211 | def_map.block = Some(block_info); | 225 | def_map.block = Some(block_info); |
212 | 226 | ||
213 | let def_map = collector::collect_defs(db, def_map, Some(block.ast_id)); | 227 | let def_map = collector::collect_defs(db, def_map, Some(block.ast_id)); |
214 | Arc::new(def_map) | 228 | Some(Arc::new(def_map)) |
215 | } | 229 | } |
216 | 230 | ||
217 | fn empty(krate: CrateId, edition: Edition) -> DefMap { | 231 | fn empty(krate: CrateId, edition: Edition) -> DefMap { |
@@ -275,9 +289,15 @@ impl DefMap { | |||
275 | ModuleId { krate: self.krate, local_id, block } | 289 | ModuleId { krate: self.krate, local_id, block } |
276 | } | 290 | } |
277 | 291 | ||
278 | pub(crate) fn crate_root(&self) -> ModuleId { | 292 | pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId { |
279 | let (root_map, _) = self.ancestor_maps(self.root).last().unwrap(); | 293 | self.with_ancestor_maps(db, self.root, &mut |def_map, _module| { |
280 | root_map.module_id(root_map.root) | 294 | if def_map.block.is_none() { |
295 | Some(def_map.module_id(def_map.root)) | ||
296 | } else { | ||
297 | None | ||
298 | } | ||
299 | }) | ||
300 | .expect("DefMap chain without root") | ||
281 | } | 301 | } |
282 | 302 | ||
283 | pub(crate) fn resolve_path( | 303 | pub(crate) fn resolve_path( |
@@ -292,25 +312,42 @@ impl DefMap { | |||
292 | (res.resolved_def, res.segment_index) | 312 | (res.resolved_def, res.segment_index) |
293 | } | 313 | } |
294 | 314 | ||
295 | /// Iterates over the containing `DefMap`s, if `self` is a `DefMap` corresponding to a block | 315 | /// Ascends the `DefMap` hierarchy and calls `f` with every `DefMap` and containing module. |
296 | /// expression. | 316 | /// |
297 | fn ancestor_maps( | 317 | /// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns |
318 | /// `None`, iteration continues. | ||
319 | fn with_ancestor_maps<T>( | ||
298 | &self, | 320 | &self, |
321 | db: &dyn DefDatabase, | ||
299 | local_mod: LocalModuleId, | 322 | local_mod: LocalModuleId, |
300 | ) -> impl Iterator<Item = (&DefMap, LocalModuleId)> { | 323 | f: &mut dyn FnMut(&DefMap, LocalModuleId) -> Option<T>, |
301 | std::iter::successors(Some((self, local_mod)), |(map, _)| { | 324 | ) -> Option<T> { |
302 | map.block.as_ref().map(|block| (&*block.parent, block.parent_module)) | 325 | if let Some(it) = f(self, local_mod) { |
303 | }) | 326 | return Some(it); |
327 | } | ||
328 | let mut block = self.block; | ||
329 | while let Some(block_info) = block { | ||
330 | let parent = block_info.parent.def_map(db); | ||
331 | if let Some(it) = f(&parent, block_info.parent.local_id) { | ||
332 | return Some(it); | ||
333 | } | ||
334 | block = parent.block; | ||
335 | } | ||
336 | |||
337 | None | ||
304 | } | 338 | } |
305 | 339 | ||
306 | // FIXME: this can use some more human-readable format (ideally, an IR | 340 | // FIXME: this can use some more human-readable format (ideally, an IR |
307 | // even), as this should be a great debugging aid. | 341 | // even), as this should be a great debugging aid. |
308 | pub fn dump(&self) -> String { | 342 | pub fn dump(&self, db: &dyn DefDatabase) -> String { |
309 | let mut buf = String::new(); | 343 | let mut buf = String::new(); |
344 | let mut arc; | ||
310 | let mut current_map = self; | 345 | let mut current_map = self; |
311 | while let Some(block) = ¤t_map.block { | 346 | while let Some(block) = ¤t_map.block { |
312 | go(&mut buf, current_map, "block scope", current_map.root); | 347 | go(&mut buf, current_map, "block scope", current_map.root); |
313 | current_map = &*block.parent; | 348 | buf.push('\n'); |
349 | arc = block.parent.def_map(db); | ||
350 | current_map = &*arc; | ||
314 | } | 351 | } |
315 | go(&mut buf, current_map, "crate", current_map.root); | 352 | go(&mut buf, current_map, "crate", current_map.root); |
316 | return buf; | 353 | return buf; |
@@ -318,27 +355,7 @@ impl DefMap { | |||
318 | fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) { | 355 | fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) { |
319 | format_to!(buf, "{}\n", path); | 356 | format_to!(buf, "{}\n", path); |
320 | 357 | ||
321 | let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect(); | 358 | map.modules[module].scope.dump(buf); |
322 | entries.sort_by_key(|(name, _)| name.clone()); | ||
323 | |||
324 | for (name, def) in entries { | ||
325 | format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string())); | ||
326 | |||
327 | if def.types.is_some() { | ||
328 | buf.push_str(" t"); | ||
329 | } | ||
330 | if def.values.is_some() { | ||
331 | buf.push_str(" v"); | ||
332 | } | ||
333 | if def.macros.is_some() { | ||
334 | buf.push_str(" m"); | ||
335 | } | ||
336 | if def.is_none() { | ||
337 | buf.push_str(" _"); | ||
338 | } | ||
339 | |||
340 | buf.push('\n'); | ||
341 | } | ||
342 | 359 | ||
343 | for (name, child) in map.modules[module].children.iter() { | 360 | for (name, child) in map.modules[module].children.iter() { |
344 | let path = format!("{}::{}", path, name); | 361 | let path = format!("{}::{}", path, name); |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 6e86cc4a7..6bd41bc08 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -655,7 +655,7 @@ impl DefCollector<'_> { | |||
655 | } | 655 | } |
656 | } | 656 | } |
657 | } else { | 657 | } else { |
658 | match import.path.segments.last() { | 658 | match import.path.segments().last() { |
659 | Some(last_segment) => { | 659 | Some(last_segment) => { |
660 | let name = match &import.alias { | 660 | let name = match &import.alias { |
661 | Some(ImportAlias::Alias(name)) => Some(name.clone()), | 661 | Some(ImportAlias::Alias(name)) => Some(name.clone()), |
@@ -956,7 +956,7 @@ impl DefCollector<'_> { | |||
956 | let item_tree = self.db.item_tree(import.file_id); | 956 | let item_tree = self.db.item_tree(import.file_id); |
957 | let import_data = &item_tree[import.value]; | 957 | let import_data = &item_tree[import.value]; |
958 | 958 | ||
959 | match (import_data.path.segments.first(), &import_data.path.kind) { | 959 | match (import_data.path.segments().first(), &import_data.path.kind) { |
960 | (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => { | 960 | (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => { |
961 | if diagnosed_extern_crates.contains(krate) { | 961 | if diagnosed_extern_crates.contains(krate) { |
962 | continue; | 962 | continue; |
@@ -1449,10 +1449,11 @@ impl ModCollector<'_, '_> { | |||
1449 | if let Some(macro_call_id) = | 1449 | if let Some(macro_call_id) = |
1450 | ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| { | 1450 | ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| { |
1451 | path.as_ident().and_then(|name| { | 1451 | path.as_ident().and_then(|name| { |
1452 | self.def_collector | 1452 | self.def_collector.def_map.with_ancestor_maps( |
1453 | .def_map | 1453 | self.def_collector.db, |
1454 | .ancestor_maps(self.module_id) | 1454 | self.module_id, |
1455 | .find_map(|(map, module)| map[module].scope.get_legacy_macro(&name)) | 1455 | &mut |map, module| map[module].scope.get_legacy_macro(&name), |
1456 | ) | ||
1456 | }) | 1457 | }) |
1457 | }) | 1458 | }) |
1458 | { | 1459 | { |
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index ecf75c777..036e389b0 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -108,8 +108,8 @@ impl DefMap { | |||
108 | shadow: BuiltinShadowMode, | 108 | shadow: BuiltinShadowMode, |
109 | ) -> ResolvePathResult { | 109 | ) -> ResolvePathResult { |
110 | let mut result = ResolvePathResult::empty(ReachedFixedPoint::No); | 110 | let mut result = ResolvePathResult::empty(ReachedFixedPoint::No); |
111 | result.segment_index = Some(usize::max_value()); | ||
112 | 111 | ||
112 | let mut arc; | ||
113 | let mut current_map = self; | 113 | let mut current_map = self; |
114 | loop { | 114 | loop { |
115 | let new = current_map.resolve_path_fp_with_macro_single( | 115 | let new = current_map.resolve_path_fp_with_macro_single( |
@@ -127,12 +127,17 @@ impl DefMap { | |||
127 | } | 127 | } |
128 | // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates? | 128 | // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates? |
129 | result.krate = result.krate.or(new.krate); | 129 | result.krate = result.krate.or(new.krate); |
130 | result.segment_index = result.segment_index.min(new.segment_index); | 130 | result.segment_index = match (result.segment_index, new.segment_index) { |
131 | (Some(idx), None) => Some(idx), | ||
132 | (Some(old), Some(new)) => Some(old.max(new)), | ||
133 | (None, new) => new, | ||
134 | }; | ||
131 | 135 | ||
132 | match ¤t_map.block { | 136 | match ¤t_map.block { |
133 | Some(block) => { | 137 | Some(block) => { |
134 | current_map = &block.parent; | 138 | original_module = block.parent.local_id; |
135 | original_module = block.parent_module; | 139 | arc = block.parent.def_map(db); |
140 | current_map = &*arc; | ||
136 | } | 141 | } |
137 | None => return result, | 142 | None => return result, |
138 | } | 143 | } |
@@ -147,12 +152,12 @@ impl DefMap { | |||
147 | path: &ModPath, | 152 | path: &ModPath, |
148 | shadow: BuiltinShadowMode, | 153 | shadow: BuiltinShadowMode, |
149 | ) -> ResolvePathResult { | 154 | ) -> ResolvePathResult { |
150 | let mut segments = path.segments.iter().enumerate(); | 155 | let mut segments = path.segments().iter().enumerate(); |
151 | let mut curr_per_ns: PerNs = match path.kind { | 156 | let mut curr_per_ns: PerNs = match path.kind { |
152 | PathKind::DollarCrate(krate) => { | 157 | PathKind::DollarCrate(krate) => { |
153 | if krate == self.krate { | 158 | if krate == self.krate { |
154 | mark::hit!(macro_dollar_crate_self); | 159 | mark::hit!(macro_dollar_crate_self); |
155 | PerNs::types(self.crate_root().into(), Visibility::Public) | 160 | PerNs::types(self.crate_root(db).into(), Visibility::Public) |
156 | } else { | 161 | } else { |
157 | let def_map = db.crate_def_map(krate); | 162 | let def_map = db.crate_def_map(krate); |
158 | let module = def_map.module_id(def_map.root); | 163 | let module = def_map.module_id(def_map.root); |
@@ -160,7 +165,7 @@ impl DefMap { | |||
160 | PerNs::types(module.into(), Visibility::Public) | 165 | PerNs::types(module.into(), Visibility::Public) |
161 | } | 166 | } |
162 | } | 167 | } |
163 | PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public), | 168 | PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public), |
164 | // plain import or absolute path in 2015: crate-relative with | 169 | // plain import or absolute path in 2015: crate-relative with |
165 | // fallback to extern prelude (with the simplification in | 170 | // fallback to extern prelude (with the simplification in |
166 | // rust-lang/rust#57745) | 171 | // rust-lang/rust#57745) |
@@ -188,7 +193,7 @@ impl DefMap { | |||
188 | // BuiltinShadowMode wasn't Module, then we need to try | 193 | // BuiltinShadowMode wasn't Module, then we need to try |
189 | // resolving it as a builtin. | 194 | // resolving it as a builtin. |
190 | let prefer_module = | 195 | let prefer_module = |
191 | if path.segments.len() == 1 { shadow } else { BuiltinShadowMode::Module }; | 196 | if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module }; |
192 | 197 | ||
193 | log::debug!("resolving {:?} in module", segment); | 198 | log::debug!("resolving {:?} in module", segment); |
194 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) | 199 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) |
@@ -201,15 +206,15 @@ impl DefMap { | |||
201 | None => match &self.block { | 206 | None => match &self.block { |
202 | Some(block) => { | 207 | Some(block) => { |
203 | // Look up remaining path in parent `DefMap` | 208 | // Look up remaining path in parent `DefMap` |
204 | let new_path = ModPath { | 209 | let new_path = ModPath::from_segments( |
205 | kind: PathKind::Super(lvl - i), | 210 | PathKind::Super(lvl - i), |
206 | segments: path.segments.clone(), | 211 | path.segments().to_vec(), |
207 | }; | 212 | ); |
208 | log::debug!("`super` path: {} -> {} in parent map", path, new_path); | 213 | log::debug!("`super` path: {} -> {} in parent map", path, new_path); |
209 | return block.parent.resolve_path_fp_with_macro( | 214 | return block.parent.def_map(db).resolve_path_fp_with_macro( |
210 | db, | 215 | db, |
211 | mode, | 216 | mode, |
212 | block.parent_module, | 217 | block.parent.local_id, |
213 | &new_path, | 218 | &new_path, |
214 | shadow, | 219 | shadow, |
215 | ); | 220 | ); |
@@ -256,10 +261,10 @@ impl DefMap { | |||
256 | curr_per_ns = match curr { | 261 | curr_per_ns = match curr { |
257 | ModuleDefId::ModuleId(module) => { | 262 | ModuleDefId::ModuleId(module) => { |
258 | if module.krate != self.krate { | 263 | if module.krate != self.krate { |
259 | let path = ModPath { | 264 | let path = ModPath::from_segments( |
260 | segments: path.segments[i..].to_vec(), | 265 | PathKind::Super(0), |
261 | kind: PathKind::Super(0), | 266 | path.segments()[i..].iter().cloned(), |
262 | }; | 267 | ); |
263 | log::debug!("resolving {:?} in other crate", path); | 268 | log::debug!("resolving {:?} in other crate", path); |
264 | let defp_map = module.def_map(db); | 269 | let defp_map = module.def_map(db); |
265 | let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow); | 270 | let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow); |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index b36d0b59b..bd3e2701b 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -4,16 +4,16 @@ mod macros; | |||
4 | mod mod_resolution; | 4 | mod mod_resolution; |
5 | mod diagnostics; | 5 | mod diagnostics; |
6 | mod primitives; | 6 | mod primitives; |
7 | mod block; | ||
8 | 7 | ||
9 | use std::sync::Arc; | 8 | use std::sync::Arc; |
10 | 9 | ||
11 | use base_db::{fixture::WithFixture, FilePosition, SourceDatabase}; | 10 | use base_db::{fixture::WithFixture, SourceDatabase}; |
12 | use expect_test::{expect, Expect}; | 11 | use expect_test::{expect, Expect}; |
13 | use syntax::AstNode; | ||
14 | use test_utils::mark; | 12 | use test_utils::mark; |
15 | 13 | ||
16 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB, Lookup}; | 14 | use crate::{db::DefDatabase, test_db::TestDB}; |
15 | |||
16 | use super::DefMap; | ||
17 | 17 | ||
18 | fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { | 18 | fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { |
19 | let db = TestDB::with_files(ra_fixture); | 19 | let db = TestDB::with_files(ra_fixture); |
@@ -21,71 +21,14 @@ fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { | |||
21 | db.crate_def_map(krate) | 21 | db.crate_def_map(krate) |
22 | } | 22 | } |
23 | 23 | ||
24 | fn compute_block_def_map(ra_fixture: &str) -> Arc<DefMap> { | 24 | fn render_crate_def_map(ra_fixture: &str) -> String { |
25 | let (db, position) = TestDB::with_position(ra_fixture); | 25 | let db = TestDB::with_files(ra_fixture); |
26 | 26 | let krate = db.crate_graph().iter().next().unwrap(); | |
27 | // FIXME: perhaps we should make this use body lowering tests instead? | 27 | db.crate_def_map(krate).dump(&db) |
28 | |||
29 | let module = db.module_for_file(position.file_id); | ||
30 | let mut def_map = db.crate_def_map(module.krate); | ||
31 | while let Some(new_def_map) = descend_def_map_at_position(&db, position, def_map.clone()) { | ||
32 | def_map = new_def_map; | ||
33 | } | ||
34 | |||
35 | // FIXME: select the right module, not the root | ||
36 | |||
37 | def_map | ||
38 | } | ||
39 | |||
40 | fn descend_def_map_at_position( | ||
41 | db: &dyn DefDatabase, | ||
42 | position: FilePosition, | ||
43 | def_map: Arc<DefMap>, | ||
44 | ) -> Option<Arc<DefMap>> { | ||
45 | for (local_id, module_data) in def_map.modules() { | ||
46 | let mod_def = module_data.origin.definition_source(db); | ||
47 | let ast_map = db.ast_id_map(mod_def.file_id); | ||
48 | let item_tree = db.item_tree(mod_def.file_id); | ||
49 | let root = db.parse_or_expand(mod_def.file_id).unwrap(); | ||
50 | for item in module_data.scope.declarations() { | ||
51 | match item { | ||
52 | ModuleDefId::FunctionId(it) => { | ||
53 | // Technically blocks can be inside any type (due to arrays and const generics), | ||
54 | // and also in const/static initializers. For tests we only really care about | ||
55 | // functions though. | ||
56 | |||
57 | let ast = ast_map.get(item_tree[it.lookup(db).id.value].ast_id).to_node(&root); | ||
58 | |||
59 | if ast.syntax().text_range().contains(position.offset) { | ||
60 | // Cursor inside function, descend into its body's DefMap. | ||
61 | // Note that we don't handle block *expressions* inside function bodies. | ||
62 | let ast_map = db.ast_id_map(position.file_id.into()); | ||
63 | let ast_id = ast_map.ast_id(&ast.body().unwrap()); | ||
64 | let block = BlockLoc { | ||
65 | ast_id: InFile::new(position.file_id.into(), ast_id), | ||
66 | module: def_map.module_id(local_id), | ||
67 | }; | ||
68 | let block_id = db.intern_block(block); | ||
69 | return Some(db.block_def_map(block_id)); | ||
70 | } | ||
71 | } | ||
72 | _ => continue, | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | |||
77 | None | ||
78 | } | 28 | } |
79 | 29 | ||
80 | fn check(ra_fixture: &str, expect: Expect) { | 30 | fn check(ra_fixture: &str, expect: Expect) { |
81 | let def_map = compute_crate_def_map(ra_fixture); | 31 | let actual = render_crate_def_map(ra_fixture); |
82 | let actual = def_map.dump(); | ||
83 | expect.assert_eq(&actual); | ||
84 | } | ||
85 | |||
86 | fn check_at(ra_fixture: &str, expect: Expect) { | ||
87 | let def_map = compute_block_def_map(ra_fixture); | ||
88 | let actual = def_map.dump(); | ||
89 | expect.assert_eq(&actual); | 32 | expect.assert_eq(&actual); |
90 | } | 33 | } |
91 | 34 | ||
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index 84ea09b53..0e60dc2b6 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -20,7 +20,7 @@ use crate::{ | |||
20 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | 20 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
21 | pub struct ModPath { | 21 | pub struct ModPath { |
22 | pub kind: PathKind, | 22 | pub kind: PathKind, |
23 | pub segments: Vec<Name>, | 23 | segments: Vec<Name>, |
24 | } | 24 | } |
25 | 25 | ||
26 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | 26 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
@@ -53,6 +53,11 @@ impl ModPath { | |||
53 | ModPath { kind, segments } | 53 | ModPath { kind, segments } |
54 | } | 54 | } |
55 | 55 | ||
56 | /// Creates a `ModPath` from a `PathKind`, with no extra path segments. | ||
57 | pub const fn from_kind(kind: PathKind) -> ModPath { | ||
58 | ModPath { kind, segments: Vec::new() } | ||
59 | } | ||
60 | |||
56 | /// Calls `cb` with all paths, represented by this use item. | 61 | /// Calls `cb` with all paths, represented by this use item. |
57 | pub(crate) fn expand_use_item( | 62 | pub(crate) fn expand_use_item( |
58 | item_src: InFile<ast::Use>, | 63 | item_src: InFile<ast::Use>, |
@@ -64,6 +69,18 @@ impl ModPath { | |||
64 | } | 69 | } |
65 | } | 70 | } |
66 | 71 | ||
72 | pub fn segments(&self) -> &[Name] { | ||
73 | &self.segments | ||
74 | } | ||
75 | |||
76 | pub fn push_segment(&mut self, segment: Name) { | ||
77 | self.segments.push(segment); | ||
78 | } | ||
79 | |||
80 | pub fn pop_segment(&mut self) -> Option<Name> { | ||
81 | self.segments.pop() | ||
82 | } | ||
83 | |||
67 | /// Returns the number of segments in the path (counting special segments like `$crate` and | 84 | /// Returns the number of segments in the path (counting special segments like `$crate` and |
68 | /// `super`). | 85 | /// `super`). |
69 | pub fn len(&self) -> usize { | 86 | pub fn len(&self) -> usize { |
@@ -78,7 +95,7 @@ impl ModPath { | |||
78 | } | 95 | } |
79 | 96 | ||
80 | pub fn is_ident(&self) -> bool { | 97 | pub fn is_ident(&self) -> bool { |
81 | self.kind == PathKind::Plain && self.segments.len() == 1 | 98 | self.as_ident().is_some() |
82 | } | 99 | } |
83 | 100 | ||
84 | pub fn is_self(&self) -> bool { | 101 | pub fn is_self(&self) -> bool { |
@@ -87,10 +104,14 @@ impl ModPath { | |||
87 | 104 | ||
88 | /// If this path is a single identifier, like `foo`, return its name. | 105 | /// If this path is a single identifier, like `foo`, return its name. |
89 | pub fn as_ident(&self) -> Option<&Name> { | 106 | pub fn as_ident(&self) -> Option<&Name> { |
90 | if !self.is_ident() { | 107 | if self.kind != PathKind::Plain { |
91 | return None; | 108 | return None; |
92 | } | 109 | } |
93 | self.segments.first() | 110 | |
111 | match &*self.segments { | ||
112 | [name] => Some(name), | ||
113 | _ => None, | ||
114 | } | ||
94 | } | 115 | } |
95 | } | 116 | } |
96 | 117 | ||
@@ -180,10 +201,10 @@ impl Path { | |||
180 | } | 201 | } |
181 | let res = Path { | 202 | let res = Path { |
182 | type_anchor: self.type_anchor.clone(), | 203 | type_anchor: self.type_anchor.clone(), |
183 | mod_path: ModPath { | 204 | mod_path: ModPath::from_segments( |
184 | kind: self.mod_path.kind.clone(), | 205 | self.mod_path.kind.clone(), |
185 | segments: self.mod_path.segments[..self.mod_path.segments.len() - 1].to_vec(), | 206 | self.mod_path.segments[..self.mod_path.segments.len() - 1].iter().cloned(), |
186 | }, | 207 | ), |
187 | generic_args: self.generic_args[..self.generic_args.len() - 1].to_vec(), | 208 | generic_args: self.generic_args[..self.generic_args.len() - 1].to_vec(), |
188 | }; | 209 | }; |
189 | Some(res) | 210 | Some(res) |
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs index 9518ac109..a469546c1 100644 --- a/crates/hir_def/src/path/lower.rs +++ b/crates/hir_def/src/path/lower.rs | |||
@@ -129,7 +129,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> | |||
129 | } | 129 | } |
130 | } | 130 | } |
131 | 131 | ||
132 | let mod_path = ModPath { kind, segments }; | 132 | let mod_path = ModPath::from_segments(kind, segments); |
133 | return Some(Path { type_anchor, mod_path, generic_args }); | 133 | return Some(Path { type_anchor, mod_path, generic_args }); |
134 | 134 | ||
135 | fn qualifier(path: &ast::Path) -> Option<ast::Path> { | 135 | fn qualifier(path: &ast::Path) -> Option<ast::Path> { |
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs index ba0d1f0e7..d584b0b70 100644 --- a/crates/hir_def/src/path/lower/lower_use.rs +++ b/crates/hir_def/src/path/lower/lower_use.rs | |||
@@ -75,9 +75,10 @@ fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> | |||
75 | match hygiene.name_ref_to_name(name_ref) { | 75 | match hygiene.name_ref_to_name(name_ref) { |
76 | Either::Left(name) => { | 76 | Either::Left(name) => { |
77 | // no type args in use | 77 | // no type args in use |
78 | let mut res = prefix.unwrap_or_else(|| ModPath { | 78 | let mut res = prefix.unwrap_or_else(|| { |
79 | kind: segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs), | 79 | ModPath::from_kind( |
80 | segments: Vec::with_capacity(1), | 80 | segment.coloncolon_token().map_or(PathKind::Plain, |_| PathKind::Abs), |
81 | ) | ||
81 | }); | 82 | }); |
82 | res.segments.push(name); | 83 | res.segments.push(name); |
83 | res | 84 | res |
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index 9021ea712..f9ad50301 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs | |||
@@ -164,7 +164,7 @@ impl Resolver { | |||
164 | db: &dyn DefDatabase, | 164 | db: &dyn DefDatabase, |
165 | path: &ModPath, | 165 | path: &ModPath, |
166 | ) -> Option<(TypeNs, Option<usize>)> { | 166 | ) -> Option<(TypeNs, Option<usize>)> { |
167 | let first_name = path.segments.first()?; | 167 | let first_name = path.segments().first()?; |
168 | let skip_to_mod = path.kind != PathKind::Plain; | 168 | let skip_to_mod = path.kind != PathKind::Plain; |
169 | for scope in self.scopes.iter().rev() { | 169 | for scope in self.scopes.iter().rev() { |
170 | match scope { | 170 | match scope { |
@@ -179,7 +179,7 @@ impl Resolver { | |||
179 | 179 | ||
180 | Scope::GenericParams { params, def } => { | 180 | Scope::GenericParams { params, def } => { |
181 | if let Some(local_id) = params.find_type_by_name(first_name) { | 181 | if let Some(local_id) = params.find_type_by_name(first_name) { |
182 | let idx = if path.segments.len() == 1 { None } else { Some(1) }; | 182 | let idx = if path.segments().len() == 1 { None } else { Some(1) }; |
183 | return Some(( | 183 | return Some(( |
184 | TypeNs::GenericParam(TypeParamId { local_id, parent: *def }), | 184 | TypeNs::GenericParam(TypeParamId { local_id, parent: *def }), |
185 | idx, | 185 | idx, |
@@ -188,13 +188,13 @@ impl Resolver { | |||
188 | } | 188 | } |
189 | Scope::ImplDefScope(impl_) => { | 189 | Scope::ImplDefScope(impl_) => { |
190 | if first_name == &name![Self] { | 190 | if first_name == &name![Self] { |
191 | let idx = if path.segments.len() == 1 { None } else { Some(1) }; | 191 | let idx = if path.segments().len() == 1 { None } else { Some(1) }; |
192 | return Some((TypeNs::SelfType(*impl_), idx)); | 192 | return Some((TypeNs::SelfType(*impl_), idx)); |
193 | } | 193 | } |
194 | } | 194 | } |
195 | Scope::AdtScope(adt) => { | 195 | Scope::AdtScope(adt) => { |
196 | if first_name == &name![Self] { | 196 | if first_name == &name![Self] { |
197 | let idx = if path.segments.len() == 1 { None } else { Some(1) }; | 197 | let idx = if path.segments().len() == 1 { None } else { Some(1) }; |
198 | return Some((TypeNs::AdtSelfType(*adt), idx)); | 198 | return Some((TypeNs::AdtSelfType(*adt), idx)); |
199 | } | 199 | } |
200 | } | 200 | } |
@@ -270,9 +270,9 @@ impl Resolver { | |||
270 | db: &dyn DefDatabase, | 270 | db: &dyn DefDatabase, |
271 | path: &ModPath, | 271 | path: &ModPath, |
272 | ) -> Option<ResolveValueResult> { | 272 | ) -> Option<ResolveValueResult> { |
273 | let n_segments = path.segments.len(); | 273 | let n_segments = path.segments().len(); |
274 | let tmp = name![self]; | 274 | let tmp = name![self]; |
275 | let first_name = if path.is_self() { &tmp } else { path.segments.first()? }; | 275 | let first_name = if path.is_self() { &tmp } else { path.segments().first()? }; |
276 | let skip_to_mod = path.kind != PathKind::Plain && !path.is_self(); | 276 | let skip_to_mod = path.kind != PathKind::Plain && !path.is_self(); |
277 | for scope in self.scopes.iter().rev() { | 277 | for scope in self.scopes.iter().rev() { |
278 | match scope { | 278 | match scope { |
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs index e79a91102..38da3132b 100644 --- a/crates/hir_def/src/visibility.rs +++ b/crates/hir_def/src/visibility.rs | |||
@@ -22,8 +22,7 @@ pub enum RawVisibility { | |||
22 | 22 | ||
23 | impl RawVisibility { | 23 | impl RawVisibility { |
24 | pub(crate) const fn private() -> RawVisibility { | 24 | pub(crate) const fn private() -> RawVisibility { |
25 | let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() }; | 25 | RawVisibility::Module(ModPath::from_kind(PathKind::Super(0))) |
26 | RawVisibility::Module(path) | ||
27 | } | 26 | } |
28 | 27 | ||
29 | pub(crate) fn from_ast( | 28 | pub(crate) fn from_ast( |
@@ -59,15 +58,15 @@ impl RawVisibility { | |||
59 | RawVisibility::Module(path) | 58 | RawVisibility::Module(path) |
60 | } | 59 | } |
61 | ast::VisibilityKind::PubCrate => { | 60 | ast::VisibilityKind::PubCrate => { |
62 | let path = ModPath { kind: PathKind::Crate, segments: Vec::new() }; | 61 | let path = ModPath::from_kind(PathKind::Crate); |
63 | RawVisibility::Module(path) | 62 | RawVisibility::Module(path) |
64 | } | 63 | } |
65 | ast::VisibilityKind::PubSuper => { | 64 | ast::VisibilityKind::PubSuper => { |
66 | let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() }; | 65 | let path = ModPath::from_kind(PathKind::Super(1)); |
67 | RawVisibility::Module(path) | 66 | RawVisibility::Module(path) |
68 | } | 67 | } |
69 | ast::VisibilityKind::PubSelf => { | 68 | ast::VisibilityKind::PubSelf => { |
70 | let path = ModPath { kind: PathKind::Plain, segments: Vec::new() }; | 69 | let path = ModPath::from_kind(PathKind::Plain); |
71 | RawVisibility::Module(path) | 70 | RawVisibility::Module(path) |
72 | } | 71 | } |
73 | ast::VisibilityKind::Pub => RawVisibility::Public, | 72 | ast::VisibilityKind::Pub => RawVisibility::Public, |
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 323c5f963..08483760c 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -345,6 +345,37 @@ impl fmt::Display for CaseType { | |||
345 | } | 345 | } |
346 | } | 346 | } |
347 | 347 | ||
348 | #[derive(Debug)] | ||
349 | pub enum IdentType { | ||
350 | Argument, | ||
351 | Constant, | ||
352 | Enum, | ||
353 | Field, | ||
354 | Function, | ||
355 | StaticVariable, | ||
356 | Structure, | ||
357 | Variable, | ||
358 | Variant, | ||
359 | } | ||
360 | |||
361 | impl fmt::Display for IdentType { | ||
362 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
363 | let repr = match self { | ||
364 | IdentType::Argument => "Argument", | ||
365 | IdentType::Constant => "Constant", | ||
366 | IdentType::Enum => "Enum", | ||
367 | IdentType::Field => "Field", | ||
368 | IdentType::Function => "Function", | ||
369 | IdentType::StaticVariable => "Static variable", | ||
370 | IdentType::Structure => "Structure", | ||
371 | IdentType::Variable => "Variable", | ||
372 | IdentType::Variant => "Variant", | ||
373 | }; | ||
374 | |||
375 | write!(f, "{}", repr) | ||
376 | } | ||
377 | } | ||
378 | |||
348 | // Diagnostic: incorrect-ident-case | 379 | // Diagnostic: incorrect-ident-case |
349 | // | 380 | // |
350 | // This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. | 381 | // This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. |
@@ -353,7 +384,7 @@ pub struct IncorrectCase { | |||
353 | pub file: HirFileId, | 384 | pub file: HirFileId, |
354 | pub ident: AstPtr<ast::Name>, | 385 | pub ident: AstPtr<ast::Name>, |
355 | pub expected_case: CaseType, | 386 | pub expected_case: CaseType, |
356 | pub ident_type: String, | 387 | pub ident_type: IdentType, |
357 | pub ident_text: String, | 388 | pub ident_text: String, |
358 | pub suggested_text: String, | 389 | pub suggested_text: String, |
359 | } | 390 | } |
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index eaeb6899f..6773ddea3 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -23,6 +23,7 @@ use hir_expand::{ | |||
23 | diagnostics::DiagnosticSink, | 23 | diagnostics::DiagnosticSink, |
24 | name::{AsName, Name}, | 24 | name::{AsName, Name}, |
25 | }; | 25 | }; |
26 | use stdx::{always, never}; | ||
26 | use syntax::{ | 27 | use syntax::{ |
27 | ast::{self, NameOwner}, | 28 | ast::{self, NameOwner}, |
28 | AstNode, AstPtr, | 29 | AstNode, AstPtr, |
@@ -31,7 +32,7 @@ use test_utils::mark; | |||
31 | 32 | ||
32 | use crate::{ | 33 | use crate::{ |
33 | db::HirDatabase, | 34 | db::HirDatabase, |
34 | diagnostics::{decl_check::case_conv::*, CaseType, IncorrectCase}, | 35 | diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, |
35 | }; | 36 | }; |
36 | 37 | ||
37 | mod allow { | 38 | mod allow { |
@@ -40,7 +41,7 @@ mod allow { | |||
40 | pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; | 41 | pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; |
41 | } | 42 | } |
42 | 43 | ||
43 | pub(super) struct DeclValidator<'a, 'b: 'a> { | 44 | pub(super) struct DeclValidator<'a, 'b> { |
44 | db: &'a dyn HirDatabase, | 45 | db: &'a dyn HirDatabase, |
45 | krate: CrateId, | 46 | krate: CrateId, |
46 | sink: &'a mut DiagnosticSink<'b>, | 47 | sink: &'a mut DiagnosticSink<'b>, |
@@ -77,7 +78,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
77 | AdtId::StructId(struct_id) => self.validate_struct(struct_id), | 78 | AdtId::StructId(struct_id) => self.validate_struct(struct_id), |
78 | AdtId::EnumId(enum_id) => self.validate_enum(enum_id), | 79 | AdtId::EnumId(enum_id) => self.validate_enum(enum_id), |
79 | AdtId::UnionId(_) => { | 80 | AdtId::UnionId(_) => { |
80 | // Unions aren't yet supported by this validator. | 81 | // FIXME: Unions aren't yet supported by this validator. |
81 | } | 82 | } |
82 | } | 83 | } |
83 | } | 84 | } |
@@ -111,63 +112,50 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
111 | 112 | ||
112 | // Check the function name. | 113 | // Check the function name. |
113 | let function_name = data.name.to_string(); | 114 | let function_name = data.name.to_string(); |
114 | let fn_name_replacement = if let Some(new_name) = to_lower_snake_case(&function_name) { | 115 | let fn_name_replacement = to_lower_snake_case(&function_name).map(|new_name| Replacement { |
115 | let replacement = Replacement { | 116 | current_name: data.name.clone(), |
116 | current_name: data.name.clone(), | 117 | suggested_text: new_name, |
117 | suggested_text: new_name, | 118 | expected_case: CaseType::LowerSnakeCase, |
118 | expected_case: CaseType::LowerSnakeCase, | 119 | }); |
119 | }; | ||
120 | Some(replacement) | ||
121 | } else { | ||
122 | None | ||
123 | }; | ||
124 | 120 | ||
125 | // Check the param names. | 121 | // Check the param names. |
126 | let mut fn_param_replacements = Vec::new(); | 122 | let fn_param_replacements = body |
127 | 123 | .params | |
128 | for pat_id in body.params.iter().cloned() { | 124 | .iter() |
129 | let pat = &body[pat_id]; | 125 | .filter_map(|&id| match &body[id] { |
130 | 126 | Pat::Bind { name, .. } => Some(name), | |
131 | let param_name = match pat { | 127 | _ => None, |
132 | Pat::Bind { name, .. } => name, | 128 | }) |
133 | _ => continue, | 129 | .filter_map(|param_name| { |
134 | }; | 130 | Some(Replacement { |
135 | |||
136 | let name = param_name.to_string(); | ||
137 | if let Some(new_name) = to_lower_snake_case(&name) { | ||
138 | let replacement = Replacement { | ||
139 | current_name: param_name.clone(), | 131 | current_name: param_name.clone(), |
140 | suggested_text: new_name, | 132 | suggested_text: to_lower_snake_case(¶m_name.to_string())?, |
141 | expected_case: CaseType::LowerSnakeCase, | 133 | expected_case: CaseType::LowerSnakeCase, |
142 | }; | 134 | }) |
143 | fn_param_replacements.push(replacement); | 135 | }) |
144 | } | 136 | .collect(); |
145 | } | ||
146 | 137 | ||
147 | // Check the patterns inside the function body. | 138 | // Check the patterns inside the function body. |
148 | let mut pats_replacements = Vec::new(); | 139 | let pats_replacements = body |
149 | 140 | .pats | |
150 | for (pat_idx, pat) in body.pats.iter() { | 141 | .iter() |
151 | if body.params.contains(&pat_idx) { | 142 | // We aren't interested in function parameters, we've processed them above. |
152 | // We aren't interested in function parameters, we've processed them above. | 143 | .filter(|(pat_idx, _)| !body.params.contains(&pat_idx)) |
153 | continue; | 144 | .filter_map(|(id, pat)| match pat { |
154 | } | 145 | Pat::Bind { name, .. } => Some((id, name)), |
155 | 146 | _ => None, | |
156 | let bind_name = match pat { | 147 | }) |
157 | Pat::Bind { name, .. } => name, | 148 | .filter_map(|(id, bind_name)| { |
158 | _ => continue, | 149 | Some(( |
159 | }; | 150 | id, |
160 | 151 | Replacement { | |
161 | let name = bind_name.to_string(); | 152 | current_name: bind_name.clone(), |
162 | if let Some(new_name) = to_lower_snake_case(&name) { | 153 | suggested_text: to_lower_snake_case(&bind_name.to_string())?, |
163 | let replacement = Replacement { | 154 | expected_case: CaseType::LowerSnakeCase, |
164 | current_name: bind_name.clone(), | 155 | }, |
165 | suggested_text: new_name, | 156 | )) |
166 | expected_case: CaseType::LowerSnakeCase, | 157 | }) |
167 | }; | 158 | .collect(); |
168 | pats_replacements.push((pat_idx, replacement)); | ||
169 | } | ||
170 | } | ||
171 | 159 | ||
172 | // If there is at least one element to spawn a warning on, go to the source map and generate a warning. | 160 | // If there is at least one element to spawn a warning on, go to the source map and generate a warning. |
173 | self.create_incorrect_case_diagnostic_for_func( | 161 | self.create_incorrect_case_diagnostic_for_func( |
@@ -199,8 +187,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
199 | let ast_ptr = match fn_src.value.name() { | 187 | let ast_ptr = match fn_src.value.name() { |
200 | Some(name) => name, | 188 | Some(name) => name, |
201 | None => { | 189 | None => { |
202 | // We don't want rust-analyzer to panic over this, but it is definitely some kind of error in the logic. | 190 | never!( |
203 | log::error!( | ||
204 | "Replacement ({:?}) was generated for a function without a name: {:?}", | 191 | "Replacement ({:?}) was generated for a function without a name: {:?}", |
205 | replacement, | 192 | replacement, |
206 | fn_src | 193 | fn_src |
@@ -211,7 +198,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
211 | 198 | ||
212 | let diagnostic = IncorrectCase { | 199 | let diagnostic = IncorrectCase { |
213 | file: fn_src.file_id, | 200 | file: fn_src.file_id, |
214 | ident_type: "Function".to_string(), | 201 | ident_type: IdentType::Function, |
215 | ident: AstPtr::new(&ast_ptr).into(), | 202 | ident: AstPtr::new(&ast_ptr).into(), |
216 | expected_case: replacement.expected_case, | 203 | expected_case: replacement.expected_case, |
217 | ident_text: replacement.current_name.to_string(), | 204 | ident_text: replacement.current_name.to_string(), |
@@ -225,12 +212,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
225 | let fn_params_list = match fn_src.value.param_list() { | 212 | let fn_params_list = match fn_src.value.param_list() { |
226 | Some(params) => params, | 213 | Some(params) => params, |
227 | None => { | 214 | None => { |
228 | if !fn_param_replacements.is_empty() { | 215 | always!( |
229 | log::error!( | 216 | fn_param_replacements.is_empty(), |
230 | "Replacements ({:?}) were generated for a function parameters which had no parameters list: {:?}", | 217 | "Replacements ({:?}) were generated for a function parameters which had no parameters list: {:?}", |
231 | fn_param_replacements, fn_src | 218 | fn_param_replacements, |
232 | ); | 219 | fn_src |
233 | } | 220 | ); |
234 | return; | 221 | return; |
235 | } | 222 | } |
236 | }; | 223 | }; |
@@ -240,23 +227,25 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
240 | // actual params list, but just some of them (ones that named correctly) are skipped. | 227 | // actual params list, but just some of them (ones that named correctly) are skipped. |
241 | let ast_ptr: ast::Name = loop { | 228 | let ast_ptr: ast::Name = loop { |
242 | match fn_params_iter.next() { | 229 | match fn_params_iter.next() { |
243 | Some(element) | 230 | Some(element) => { |
244 | if pat_equals_to_name(element.pat(), ¶m_to_rename.current_name) => | 231 | if let Some(ast::Pat::IdentPat(pat)) = element.pat() { |
245 | { | 232 | if pat.to_string() == param_to_rename.current_name.to_string() { |
246 | if let ast::Pat::IdentPat(pat) = element.pat().unwrap() { | 233 | if let Some(name) = pat.name() { |
247 | break pat.name().unwrap(); | 234 | break name; |
248 | } else { | 235 | } |
249 | // This is critical. If we consider this parameter the expected one, | 236 | // This is critical. If we consider this parameter the expected one, |
250 | // it **must** have a name. | 237 | // it **must** have a name. |
251 | panic!( | 238 | never!( |
252 | "Pattern {:?} equals to expected replacement {:?}, but has no name", | 239 | "Pattern {:?} equals to expected replacement {:?}, but has no name", |
253 | element, param_to_rename | 240 | element, |
254 | ); | 241 | param_to_rename |
242 | ); | ||
243 | return; | ||
244 | } | ||
255 | } | 245 | } |
256 | } | 246 | } |
257 | Some(_) => {} | ||
258 | None => { | 247 | None => { |
259 | log::error!( | 248 | never!( |
260 | "Replacement ({:?}) was generated for a function parameter which was not found: {:?}", | 249 | "Replacement ({:?}) was generated for a function parameter which was not found: {:?}", |
261 | param_to_rename, fn_src | 250 | param_to_rename, fn_src |
262 | ); | 251 | ); |
@@ -267,7 +256,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
267 | 256 | ||
268 | let diagnostic = IncorrectCase { | 257 | let diagnostic = IncorrectCase { |
269 | file: fn_src.file_id, | 258 | file: fn_src.file_id, |
270 | ident_type: "Argument".to_string(), | 259 | ident_type: IdentType::Argument, |
271 | ident: AstPtr::new(&ast_ptr).into(), | 260 | ident: AstPtr::new(&ast_ptr).into(), |
272 | expected_case: param_to_rename.expected_case, | 261 | expected_case: param_to_rename.expected_case, |
273 | ident_text: param_to_rename.current_name.to_string(), | 262 | ident_text: param_to_rename.current_name.to_string(), |
@@ -309,8 +298,8 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
309 | // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement, | 298 | // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement, |
310 | // because e.g. match arms are patterns as well. | 299 | // because e.g. match arms are patterns as well. |
311 | // In other words, we check that it's a named variable binding. | 300 | // In other words, we check that it's a named variable binding. |
312 | let is_binding = ast::LetStmt::cast(parent.clone()).is_some() | 301 | let is_binding = ast::LetStmt::can_cast(parent.kind()) |
313 | || (ast::MatchArm::cast(parent).is_some() | 302 | || (ast::MatchArm::can_cast(parent.kind()) |
314 | && ident_pat.at_token().is_some()); | 303 | && ident_pat.at_token().is_some()); |
315 | if !is_binding { | 304 | if !is_binding { |
316 | // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm. | 305 | // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm. |
@@ -319,7 +308,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
319 | 308 | ||
320 | let diagnostic = IncorrectCase { | 309 | let diagnostic = IncorrectCase { |
321 | file: source_ptr.file_id, | 310 | file: source_ptr.file_id, |
322 | ident_type: "Variable".to_string(), | 311 | ident_type: IdentType::Variable, |
323 | ident: AstPtr::new(&name_ast).into(), | 312 | ident: AstPtr::new(&name_ast).into(), |
324 | expected_case: replacement.expected_case, | 313 | expected_case: replacement.expected_case, |
325 | ident_text: replacement.current_name.to_string(), | 314 | ident_text: replacement.current_name.to_string(), |
@@ -341,17 +330,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
341 | 330 | ||
342 | // Check the structure name. | 331 | // Check the structure name. |
343 | let struct_name = data.name.to_string(); | 332 | let struct_name = data.name.to_string(); |
344 | let struct_name_replacement = if let Some(new_name) = to_camel_case(&struct_name) { | 333 | let struct_name_replacement = if !non_camel_case_allowed { |
345 | let replacement = Replacement { | 334 | to_camel_case(&struct_name).map(|new_name| Replacement { |
346 | current_name: data.name.clone(), | 335 | current_name: data.name.clone(), |
347 | suggested_text: new_name, | 336 | suggested_text: new_name, |
348 | expected_case: CaseType::UpperCamelCase, | 337 | expected_case: CaseType::UpperCamelCase, |
349 | }; | 338 | }) |
350 | if non_camel_case_allowed { | ||
351 | None | ||
352 | } else { | ||
353 | Some(replacement) | ||
354 | } | ||
355 | } else { | 339 | } else { |
356 | None | 340 | None |
357 | }; | 341 | }; |
@@ -403,8 +387,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
403 | let ast_ptr = match struct_src.value.name() { | 387 | let ast_ptr = match struct_src.value.name() { |
404 | Some(name) => name, | 388 | Some(name) => name, |
405 | None => { | 389 | None => { |
406 | // We don't want rust-analyzer to panic over this, but it is definitely some kind of error in the logic. | 390 | never!( |
407 | log::error!( | ||
408 | "Replacement ({:?}) was generated for a structure without a name: {:?}", | 391 | "Replacement ({:?}) was generated for a structure without a name: {:?}", |
409 | replacement, | 392 | replacement, |
410 | struct_src | 393 | struct_src |
@@ -415,7 +398,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
415 | 398 | ||
416 | let diagnostic = IncorrectCase { | 399 | let diagnostic = IncorrectCase { |
417 | file: struct_src.file_id, | 400 | file: struct_src.file_id, |
418 | ident_type: "Structure".to_string(), | 401 | ident_type: IdentType::Structure, |
419 | ident: AstPtr::new(&ast_ptr).into(), | 402 | ident: AstPtr::new(&ast_ptr).into(), |
420 | expected_case: replacement.expected_case, | 403 | expected_case: replacement.expected_case, |
421 | ident_text: replacement.current_name.to_string(), | 404 | ident_text: replacement.current_name.to_string(), |
@@ -428,12 +411,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
428 | let struct_fields_list = match struct_src.value.field_list() { | 411 | let struct_fields_list = match struct_src.value.field_list() { |
429 | Some(ast::FieldList::RecordFieldList(fields)) => fields, | 412 | Some(ast::FieldList::RecordFieldList(fields)) => fields, |
430 | _ => { | 413 | _ => { |
431 | if !struct_fields_replacements.is_empty() { | 414 | always!( |
432 | log::error!( | 415 | struct_fields_replacements.is_empty(), |
433 | "Replacements ({:?}) were generated for a structure fields which had no fields list: {:?}", | 416 | "Replacements ({:?}) were generated for a structure fields which had no fields list: {:?}", |
434 | struct_fields_replacements, struct_src | 417 | struct_fields_replacements, |
435 | ); | 418 | struct_src |
436 | } | 419 | ); |
437 | return; | 420 | return; |
438 | } | 421 | } |
439 | }; | 422 | }; |
@@ -442,13 +425,14 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
442 | // We assume that parameters in replacement are in the same order as in the | 425 | // We assume that parameters in replacement are in the same order as in the |
443 | // actual params list, but just some of them (ones that named correctly) are skipped. | 426 | // actual params list, but just some of them (ones that named correctly) are skipped. |
444 | let ast_ptr = loop { | 427 | let ast_ptr = loop { |
445 | match struct_fields_iter.next() { | 428 | match struct_fields_iter.next().and_then(|field| field.name()) { |
446 | Some(element) if names_equal(element.name(), &field_to_rename.current_name) => { | 429 | Some(field_name) => { |
447 | break element.name().unwrap() | 430 | if field_name.as_name() == field_to_rename.current_name { |
431 | break field_name; | ||
432 | } | ||
448 | } | 433 | } |
449 | Some(_) => {} | ||
450 | None => { | 434 | None => { |
451 | log::error!( | 435 | never!( |
452 | "Replacement ({:?}) was generated for a structure field which was not found: {:?}", | 436 | "Replacement ({:?}) was generated for a structure field which was not found: {:?}", |
453 | field_to_rename, struct_src | 437 | field_to_rename, struct_src |
454 | ); | 438 | ); |
@@ -459,7 +443,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
459 | 443 | ||
460 | let diagnostic = IncorrectCase { | 444 | let diagnostic = IncorrectCase { |
461 | file: struct_src.file_id, | 445 | file: struct_src.file_id, |
462 | ident_type: "Field".to_string(), | 446 | ident_type: IdentType::Field, |
463 | ident: AstPtr::new(&ast_ptr).into(), | 447 | ident: AstPtr::new(&ast_ptr).into(), |
464 | expected_case: field_to_rename.expected_case, | 448 | expected_case: field_to_rename.expected_case, |
465 | ident_text: field_to_rename.current_name.to_string(), | 449 | ident_text: field_to_rename.current_name.to_string(), |
@@ -480,31 +464,24 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
480 | 464 | ||
481 | // Check the enum name. | 465 | // Check the enum name. |
482 | let enum_name = data.name.to_string(); | 466 | let enum_name = data.name.to_string(); |
483 | let enum_name_replacement = if let Some(new_name) = to_camel_case(&enum_name) { | 467 | let enum_name_replacement = to_camel_case(&enum_name).map(|new_name| Replacement { |
484 | let replacement = Replacement { | 468 | current_name: data.name.clone(), |
485 | current_name: data.name.clone(), | 469 | suggested_text: new_name, |
486 | suggested_text: new_name, | 470 | expected_case: CaseType::UpperCamelCase, |
487 | expected_case: CaseType::UpperCamelCase, | 471 | }); |
488 | }; | ||
489 | Some(replacement) | ||
490 | } else { | ||
491 | None | ||
492 | }; | ||
493 | 472 | ||
494 | // Check the field names. | 473 | // Check the field names. |
495 | let mut enum_fields_replacements = Vec::new(); | 474 | let enum_fields_replacements = data |
496 | 475 | .variants | |
497 | for (_, variant) in data.variants.iter() { | 476 | .iter() |
498 | let variant_name = variant.name.to_string(); | 477 | .filter_map(|(_, variant)| { |
499 | if let Some(new_name) = to_camel_case(&variant_name) { | 478 | Some(Replacement { |
500 | let replacement = Replacement { | ||
501 | current_name: variant.name.clone(), | 479 | current_name: variant.name.clone(), |
502 | suggested_text: new_name, | 480 | suggested_text: to_camel_case(&variant.name.to_string())?, |
503 | expected_case: CaseType::UpperCamelCase, | 481 | expected_case: CaseType::UpperCamelCase, |
504 | }; | 482 | }) |
505 | enum_fields_replacements.push(replacement); | 483 | }) |
506 | } | 484 | .collect(); |
507 | } | ||
508 | 485 | ||
509 | // If there is at least one element to spawn a warning on, go to the source map and generate a warning. | 486 | // If there is at least one element to spawn a warning on, go to the source map and generate a warning. |
510 | self.create_incorrect_case_diagnostic_for_enum( | 487 | self.create_incorrect_case_diagnostic_for_enum( |
@@ -534,8 +511,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
534 | let ast_ptr = match enum_src.value.name() { | 511 | let ast_ptr = match enum_src.value.name() { |
535 | Some(name) => name, | 512 | Some(name) => name, |
536 | None => { | 513 | None => { |
537 | // We don't want rust-analyzer to panic over this, but it is definitely some kind of error in the logic. | 514 | never!( |
538 | log::error!( | ||
539 | "Replacement ({:?}) was generated for a enum without a name: {:?}", | 515 | "Replacement ({:?}) was generated for a enum without a name: {:?}", |
540 | replacement, | 516 | replacement, |
541 | enum_src | 517 | enum_src |
@@ -546,7 +522,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
546 | 522 | ||
547 | let diagnostic = IncorrectCase { | 523 | let diagnostic = IncorrectCase { |
548 | file: enum_src.file_id, | 524 | file: enum_src.file_id, |
549 | ident_type: "Enum".to_string(), | 525 | ident_type: IdentType::Enum, |
550 | ident: AstPtr::new(&ast_ptr).into(), | 526 | ident: AstPtr::new(&ast_ptr).into(), |
551 | expected_case: replacement.expected_case, | 527 | expected_case: replacement.expected_case, |
552 | ident_text: replacement.current_name.to_string(), | 528 | ident_text: replacement.current_name.to_string(), |
@@ -559,12 +535,12 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
559 | let enum_variants_list = match enum_src.value.variant_list() { | 535 | let enum_variants_list = match enum_src.value.variant_list() { |
560 | Some(variants) => variants, | 536 | Some(variants) => variants, |
561 | _ => { | 537 | _ => { |
562 | if !enum_variants_replacements.is_empty() { | 538 | always!( |
563 | log::error!( | 539 | enum_variants_replacements.is_empty(), |
564 | "Replacements ({:?}) were generated for a enum variants which had no fields list: {:?}", | 540 | "Replacements ({:?}) were generated for a enum variants which had no fields list: {:?}", |
565 | enum_variants_replacements, enum_src | 541 | enum_variants_replacements, |
566 | ); | 542 | enum_src |
567 | } | 543 | ); |
568 | return; | 544 | return; |
569 | } | 545 | } |
570 | }; | 546 | }; |
@@ -573,15 +549,14 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
573 | // We assume that parameters in replacement are in the same order as in the | 549 | // We assume that parameters in replacement are in the same order as in the |
574 | // actual params list, but just some of them (ones that named correctly) are skipped. | 550 | // actual params list, but just some of them (ones that named correctly) are skipped. |
575 | let ast_ptr = loop { | 551 | let ast_ptr = loop { |
576 | match enum_variants_iter.next() { | 552 | match enum_variants_iter.next().and_then(|v| v.name()) { |
577 | Some(variant) | 553 | Some(variant_name) => { |
578 | if names_equal(variant.name(), &variant_to_rename.current_name) => | 554 | if variant_name.as_name() == variant_to_rename.current_name { |
579 | { | 555 | break variant_name; |
580 | break variant.name().unwrap() | 556 | } |
581 | } | 557 | } |
582 | Some(_) => {} | ||
583 | None => { | 558 | None => { |
584 | log::error!( | 559 | never!( |
585 | "Replacement ({:?}) was generated for a enum variant which was not found: {:?}", | 560 | "Replacement ({:?}) was generated for a enum variant which was not found: {:?}", |
586 | variant_to_rename, enum_src | 561 | variant_to_rename, enum_src |
587 | ); | 562 | ); |
@@ -592,7 +567,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
592 | 567 | ||
593 | let diagnostic = IncorrectCase { | 568 | let diagnostic = IncorrectCase { |
594 | file: enum_src.file_id, | 569 | file: enum_src.file_id, |
595 | ident_type: "Variant".to_string(), | 570 | ident_type: IdentType::Variant, |
596 | ident: AstPtr::new(&ast_ptr).into(), | 571 | ident: AstPtr::new(&ast_ptr).into(), |
597 | expected_case: variant_to_rename.expected_case, | 572 | expected_case: variant_to_rename.expected_case, |
598 | ident_text: variant_to_rename.current_name.to_string(), | 573 | ident_text: variant_to_rename.current_name.to_string(), |
@@ -637,7 +612,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
637 | 612 | ||
638 | let diagnostic = IncorrectCase { | 613 | let diagnostic = IncorrectCase { |
639 | file: const_src.file_id, | 614 | file: const_src.file_id, |
640 | ident_type: "Constant".to_string(), | 615 | ident_type: IdentType::Constant, |
641 | ident: AstPtr::new(&ast_ptr).into(), | 616 | ident: AstPtr::new(&ast_ptr).into(), |
642 | expected_case: replacement.expected_case, | 617 | expected_case: replacement.expected_case, |
643 | ident_text: replacement.current_name.to_string(), | 618 | ident_text: replacement.current_name.to_string(), |
@@ -685,7 +660,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
685 | 660 | ||
686 | let diagnostic = IncorrectCase { | 661 | let diagnostic = IncorrectCase { |
687 | file: static_src.file_id, | 662 | file: static_src.file_id, |
688 | ident_type: "Static variable".to_string(), | 663 | ident_type: IdentType::StaticVariable, |
689 | ident: AstPtr::new(&ast_ptr).into(), | 664 | ident: AstPtr::new(&ast_ptr).into(), |
690 | expected_case: replacement.expected_case, | 665 | expected_case: replacement.expected_case, |
691 | ident_text: replacement.current_name.to_string(), | 666 | ident_text: replacement.current_name.to_string(), |
@@ -696,22 +671,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
696 | } | 671 | } |
697 | } | 672 | } |
698 | 673 | ||
699 | fn names_equal(left: Option<ast::Name>, right: &Name) -> bool { | ||
700 | if let Some(left) = left { | ||
701 | &left.as_name() == right | ||
702 | } else { | ||
703 | false | ||
704 | } | ||
705 | } | ||
706 | |||
707 | fn pat_equals_to_name(pat: Option<ast::Pat>, name: &Name) -> bool { | ||
708 | if let Some(ast::Pat::IdentPat(ident)) = pat { | ||
709 | ident.to_string() == name.to_string() | ||
710 | } else { | ||
711 | false | ||
712 | } | ||
713 | } | ||
714 | |||
715 | #[cfg(test)] | 674 | #[cfg(test)] |
716 | mod tests { | 675 | mod tests { |
717 | use test_utils::mark; | 676 | use test_utils::mark; |
diff --git a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs index 14e4d92f0..3ab36caf2 100644 --- a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs +++ b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | // from file /compiler/rustc_lint/src/nonstandard_style.rs | 5 | // from file /compiler/rustc_lint/src/nonstandard_style.rs |
6 | 6 | ||
7 | /// Converts an identifier to an UpperCamelCase form. | 7 | /// Converts an identifier to an UpperCamelCase form. |
8 | /// Returns `None` if the string is already is UpperCamelCase. | 8 | /// Returns `None` if the string is already in UpperCamelCase. |
9 | pub(crate) fn to_camel_case(ident: &str) -> Option<String> { | 9 | pub(crate) fn to_camel_case(ident: &str) -> Option<String> { |
10 | if is_camel_case(ident) { | 10 | if is_camel_case(ident) { |
11 | return None; | 11 | return None; |
@@ -17,7 +17,7 @@ pub(crate) fn to_camel_case(ident: &str) -> Option<String> { | |||
17 | .split('_') | 17 | .split('_') |
18 | .filter(|component| !component.is_empty()) | 18 | .filter(|component| !component.is_empty()) |
19 | .map(|component| { | 19 | .map(|component| { |
20 | let mut camel_cased_component = String::new(); | 20 | let mut camel_cased_component = String::with_capacity(component.len()); |
21 | 21 | ||
22 | let mut new_word = true; | 22 | let mut new_word = true; |
23 | let mut prev_is_lower_case = true; | 23 | let mut prev_is_lower_case = true; |
@@ -30,9 +30,9 @@ pub(crate) fn to_camel_case(ident: &str) -> Option<String> { | |||
30 | } | 30 | } |
31 | 31 | ||
32 | if new_word { | 32 | if new_word { |
33 | camel_cased_component.push_str(&c.to_uppercase().to_string()); | 33 | camel_cased_component.extend(c.to_uppercase()); |
34 | } else { | 34 | } else { |
35 | camel_cased_component.push_str(&c.to_lowercase().to_string()); | 35 | camel_cased_component.extend(c.to_lowercase()); |
36 | } | 36 | } |
37 | 37 | ||
38 | prev_is_lower_case = c.is_lowercase(); | 38 | prev_is_lower_case = c.is_lowercase(); |
@@ -41,16 +41,16 @@ pub(crate) fn to_camel_case(ident: &str) -> Option<String> { | |||
41 | 41 | ||
42 | camel_cased_component | 42 | camel_cased_component |
43 | }) | 43 | }) |
44 | .fold((String::new(), None), |(acc, prev): (String, Option<String>), next| { | 44 | .fold((String::new(), None), |(acc, prev): (_, Option<String>), next| { |
45 | // separate two components with an underscore if their boundary cannot | 45 | // separate two components with an underscore if their boundary cannot |
46 | // be distinguished using a uppercase/lowercase case distinction | 46 | // be distinguished using a uppercase/lowercase case distinction |
47 | let join = if let Some(prev) = prev { | 47 | let join = prev |
48 | let l = prev.chars().last().unwrap(); | 48 | .and_then(|prev| { |
49 | let f = next.chars().next().unwrap(); | 49 | let f = next.chars().next()?; |
50 | !char_has_case(l) && !char_has_case(f) | 50 | let l = prev.chars().last()?; |
51 | } else { | 51 | Some(!char_has_case(l) && !char_has_case(f)) |
52 | false | 52 | }) |
53 | }; | 53 | .unwrap_or(false); |
54 | (acc + if join { "_" } else { "" } + &next, Some(next)) | 54 | (acc + if join { "_" } else { "" } + &next, Some(next)) |
55 | }) | 55 | }) |
56 | .0; | 56 | .0; |
@@ -92,14 +92,12 @@ fn is_camel_case(name: &str) -> bool { | |||
92 | let mut fst = None; | 92 | let mut fst = None; |
93 | // start with a non-lowercase letter rather than non-uppercase | 93 | // start with a non-lowercase letter rather than non-uppercase |
94 | // ones (some scripts don't have a concept of upper/lowercase) | 94 | // ones (some scripts don't have a concept of upper/lowercase) |
95 | !name.chars().next().unwrap().is_lowercase() | 95 | name.chars().next().map_or(true, |c| !c.is_lowercase()) |
96 | && !name.contains("__") | 96 | && !name.contains("__") |
97 | && !name.chars().any(|snd| { | 97 | && !name.chars().any(|snd| { |
98 | let ret = match (fst, snd) { | 98 | let ret = match fst { |
99 | (None, _) => false, | 99 | None => false, |
100 | (Some(fst), snd) => { | 100 | Some(fst) => char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_', |
101 | char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_' | ||
102 | } | ||
103 | }; | 101 | }; |
104 | fst = Some(snd); | 102 | fst = Some(snd); |
105 | 103 | ||
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index d08867c70..4b683c5a7 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -461,7 +461,7 @@ impl<'a> InferenceContext<'a> { | |||
461 | (ty, variant) | 461 | (ty, variant) |
462 | } | 462 | } |
463 | Some(1) => { | 463 | Some(1) => { |
464 | let segment = path.mod_path().segments.last().unwrap(); | 464 | let segment = path.mod_path().segments().last().unwrap(); |
465 | // this could be an enum variant or associated type | 465 | // this could be an enum variant or associated type |
466 | if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { | 466 | if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { |
467 | let enum_data = self.db.enum_data(enum_id); | 467 | let enum_data = self.db.enum_data(enum_id); |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index d7351d212..12f1591c8 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -137,7 +137,7 @@ impl<'a> InferenceContext<'a> { | |||
137 | 137 | ||
138 | self.coerce_merge_branch(&then_ty, &else_ty) | 138 | self.coerce_merge_branch(&then_ty, &else_ty) |
139 | } | 139 | } |
140 | Expr::Block { statements, tail, label } => match label { | 140 | Expr::Block { statements, tail, label, id: _ } => match label { |
141 | Some(_) => { | 141 | Some(_) => { |
142 | let break_ty = self.table.new_type_var(); | 142 | let break_ty = self.table.new_type_var(); |
143 | self.breakables.push(BreakableContext { | 143 | self.breakables.push(BreakableContext { |
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 0dcc4dd29..bc7aee110 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs | |||
@@ -24,7 +24,7 @@ pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { | |||
24 | } | 24 | } |
25 | 25 | ||
26 | segments.extend( | 26 | segments.extend( |
27 | path.segments | 27 | path.segments() |
28 | .iter() | 28 | .iter() |
29 | .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))), | 29 | .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))), |
30 | ); | 30 | ); |
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml index 43bc10490..ef0907194 100644 --- a/crates/mbe/Cargo.toml +++ b/crates/mbe/Cargo.toml | |||
@@ -17,5 +17,5 @@ log = "0.4.8" | |||
17 | syntax = { path = "../syntax", version = "0.0.0" } | 17 | syntax = { path = "../syntax", version = "0.0.0" } |
18 | parser = { path = "../parser", version = "0.0.0" } | 18 | parser = { path = "../parser", version = "0.0.0" } |
19 | tt = { path = "../tt", version = "0.0.0" } | 19 | tt = { path = "../tt", version = "0.0.0" } |
20 | test_utils = { path = "../test_utils" } | 20 | test_utils = { path = "../test_utils", version = "0.0.0" } |
21 | 21 | ||
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 07204436c..5a6501216 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -1729,7 +1729,7 @@ fn fill_resolve_data( | |||
1729 | ) -> Option<()> { | 1729 | ) -> Option<()> { |
1730 | let import_edit = item.import_to_add()?; | 1730 | let import_edit = item.import_to_add()?; |
1731 | let full_import_path = import_edit.import_path.to_string(); | 1731 | let full_import_path = import_edit.import_path.to_string(); |
1732 | let imported_name = import_edit.import_path.segments.clone().pop()?.to_string(); | 1732 | let imported_name = import_edit.import_path.segments().last()?.to_string(); |
1733 | 1733 | ||
1734 | *resolve_data = Some( | 1734 | *resolve_data = Some( |
1735 | to_value(CompletionResolveData { | 1735 | to_value(CompletionResolveData { |
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index 5866c0a28..d28b5e658 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml | |||
@@ -11,7 +11,7 @@ doctest = false | |||
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | backtrace = { version = "0.3.44", optional = true } | 13 | backtrace = { version = "0.3.44", optional = true } |
14 | always-assert = { version = "0.1.1", features = ["log"] } | 14 | always-assert = { version = "0.1.2", features = ["log"] } |
15 | # Think twice before adding anything here | 15 | # Think twice before adding anything here |
16 | 16 | ||
17 | [features] | 17 | [features] |
diff --git a/crates/syntax/fuzz/Cargo.toml b/crates/syntax/fuzz/Cargo.toml index 32c40d1b9..e22cd6b0c 100644 --- a/crates/syntax/fuzz/Cargo.toml +++ b/crates/syntax/fuzz/Cargo.toml | |||
@@ -10,8 +10,8 @@ edition = "2018" | |||
10 | cargo-fuzz = true | 10 | cargo-fuzz = true |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | syntax = { path = ".." } | 13 | syntax = { path = "..", version = "0.0.0" } |
14 | text_edit = { path = "../../text_edit" } | 14 | text_edit = { path = "../../text_edit", version = "0.0.0" } |
15 | libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } | 15 | libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } |
16 | 16 | ||
17 | # Prevent this from interfering with workspaces | 17 | # Prevent this from interfering with workspaces |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index b755c9692..1da5a125e 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -487,7 +487,7 @@ pub mod tokens { | |||
487 | use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken}; | 487 | use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken}; |
488 | 488 | ||
489 | pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = | 489 | pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = |
490 | Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2, !true)\n;\n\n")); | 490 | Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2, !true, *p)\n;\n\n")); |
491 | 491 | ||
492 | pub fn single_space() -> SyntaxToken { | 492 | pub fn single_space() -> SyntaxToken { |
493 | SOURCE_FILE | 493 | SOURCE_FILE |