aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs65
-rw-r--r--crates/ra_ide_api/src/diagnostics.rs2
-rw-r--r--crates/ra_ide_api/src/inlay_hints.rs132
3 files changed, 127 insertions, 72 deletions
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs
index 536ba36df..d43ff2eec 100644
--- a/crates/ra_ide_api/src/completion/complete_dot.rs
+++ b/crates/ra_ide_api/src/completion/complete_dot.rs
@@ -1,19 +1,36 @@
1use hir::{AdtDef, Ty, TypeCtor}; 1use hir::{AdtDef, Ty, TypeCtor};
2 2
3use crate::completion::{CompletionContext, Completions}; 3use crate::completion::completion_item::CompletionKind;
4use crate::{
5 completion::{completion_context::CompletionContext, completion_item::Completions},
6 CompletionItem,
7};
4use rustc_hash::FxHashSet; 8use rustc_hash::FxHashSet;
5 9
6/// Complete dot accesses, i.e. fields or methods (currently only fields). 10/// Complete dot accesses, i.e. fields or methods (and .await syntax).
7pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 11pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
8 let receiver_ty = 12 let dot_receiver = match &ctx.dot_receiver {
9 match ctx.dot_receiver.as_ref().and_then(|it| ctx.analyzer.type_of(ctx.db, it)) { 13 Some(expr) => expr,
10 Some(it) => it, 14 _ => return,
11 None => return, 15 };
12 }; 16
17 let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) {
18 Some(ty) => ty,
19 _ => return,
20 };
21
13 if !ctx.is_call { 22 if !ctx.is_call {
14 complete_fields(acc, ctx, receiver_ty.clone()); 23 complete_fields(acc, ctx, receiver_ty.clone());
15 } 24 }
16 complete_methods(acc, ctx, receiver_ty); 25 complete_methods(acc, ctx, receiver_ty.clone());
26
27 // Suggest .await syntax for types that implement Future trait
28 if ctx.analyzer.impls_future(ctx.db, receiver_ty) {
29 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
30 .detail("expr.await")
31 .insert_text("await")
32 .add_to(acc);
33 }
17} 34}
18 35
19fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { 36fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
@@ -406,4 +423,36 @@ mod tests {
406 "### 423 "###
407 ); 424 );
408 } 425 }
426
427 #[test]
428 fn test_completion_await_impls_future() {
429 assert_debug_snapshot_matches!(
430 do_completion(
431 r###"
432 //- /main.rs
433 use std::future::*;
434 struct A {}
435 impl Future for A {}
436 fn foo(a: A) {
437 a.<|>
438 }
439
440 //- /std/lib.rs
441 pub mod future {
442 pub trait Future {}
443 }
444 "###, CompletionKind::Keyword),
445 @r###"
446 ⋮[
447 ⋮ CompletionItem {
448 ⋮ label: "await",
449 ⋮ source_range: [74; 74),
450 ⋮ delete: [74; 74),
451 ⋮ insert: "await",
452 ⋮ detail: "expr.await",
453 ⋮ },
454 ⋮]
455 "###
456 )
457 }
409} 458}
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs
index 028dc3d4f..98b840b26 100644
--- a/crates/ra_ide_api/src/diagnostics.rs
+++ b/crates/ra_ide_api/src/diagnostics.rs
@@ -48,7 +48,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
48 }) 48 })
49 }) 49 })
50 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 50 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
51 let source_root = db.file_source_root(d.file().original_file(db)); 51 let source_root = db.file_source_root(d.source().file_id.original_file(db));
52 let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() }; 52 let create_file = FileSystemEdit::CreateFile { source_root, path: d.candidate.clone() };
53 let fix = SourceChange::file_system_edit("create module", create_file); 53 let fix = SourceChange::file_system_edit("create module", create_file);
54 res.borrow_mut().push(Diagnostic { 54 res.borrow_mut().push(Diagnostic {
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs
index a524e014f..0b3c96d26 100644
--- a/crates/ra_ide_api/src/inlay_hints.rs
+++ b/crates/ra_ide_api/src/inlay_hints.rs
@@ -9,14 +9,9 @@ use ra_syntax::{
9 SmolStr, SyntaxKind, SyntaxNode, TextRange, 9 SmolStr, SyntaxKind, SyntaxNode, TextRange,
10}; 10};
11 11
12#[derive(Debug, PartialEq, Eq, Clone)] 12#[derive(Debug, PartialEq, Eq)]
13pub enum InlayKind { 13pub enum InlayKind {
14 LetBindingType, 14 TypeHint,
15 ClosureParameterType,
16 ForExpressionBindingType,
17 IfExpressionType,
18 WhileLetExpressionType,
19 MatchArmType,
20} 15}
21 16
22#[derive(Debug)] 17#[derive(Debug)]
@@ -46,7 +41,7 @@ fn get_inlay_hints(
46 } 41 }
47 let pat = let_statement.pat()?; 42 let pat = let_statement.pat()?;
48 let analyzer = SourceAnalyzer::new(db, file_id, let_statement.syntax(), None); 43 let analyzer = SourceAnalyzer::new(db, file_id, let_statement.syntax(), None);
49 Some(get_pat_hints(db, &analyzer, pat, InlayKind::LetBindingType, false)) 44 Some(get_pat_type_hints(db, &analyzer, pat, false))
50 }) 45 })
51 .visit(|closure_parameter: LambdaExpr| { 46 .visit(|closure_parameter: LambdaExpr| {
52 let analyzer = SourceAnalyzer::new(db, file_id, closure_parameter.syntax(), None); 47 let analyzer = SourceAnalyzer::new(db, file_id, closure_parameter.syntax(), None);
@@ -55,15 +50,7 @@ fn get_inlay_hints(
55 .params() 50 .params()
56 .filter(|closure_param| closure_param.ascribed_type().is_none()) 51 .filter(|closure_param| closure_param.ascribed_type().is_none())
57 .filter_map(|closure_param| closure_param.pat()) 52 .filter_map(|closure_param| closure_param.pat())
58 .map(|root_pat| { 53 .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false))
59 get_pat_hints(
60 db,
61 &analyzer,
62 root_pat,
63 InlayKind::ClosureParameterType,
64 false,
65 )
66 })
67 .flatten() 54 .flatten()
68 .collect() 55 .collect()
69 }) 56 })
@@ -71,17 +58,17 @@ fn get_inlay_hints(
71 .visit(|for_expression: ForExpr| { 58 .visit(|for_expression: ForExpr| {
72 let pat = for_expression.pat()?; 59 let pat = for_expression.pat()?;
73 let analyzer = SourceAnalyzer::new(db, file_id, for_expression.syntax(), None); 60 let analyzer = SourceAnalyzer::new(db, file_id, for_expression.syntax(), None);
74 Some(get_pat_hints(db, &analyzer, pat, InlayKind::ForExpressionBindingType, false)) 61 Some(get_pat_type_hints(db, &analyzer, pat, false))
75 }) 62 })
76 .visit(|if_expr: IfExpr| { 63 .visit(|if_expr: IfExpr| {
77 let pat = if_expr.condition()?.pat()?; 64 let pat = if_expr.condition()?.pat()?;
78 let analyzer = SourceAnalyzer::new(db, file_id, if_expr.syntax(), None); 65 let analyzer = SourceAnalyzer::new(db, file_id, if_expr.syntax(), None);
79 Some(get_pat_hints(db, &analyzer, pat, InlayKind::IfExpressionType, true)) 66 Some(get_pat_type_hints(db, &analyzer, pat, true))
80 }) 67 })
81 .visit(|while_expr: WhileExpr| { 68 .visit(|while_expr: WhileExpr| {
82 let pat = while_expr.condition()?.pat()?; 69 let pat = while_expr.condition()?.pat()?;
83 let analyzer = SourceAnalyzer::new(db, file_id, while_expr.syntax(), None); 70 let analyzer = SourceAnalyzer::new(db, file_id, while_expr.syntax(), None);
84 Some(get_pat_hints(db, &analyzer, pat, InlayKind::WhileLetExpressionType, true)) 71 Some(get_pat_type_hints(db, &analyzer, pat, true))
85 }) 72 })
86 .visit(|match_arm_list: MatchArmList| { 73 .visit(|match_arm_list: MatchArmList| {
87 let analyzer = SourceAnalyzer::new(db, file_id, match_arm_list.syntax(), None); 74 let analyzer = SourceAnalyzer::new(db, file_id, match_arm_list.syntax(), None);
@@ -90,9 +77,7 @@ fn get_inlay_hints(
90 .arms() 77 .arms()
91 .map(|match_arm| match_arm.pats()) 78 .map(|match_arm| match_arm.pats())
92 .flatten() 79 .flatten()
93 .map(|root_pat| { 80 .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true))
94 get_pat_hints(db, &analyzer, root_pat, InlayKind::MatchArmType, true)
95 })
96 .flatten() 81 .flatten()
97 .collect(), 82 .collect(),
98 ) 83 )
@@ -100,11 +85,10 @@ fn get_inlay_hints(
100 .accept(&node)? 85 .accept(&node)?
101} 86}
102 87
103fn get_pat_hints( 88fn get_pat_type_hints(
104 db: &RootDatabase, 89 db: &RootDatabase,
105 analyzer: &SourceAnalyzer, 90 analyzer: &SourceAnalyzer,
106 root_pat: Pat, 91 root_pat: Pat,
107 kind: InlayKind,
108 skip_root_pat_hint: bool, 92 skip_root_pat_hint: bool,
109) -> Vec<InlayHint> { 93) -> Vec<InlayHint> {
110 let original_pat = &root_pat.clone(); 94 let original_pat = &root_pat.clone();
@@ -118,7 +102,7 @@ fn get_pat_hints(
118 }) 102 })
119 .map(|(range, pat_type)| InlayHint { 103 .map(|(range, pat_type)| InlayHint {
120 range, 104 range,
121 kind: kind.clone(), 105 kind: InlayKind::TypeHint,
122 label: pat_type.display(db).to_string().into(), 106 label: pat_type.display(db).to_string().into(),
123 }) 107 })
124 .collect() 108 .collect()
@@ -232,52 +216,52 @@ fn main() {
232 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 216 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
233 InlayHint { 217 InlayHint {
234 range: [193; 197), 218 range: [193; 197),
235 kind: LetBindingType, 219 kind: TypeHint,
236 label: "i32", 220 label: "i32",
237 }, 221 },
238 InlayHint { 222 InlayHint {
239 range: [236; 244), 223 range: [236; 244),
240 kind: LetBindingType, 224 kind: TypeHint,
241 label: "i32", 225 label: "i32",
242 }, 226 },
243 InlayHint { 227 InlayHint {
244 range: [275; 279), 228 range: [275; 279),
245 kind: LetBindingType, 229 kind: TypeHint,
246 label: "&str", 230 label: "&str",
247 }, 231 },
248 InlayHint { 232 InlayHint {
249 range: [539; 543), 233 range: [539; 543),
250 kind: LetBindingType, 234 kind: TypeHint,
251 label: "(i32, char)", 235 label: "(i32, char)",
252 }, 236 },
253 InlayHint { 237 InlayHint {
254 range: [566; 567), 238 range: [566; 567),
255 kind: LetBindingType, 239 kind: TypeHint,
256 label: "i32", 240 label: "i32",
257 }, 241 },
258 InlayHint { 242 InlayHint {
259 range: [570; 571), 243 range: [570; 571),
260 kind: LetBindingType, 244 kind: TypeHint,
261 label: "i32", 245 label: "i32",
262 }, 246 },
263 InlayHint { 247 InlayHint {
264 range: [573; 574), 248 range: [573; 574),
265 kind: LetBindingType, 249 kind: TypeHint,
266 label: "i32", 250 label: "i32",
267 }, 251 },
268 InlayHint { 252 InlayHint {
269 range: [584; 585), 253 range: [584; 585),
270 kind: LetBindingType, 254 kind: TypeHint,
271 label: "i32", 255 label: "i32",
272 }, 256 },
273 InlayHint { 257 InlayHint {
274 range: [577; 578), 258 range: [577; 578),
275 kind: LetBindingType, 259 kind: TypeHint,
276 label: "f64", 260 label: "f64",
277 }, 261 },
278 InlayHint { 262 InlayHint {
279 range: [580; 581), 263 range: [580; 581),
280 kind: LetBindingType, 264 kind: TypeHint,
281 label: "f64", 265 label: "f64",
282 }, 266 },
283]"# 267]"#
@@ -299,12 +283,12 @@ fn main() {
299 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 283 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
300 InlayHint { 284 InlayHint {
301 range: [21; 30), 285 range: [21; 30),
302 kind: LetBindingType, 286 kind: TypeHint,
303 label: "i32", 287 label: "i32",
304 }, 288 },
305 InlayHint { 289 InlayHint {
306 range: [57; 66), 290 range: [57; 66),
307 kind: ClosureParameterType, 291 kind: TypeHint,
308 label: "i32", 292 label: "i32",
309 }, 293 },
310]"# 294]"#
@@ -326,12 +310,12 @@ fn main() {
326 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 310 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
327 InlayHint { 311 InlayHint {
328 range: [21; 30), 312 range: [21; 30),
329 kind: LetBindingType, 313 kind: TypeHint,
330 label: "i32", 314 label: "i32",
331 }, 315 },
332 InlayHint { 316 InlayHint {
333 range: [44; 53), 317 range: [44; 53),
334 kind: ForExpressionBindingType, 318 kind: TypeHint,
335 label: "i32", 319 label: "i32",
336 }, 320 },
337]"# 321]"#
@@ -364,7 +348,7 @@ fn main() {
364 if let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {}; 348 if let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {};
365 if let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {}; 349 if let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {};
366 if let CustomOption::Some(Test { b: y, .. }) = &test {}; 350 if let CustomOption::Some(Test { b: y, .. }) = &test {};
367 351
368 if test == CustomOption::None {} 352 if test == CustomOption::None {}
369}"#, 353}"#,
370 ); 354 );
@@ -372,27 +356,27 @@ fn main() {
372 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 356 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
373 InlayHint { 357 InlayHint {
374 range: [166; 170), 358 range: [166; 170),
375 kind: LetBindingType, 359 kind: TypeHint,
376 label: "CustomOption<Test>", 360 label: "CustomOption<Test>",
377 }, 361 },
378 InlayHint { 362 InlayHint {
379 range: [334; 338), 363 range: [334; 338),
380 kind: IfExpressionType, 364 kind: TypeHint,
381 label: "&Test", 365 label: "&Test",
382 }, 366 },
383 InlayHint { 367 InlayHint {
384 range: [389; 390), 368 range: [389; 390),
385 kind: IfExpressionType, 369 kind: TypeHint,
386 label: "&CustomOption<u32>", 370 label: "&CustomOption<u32>",
387 }, 371 },
388 InlayHint { 372 InlayHint {
389 range: [392; 393), 373 range: [392; 393),
390 kind: IfExpressionType, 374 kind: TypeHint,
391 label: "&u8", 375 label: "&u8",
392 }, 376 },
393 InlayHint { 377 InlayHint {
394 range: [531; 532), 378 range: [531; 532),
395 kind: IfExpressionType, 379 kind: TypeHint,
396 label: "&u32", 380 label: "&u32",
397 }, 381 },
398]"# 382]"#
@@ -425,18 +409,40 @@ fn main() {
425 while let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {}; 409 while let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {};
426 while let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {}; 410 while let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {};
427 while let CustomOption::Some(Test { b: y, .. }) = &test {}; 411 while let CustomOption::Some(Test { b: y, .. }) = &test {};
428 412
429 while test == CustomOption::None {} 413 while test == CustomOption::None {}
430}"#, 414}"#,
431 ); 415 );
432 416
433 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 417 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r###"
434 InlayHint { 418 ⋮[
435 range: [166; 170), 419 ⋮ InlayHint {
436 kind: LetBindingType, 420 ⋮ range: [166; 170),
437 label: "CustomOption<Test>", 421 ⋮ kind: TypeHint,
438 }, 422 ⋮ label: "CustomOption<Test>",
439]"# 423 ⋮ },
424 ⋮ InlayHint {
425 ⋮ range: [343; 347),
426 ⋮ kind: TypeHint,
427 ⋮ label: "&Test",
428 ⋮ },
429 ⋮ InlayHint {
430 ⋮ range: [401; 402),
431 ⋮ kind: TypeHint,
432 ⋮ label: "&CustomOption<u32>",
433 ⋮ },
434 ⋮ InlayHint {
435 ⋮ range: [404; 405),
436 ⋮ kind: TypeHint,
437 ⋮ label: "&u8",
438 ⋮ },
439 ⋮ InlayHint {
440 ⋮ range: [549; 550),
441 ⋮ kind: TypeHint,
442 ⋮ label: "&u32",
443 ⋮ },
444 ⋮]
445 "###
440 ); 446 );
441 } 447 }
442 448
@@ -445,7 +451,7 @@ fn main() {
445 let (analysis, file_id) = single_file( 451 let (analysis, file_id) = single_file(
446 r#" 452 r#"
447#[derive(PartialEq)] 453#[derive(PartialEq)]
448enum CustomOption<T> { 454enum CustomOption<T> {
449 None, 455 None,
450 Some(T), 456 Some(T),
451} 457}
@@ -473,23 +479,23 @@ fn main() {
473 479
474 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ 480 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
475 InlayHint { 481 InlayHint {
476 range: [312; 316), 482 range: [311; 315),
477 kind: MatchArmType, 483 kind: TypeHint,
478 label: "Test", 484 label: "Test",
479 }, 485 },
480 InlayHint { 486 InlayHint {
481 range: [359; 360), 487 range: [358; 359),
482 kind: MatchArmType, 488 kind: TypeHint,
483 label: "CustomOption<u32>", 489 label: "CustomOption<u32>",
484 }, 490 },
485 InlayHint { 491 InlayHint {
486 range: [362; 363), 492 range: [361; 362),
487 kind: MatchArmType, 493 kind: TypeHint,
488 label: "u8", 494 label: "u8",
489 }, 495 },
490 InlayHint { 496 InlayHint {
491 range: [485; 486), 497 range: [484; 485),
492 kind: MatchArmType, 498 kind: TypeHint,
493 label: "u32", 499 label: "u32",
494 }, 500 },
495]"# 501]"#