aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--crates/base_db/Cargo.toml2
-rw-r--r--crates/base_db/src/cancellation.rs48
-rw-r--r--crates/base_db/src/lib.rs45
-rw-r--r--crates/hir/src/diagnostics.rs24
-rw-r--r--crates/hir/src/lib.rs12
-rw-r--r--crates/hir/src/semantics.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs52
-rw-r--r--crates/hir_def/src/nameres/diagnostics.rs9
-rw-r--r--crates/hir_def/src/test_db.rs7
-rw-r--r--crates/hir_ty/src/infer/expr.rs2
-rw-r--r--crates/hir_ty/src/traits.rs2
-rw-r--r--crates/ide/src/diagnostics.rs5
-rw-r--r--crates/ide/src/lib.rs128
-rw-r--r--crates/ide_completion/src/completions.rs17
-rw-r--r--crates/ide_completion/src/completions/dot.rs84
-rw-r--r--crates/ide_completion/src/completions/record.rs2
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs2
-rw-r--r--crates/ide_completion/src/config.rs1
-rw-r--r--crates/ide_completion/src/render.rs30
-rw-r--r--crates/ide_completion/src/render/function.rs36
-rw-r--r--crates/ide_completion/src/test_utils.rs1
-rw-r--r--crates/ide_db/src/lib.rs20
-rw-r--r--crates/ide_db/src/symbol_index.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--crates/rust-analyzer/src/dispatch.rs4
-rw-r--r--crates/rust-analyzer/src/global_state.rs4
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs6
-rw-r--r--crates/rust-analyzer/src/main_loop.rs4
-rw-r--r--crates/rust-analyzer/src/to_proto.rs7
-rw-r--r--docs/dev/architecture.md5
-rw-r--r--docs/user/generated_config.adoc6
-rw-r--r--editors/code/package.json5
34 files changed, 355 insertions, 233 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 15ccf4146..ee04cdce8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1375,9 +1375,9 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
1375 1375
1376[[package]] 1376[[package]]
1377name = "salsa" 1377name = "salsa"
1378version = "0.16.1" 1378version = "0.17.0-pre.1"
1379source = "registry+https://github.com/rust-lang/crates.io-index" 1379source = "registry+https://github.com/rust-lang/crates.io-index"
1380checksum = "4b84d9f96071f3f3be0dc818eae3327625d8ebc95b58da37d6850724f31d3403" 1380checksum = "58038261ea8cd5a7730c4d8c97a22063d7c7eb1c2809e55c3c15f0a5903e5582"
1381dependencies = [ 1381dependencies = [
1382 "crossbeam-utils", 1382 "crossbeam-utils",
1383 "indexmap", 1383 "indexmap",
@@ -1392,9 +1392,9 @@ dependencies = [
1392 1392
1393[[package]] 1393[[package]]
1394name = "salsa-macros" 1394name = "salsa-macros"
1395version = "0.16.0" 1395version = "0.17.0-pre.1"
1396source = "registry+https://github.com/rust-lang/crates.io-index" 1396source = "registry+https://github.com/rust-lang/crates.io-index"
1397checksum = "cd3904a4ba0a9d0211816177fd34b04c7095443f8cdacd11175064fe541c8fe2" 1397checksum = "2e2fc060627fa5d44bffac98f6089b9497779e2deccc26687f60adc2638e32fb"
1398dependencies = [ 1398dependencies = [
1399 "heck", 1399 "heck",
1400 "proc-macro2", 1400 "proc-macro2",
diff --git a/crates/base_db/Cargo.toml b/crates/base_db/Cargo.toml
index 1724d2f85..69173ad1f 100644
--- a/crates/base_db/Cargo.toml
+++ b/crates/base_db/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13salsa = "0.16.0" 13salsa = "0.17.0-pre.1"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15 15
16syntax = { path = "../syntax", version = "0.0.0" } 16syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/base_db/src/cancellation.rs b/crates/base_db/src/cancellation.rs
deleted file mode 100644
index 7420a1976..000000000
--- a/crates/base_db/src/cancellation.rs
+++ /dev/null
@@ -1,48 +0,0 @@
1//! Utility types to support cancellation.
2//!
3//! In a typical IDE use-case, requests and modification happen concurrently, as
4//! in the following scenario:
5//!
6//! * user types a character,
7//! * a syntax highlighting process is started
8//! * user types next character, while syntax highlighting *is still in
9//! progress*.
10//!
11//! In this situation, we want to react to modification as quickly as possible.
12//! At the same time, in-progress results are not very interesting, because they
13//! are invalidated by the edit anyway. So, we first cancel all in-flight
14//! requests, and then apply modification knowing that it won't interfere with
15//! any background processing (this bit is handled by salsa, see the
16//! `BaseDatabase::check_canceled` method).
17
18/// An "error" signifying that the operation was canceled.
19#[derive(Clone, PartialEq, Eq, Hash)]
20pub struct Canceled {
21 _private: (),
22}
23
24impl Canceled {
25 pub(crate) fn new() -> Canceled {
26 Canceled { _private: () }
27 }
28
29 pub fn throw() -> ! {
30 // We use resume and not panic here to avoid running the panic
31 // hook (that is, to avoid collecting and printing backtrace).
32 std::panic::resume_unwind(Box::new(Canceled::new()))
33 }
34}
35
36impl std::fmt::Display for Canceled {
37 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 fmt.write_str("canceled")
39 }
40}
41
42impl std::fmt::Debug for Canceled {
43 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 write!(fmt, "Canceled")
45 }
46}
47
48impl std::error::Error for Canceled {}
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs
index 980a0ed98..62bf2a4b2 100644
--- a/crates/base_db/src/lib.rs
+++ b/crates/base_db/src/lib.rs
@@ -1,5 +1,4 @@
1//! base_db defines basic database traits. The concrete DB is defined by ide. 1//! base_db defines basic database traits. The concrete DB is defined by ide.
2mod cancellation;
3mod input; 2mod input;
4mod change; 3mod change;
5pub mod fixture; 4pub mod fixture;
@@ -10,14 +9,13 @@ use rustc_hash::FxHashSet;
10use syntax::{ast, Parse, SourceFile, TextRange, TextSize}; 9use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
11 10
12pub use crate::{ 11pub use crate::{
13 cancellation::Canceled,
14 change::Change, 12 change::Change,
15 input::{ 13 input::{
16 CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, 14 CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env,
17 ProcMacro, ProcMacroExpander, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId, 15 ProcMacro, ProcMacroExpander, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId,
18 }, 16 },
19}; 17};
20pub use salsa; 18pub use salsa::{self, Cancelled};
21pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath}; 19pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath};
22 20
23#[macro_export] 21#[macro_export]
@@ -38,45 +36,6 @@ pub trait Upcast<T: ?Sized> {
38 fn upcast(&self) -> &T; 36 fn upcast(&self) -> &T;
39} 37}
40 38
41pub trait CheckCanceled {
42 /// Aborts current query if there are pending changes.
43 ///
44 /// rust-analyzer needs to be able to answer semantic questions about the
45 /// code while the code is being modified. A common problem is that a
46 /// long-running query is being calculated when a new change arrives.
47 ///
48 /// We can't just apply the change immediately: this will cause the pending
49 /// query to see inconsistent state (it will observe an absence of
50 /// repeatable read). So what we do is we **cancel** all pending queries
51 /// before applying the change.
52 ///
53 /// We implement cancellation by panicking with a special value and catching
54 /// it on the API boundary. Salsa explicitly supports this use-case.
55 fn check_canceled(&self);
56
57 fn catch_canceled<F, T>(&self, f: F) -> Result<T, Canceled>
58 where
59 Self: Sized + panic::RefUnwindSafe,
60 F: FnOnce(&Self) -> T + panic::UnwindSafe,
61 {
62 // Uncomment to debug missing cancellations.
63 // let _span = profile::heartbeat_span();
64 panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::<Canceled>() {
65 Ok(canceled) => *canceled,
66 Err(payload) => panic::resume_unwind(payload),
67 })
68 }
69}
70
71impl<T: salsa::Database> CheckCanceled for T {
72 fn check_canceled(&self) {
73 // profile::heartbeat();
74 if self.salsa_runtime().is_current_revision_canceled() {
75 Canceled::throw()
76 }
77 }
78}
79
80#[derive(Clone, Copy, Debug)] 39#[derive(Clone, Copy, Debug)]
81pub struct FilePosition { 40pub struct FilePosition {
82 pub file_id: FileId, 41 pub file_id: FileId,
@@ -101,7 +60,7 @@ pub trait FileLoader {
101/// Database which stores all significant input facts: source code and project 60/// Database which stores all significant input facts: source code and project
102/// model. Everything else in rust-analyzer is derived from these queries. 61/// model. Everything else in rust-analyzer is derived from these queries.
103#[salsa::query_group(SourceDatabaseStorage)] 62#[salsa::query_group(SourceDatabaseStorage)]
104pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug { 63pub trait SourceDatabase: FileLoader + std::fmt::Debug {
105 // Parses the file into the syntax tree. 64 // Parses the file into the syntax tree.
106 #[salsa::invoke(parse_query)] 65 #[salsa::invoke(parse_query)]
107 fn parse(&self, file_id: FileId) -> Parse<ast::SourceFile>; 66 fn parse(&self, file_id: FileId) -> Parse<ast::SourceFile>;
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 22ec7c6ac..2cdbd172a 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -227,3 +227,27 @@ impl Diagnostic for MacroError {
227 true 227 true
228 } 228 }
229} 229}
230
231#[derive(Debug)]
232pub struct UnimplementedBuiltinMacro {
233 pub file: HirFileId,
234 pub node: SyntaxNodePtr,
235}
236
237impl Diagnostic for UnimplementedBuiltinMacro {
238 fn code(&self) -> DiagnosticCode {
239 DiagnosticCode("unimplemented-builtin-macro")
240 }
241
242 fn message(&self) -> String {
243 "unimplemented built-in macro".to_string()
244 }
245
246 fn display_source(&self) -> InFile<SyntaxNodePtr> {
247 InFile::new(self.file, self.node.clone())
248 }
249
250 fn as_any(&self) -> &(dyn Any + Send + 'static) {
251 self
252 }
253}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 975ae4869..d3ef29db4 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -36,8 +36,8 @@ use std::{iter, sync::Arc};
36use arrayvec::ArrayVec; 36use arrayvec::ArrayVec;
37use base_db::{CrateDisplayName, CrateId, Edition, FileId}; 37use base_db::{CrateDisplayName, CrateId, Edition, FileId};
38use diagnostics::{ 38use diagnostics::{
39 InactiveCode, MacroError, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, 39 InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport,
40 UnresolvedModule, UnresolvedProcMacro, 40 UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
41}; 41};
42use either::Either; 42use either::Either;
43use hir_def::{ 43use hir_def::{
@@ -565,6 +565,14 @@ impl Module {
565 }; 565 };
566 sink.push(MacroError { file, node: ast, message: message.clone() }); 566 sink.push(MacroError { file, node: ast, message: message.clone() });
567 } 567 }
568
569 DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
570 let node = ast.to_node(db.upcast());
571 // Must have a name, otherwise we wouldn't emit it.
572 let name = node.name().expect("unimplemented builtin macro with no name");
573 let ptr = SyntaxNodePtr::from(AstPtr::new(&name));
574 sink.push(UnimplementedBuiltinMacro { file: ast.file_id, node: ptr });
575 }
568 } 576 }
569 } 577 }
570 for decl in self.declarations(db) { 578 for decl in self.declarations(db) {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 8d3c43d08..c7f2c02e4 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -361,7 +361,7 @@ impl<'db> SemanticsImpl<'db> {
361 let sa = self.analyze(&parent); 361 let sa = self.analyze(&parent);
362 362
363 let token = successors(Some(InFile::new(sa.file_id, token)), |token| { 363 let token = successors(Some(InFile::new(sa.file_id, token)), |token| {
364 self.db.check_canceled(); 364 self.db.unwind_if_cancelled();
365 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; 365 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
366 let tt = macro_call.token_tree()?; 366 let tt = macro_call.token_tree()?;
367 if !tt.syntax().text_range().contains_range(token.value.text_range()) { 367 if !tt.syntax().text_range().contains_range(token.value.text_range()) {
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index d9d6c91a8..7f9fdb379 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -351,7 +351,7 @@ impl DefCollector<'_> {
351 let mut i = 0; 351 let mut i = 0;
352 'outer: loop { 352 'outer: loop {
353 loop { 353 loop {
354 self.db.check_canceled(); 354 self.db.unwind_if_cancelled();
355 loop { 355 loop {
356 if self.resolve_imports() == ReachedFixedPoint::Yes { 356 if self.resolve_imports() == ReachedFixedPoint::Yes {
357 break; 357 break;
@@ -836,7 +836,7 @@ impl DefCollector<'_> {
836 vis: Visibility, 836 vis: Visibility,
837 import_type: ImportType, 837 import_type: ImportType,
838 ) { 838 ) {
839 self.db.check_canceled(); 839 self.db.unwind_if_cancelled();
840 self.update_recursive(module_id, resolutions, vis, import_type, 0) 840 self.update_recursive(module_id, resolutions, vis, import_type, 0)
841 } 841 }
842 842
@@ -1679,14 +1679,22 @@ impl ModCollector<'_, '_> {
1679 None => &mac.name, 1679 None => &mac.name,
1680 }; 1680 };
1681 let krate = self.def_collector.def_map.krate; 1681 let krate = self.def_collector.def_map.krate;
1682 if let Some(macro_id) = find_builtin_macro(name, krate, ast_id) { 1682 match find_builtin_macro(name, krate, ast_id) {
1683 self.def_collector.define_macro_rules( 1683 Some(macro_id) => {
1684 self.module_id, 1684 self.def_collector.define_macro_rules(
1685 mac.name.clone(), 1685 self.module_id,
1686 macro_id, 1686 mac.name.clone(),
1687 is_export, 1687 macro_id,
1688 ); 1688 is_export,
1689 return; 1689 );
1690 return;
1691 }
1692 None => {
1693 self.def_collector
1694 .def_map
1695 .diagnostics
1696 .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, ast_id));
1697 }
1690 } 1698 }
1691 } 1699 }
1692 1700
@@ -1715,15 +1723,23 @@ impl ModCollector<'_, '_> {
1715 let macro_id = find_builtin_macro(&mac.name, krate, ast_id) 1723 let macro_id = find_builtin_macro(&mac.name, krate, ast_id)
1716 .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)); 1724 .or_else(|| find_builtin_derive(&mac.name, krate, ast_id));
1717 1725
1718 if let Some(macro_id) = macro_id { 1726 match macro_id {
1719 self.def_collector.define_macro_def( 1727 Some(macro_id) => {
1720 self.module_id, 1728 self.def_collector.define_macro_def(
1721 mac.name.clone(), 1729 self.module_id,
1722 macro_id, 1730 mac.name.clone(),
1723 &self.item_tree[mac.visibility], 1731 macro_id,
1724 ); 1732 &self.item_tree[mac.visibility],
1733 );
1734 return;
1735 }
1736 None => {
1737 self.def_collector
1738 .def_map
1739 .diagnostics
1740 .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, ast_id));
1741 }
1725 } 1742 }
1726 return;
1727 } 1743 }
1728 1744
1729 // Case 2: normal `macro` 1745 // Case 2: normal `macro`
diff --git a/crates/hir_def/src/nameres/diagnostics.rs b/crates/hir_def/src/nameres/diagnostics.rs
index 57c36c3c6..95061f601 100644
--- a/crates/hir_def/src/nameres/diagnostics.rs
+++ b/crates/hir_def/src/nameres/diagnostics.rs
@@ -27,6 +27,8 @@ pub enum DefDiagnosticKind {
27 UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath }, 27 UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath },
28 28
29 MacroError { ast: MacroCallKind, message: String }, 29 MacroError { ast: MacroCallKind, message: String },
30
31 UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
30} 32}
31 33
32#[derive(Debug, PartialEq, Eq)] 34#[derive(Debug, PartialEq, Eq)]
@@ -93,4 +95,11 @@ impl DefDiagnostic {
93 ) -> Self { 95 ) -> Self {
94 Self { in_module: container, kind: DefDiagnosticKind::UnresolvedMacroCall { ast, path } } 96 Self { in_module: container, kind: DefDiagnosticKind::UnresolvedMacroCall { ast, path } }
95 } 97 }
98
99 pub(super) fn unimplemented_builtin_macro(
100 container: LocalModuleId,
101 ast: AstId<ast::Macro>,
102 ) -> Self {
103 Self { in_module: container, kind: DefDiagnosticKind::UnimplementedBuiltinMacro { ast } }
104 }
96} 105}
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index a9c1e13e2..e840fe5e8 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -298,6 +298,13 @@ impl TestDB {
298 DefDiagnosticKind::MacroError { ast, message } => { 298 DefDiagnosticKind::MacroError { ast, message } => {
299 (ast.to_node(self.upcast()), message.as_str()) 299 (ast.to_node(self.upcast()), message.as_str())
300 } 300 }
301 DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
302 let node = ast.to_node(self.upcast());
303 (
304 InFile::new(ast.file_id, node.syntax().clone()),
305 "UnimplementedBuiltinMacro",
306 )
307 }
301 }; 308 };
302 309
303 let frange = node.as_ref().original_file_range(self); 310 let frange = node.as_ref().original_file_range(self);
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 97507305c..41ef45326 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -119,7 +119,7 @@ impl<'a> InferenceContext<'a> {
119 } 119 }
120 120
121 fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { 121 fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
122 self.db.check_canceled(); 122 self.db.unwind_if_cancelled();
123 123
124 let body = Arc::clone(&self.body); // avoid borrow checker problem 124 let body = Arc::clone(&self.body); // avoid borrow checker problem
125 let ty = match &body[tgt_expr] { 125 let ty = match &body[tgt_expr] {
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs
index 294cb531c..f589b314b 100644
--- a/crates/hir_ty/src/traits.rs
+++ b/crates/hir_ty/src/traits.rs
@@ -112,7 +112,7 @@ fn solve(
112 let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL); 112 let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL);
113 113
114 let should_continue = || { 114 let should_continue = || {
115 context.db.check_canceled(); 115 db.unwind_if_cancelled();
116 let remaining = fuel.get(); 116 let remaining = fuel.get();
117 fuel.set(remaining - 1); 117 fuel.set(remaining - 1);
118 if remaining == 0 { 118 if remaining == 0 {
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 6cf5810fa..d5c954b8b 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -182,6 +182,11 @@ pub(crate) fn diagnostics(
182 res.borrow_mut() 182 res.borrow_mut()
183 .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code()))); 183 .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code())));
184 }) 184 })
185 .on::<hir::diagnostics::UnimplementedBuiltinMacro, _>(|d| {
186 let display_range = sema.diagnostics_display_range(d.display_source()).range;
187 res.borrow_mut()
188 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
189 })
185 // Only collect experimental diagnostics when they're enabled. 190 // Only collect experimental diagnostics when they're enabled.
186 .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) 191 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
187 .filter(|diag| !config.disabled.contains(diag.code().as_str())); 192 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index ff2a54117..97c9e5d2b 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -58,7 +58,7 @@ use cfg::CfgOptions;
58 58
59use ide_db::base_db::{ 59use ide_db::base_db::{
60 salsa::{self, ParallelDatabase}, 60 salsa::{self, ParallelDatabase},
61 CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath, 61 Env, FileLoader, FileSet, SourceDatabase, VfsPath,
62}; 62};
63use ide_db::{ 63use ide_db::{
64 symbol_index::{self, FileSymbol}, 64 symbol_index::{self, FileSymbol},
@@ -98,7 +98,7 @@ pub use ide_completion::{
98}; 98};
99pub use ide_db::{ 99pub use ide_db::{
100 base_db::{ 100 base_db::{
101 Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, 101 Cancelled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange,
102 SourceRoot, SourceRootId, 102 SourceRoot, SourceRootId,
103 }, 103 },
104 call_info::CallInfo, 104 call_info::CallInfo,
@@ -113,7 +113,7 @@ pub use ide_ssr::SsrError;
113pub use syntax::{TextRange, TextSize}; 113pub use syntax::{TextRange, TextSize};
114pub use text_edit::{Indel, TextEdit}; 114pub use text_edit::{Indel, TextEdit};
115 115
116pub type Cancelable<T> = Result<T, Canceled>; 116pub type Cancellable<T> = Result<T, Cancelled>;
117 117
118/// Info associated with a text range. 118/// Info associated with a text range.
119#[derive(Debug)] 119#[derive(Debug)]
@@ -227,11 +227,11 @@ impl Analysis {
227 } 227 }
228 228
229 /// Debug info about the current state of the analysis. 229 /// Debug info about the current state of the analysis.
230 pub fn status(&self, file_id: Option<FileId>) -> Cancelable<String> { 230 pub fn status(&self, file_id: Option<FileId>) -> Cancellable<String> {
231 self.with_db(|db| status::status(&*db, file_id)) 231 self.with_db(|db| status::status(&*db, file_id))
232 } 232 }
233 233
234 pub fn prime_caches<F>(&self, cb: F) -> Cancelable<()> 234 pub fn prime_caches<F>(&self, cb: F) -> Cancellable<()>
235 where 235 where
236 F: Fn(PrimeCachesProgress) + Sync + std::panic::UnwindSafe, 236 F: Fn(PrimeCachesProgress) + Sync + std::panic::UnwindSafe,
237 { 237 {
@@ -239,35 +239,35 @@ impl Analysis {
239 } 239 }
240 240
241 /// Gets the text of the source file. 241 /// Gets the text of the source file.
242 pub fn file_text(&self, file_id: FileId) -> Cancelable<Arc<String>> { 242 pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<String>> {
243 self.with_db(|db| db.file_text(file_id)) 243 self.with_db(|db| db.file_text(file_id))
244 } 244 }
245 245
246 /// Gets the syntax tree of the file. 246 /// Gets the syntax tree of the file.
247 pub fn parse(&self, file_id: FileId) -> Cancelable<SourceFile> { 247 pub fn parse(&self, file_id: FileId) -> Cancellable<SourceFile> {
248 self.with_db(|db| db.parse(file_id).tree()) 248 self.with_db(|db| db.parse(file_id).tree())
249 } 249 }
250 250
251 /// Returns true if this file belongs to an immutable library. 251 /// Returns true if this file belongs to an immutable library.
252 pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> { 252 pub fn is_library_file(&self, file_id: FileId) -> Cancellable<bool> {
253 use ide_db::base_db::SourceDatabaseExt; 253 use ide_db::base_db::SourceDatabaseExt;
254 self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library) 254 self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
255 } 255 }
256 256
257 /// Gets the file's `LineIndex`: data structure to convert between absolute 257 /// Gets the file's `LineIndex`: data structure to convert between absolute
258 /// offsets and line/column representation. 258 /// offsets and line/column representation.
259 pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { 259 pub fn file_line_index(&self, file_id: FileId) -> Cancellable<Arc<LineIndex>> {
260 self.with_db(|db| db.line_index(file_id)) 260 self.with_db(|db| db.line_index(file_id))
261 } 261 }
262 262
263 /// Selects the next syntactic nodes encompassing the range. 263 /// Selects the next syntactic nodes encompassing the range.
264 pub fn extend_selection(&self, frange: FileRange) -> Cancelable<TextRange> { 264 pub fn extend_selection(&self, frange: FileRange) -> Cancellable<TextRange> {
265 self.with_db(|db| extend_selection::extend_selection(db, frange)) 265 self.with_db(|db| extend_selection::extend_selection(db, frange))
266 } 266 }
267 267
268 /// Returns position of the matching brace (all types of braces are 268 /// Returns position of the matching brace (all types of braces are
269 /// supported). 269 /// supported).
270 pub fn matching_brace(&self, position: FilePosition) -> Cancelable<Option<TextSize>> { 270 pub fn matching_brace(&self, position: FilePosition) -> Cancellable<Option<TextSize>> {
271 self.with_db(|db| { 271 self.with_db(|db| {
272 let parse = db.parse(position.file_id); 272 let parse = db.parse(position.file_id);
273 let file = parse.tree(); 273 let file = parse.tree();
@@ -281,30 +281,30 @@ impl Analysis {
281 &self, 281 &self,
282 file_id: FileId, 282 file_id: FileId,
283 text_range: Option<TextRange>, 283 text_range: Option<TextRange>,
284 ) -> Cancelable<String> { 284 ) -> Cancellable<String> {
285 self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) 285 self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range))
286 } 286 }
287 287
288 pub fn view_hir(&self, position: FilePosition) -> Cancelable<String> { 288 pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> {
289 self.with_db(|db| view_hir::view_hir(&db, position)) 289 self.with_db(|db| view_hir::view_hir(&db, position))
290 } 290 }
291 291
292 pub fn view_item_tree(&self, file_id: FileId) -> Cancelable<String> { 292 pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
293 self.with_db(|db| view_item_tree::view_item_tree(&db, file_id)) 293 self.with_db(|db| view_item_tree::view_item_tree(&db, file_id))
294 } 294 }
295 295
296 /// Renders the crate graph to GraphViz "dot" syntax. 296 /// Renders the crate graph to GraphViz "dot" syntax.
297 pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> { 297 pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> {
298 self.with_db(|db| view_crate_graph::view_crate_graph(&db)) 298 self.with_db(|db| view_crate_graph::view_crate_graph(&db))
299 } 299 }
300 300
301 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { 301 pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
302 self.with_db(|db| expand_macro::expand_macro(db, position)) 302 self.with_db(|db| expand_macro::expand_macro(db, position))
303 } 303 }
304 304
305 /// Returns an edit to remove all newlines in the range, cleaning up minor 305 /// Returns an edit to remove all newlines in the range, cleaning up minor
306 /// stuff like trailing commas. 306 /// stuff like trailing commas.
307 pub fn join_lines(&self, frange: FileRange) -> Cancelable<TextEdit> { 307 pub fn join_lines(&self, frange: FileRange) -> Cancellable<TextEdit> {
308 self.with_db(|db| { 308 self.with_db(|db| {
309 let parse = db.parse(frange.file_id); 309 let parse = db.parse(frange.file_id);
310 join_lines::join_lines(&parse.tree(), frange.range) 310 join_lines::join_lines(&parse.tree(), frange.range)
@@ -314,7 +314,7 @@ impl Analysis {
314 /// Returns an edit which should be applied when opening a new line, fixing 314 /// Returns an edit which should be applied when opening a new line, fixing
315 /// up minor stuff like continuing the comment. 315 /// up minor stuff like continuing the comment.
316 /// The edit will be a snippet (with `$0`). 316 /// The edit will be a snippet (with `$0`).
317 pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<TextEdit>> { 317 pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> {
318 self.with_db(|db| typing::on_enter(&db, position)) 318 self.with_db(|db| typing::on_enter(&db, position))
319 } 319 }
320 320
@@ -326,7 +326,7 @@ impl Analysis {
326 &self, 326 &self,
327 position: FilePosition, 327 position: FilePosition,
328 char_typed: char, 328 char_typed: char,
329 ) -> Cancelable<Option<SourceChange>> { 329 ) -> Cancellable<Option<SourceChange>> {
330 // Fast path to not even parse the file. 330 // Fast path to not even parse the file.
331 if !typing::TRIGGER_CHARS.contains(char_typed) { 331 if !typing::TRIGGER_CHARS.contains(char_typed) {
332 return Ok(None); 332 return Ok(None);
@@ -336,7 +336,7 @@ impl Analysis {
336 336
337 /// Returns a tree representation of symbols in the file. Useful to draw a 337 /// Returns a tree representation of symbols in the file. Useful to draw a
338 /// file outline. 338 /// file outline.
339 pub fn file_structure(&self, file_id: FileId) -> Cancelable<Vec<StructureNode>> { 339 pub fn file_structure(&self, file_id: FileId) -> Cancellable<Vec<StructureNode>> {
340 self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree())) 340 self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree()))
341 } 341 }
342 342
@@ -345,17 +345,17 @@ impl Analysis {
345 &self, 345 &self,
346 file_id: FileId, 346 file_id: FileId,
347 config: &InlayHintsConfig, 347 config: &InlayHintsConfig,
348 ) -> Cancelable<Vec<InlayHint>> { 348 ) -> Cancellable<Vec<InlayHint>> {
349 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config)) 349 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config))
350 } 350 }
351 351
352 /// Returns the set of folding ranges. 352 /// Returns the set of folding ranges.
353 pub fn folding_ranges(&self, file_id: FileId) -> Cancelable<Vec<Fold>> { 353 pub fn folding_ranges(&self, file_id: FileId) -> Cancellable<Vec<Fold>> {
354 self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree())) 354 self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree()))
355 } 355 }
356 356
357 /// Fuzzy searches for a symbol. 357 /// Fuzzy searches for a symbol.
358 pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> { 358 pub fn symbol_search(&self, query: Query) -> Cancellable<Vec<NavigationTarget>> {
359 self.with_db(|db| { 359 self.with_db(|db| {
360 symbol_index::world_symbols(db, query) 360 symbol_index::world_symbols(db, query)
361 .into_iter() 361 .into_iter()
@@ -368,7 +368,7 @@ impl Analysis {
368 pub fn goto_definition( 368 pub fn goto_definition(
369 &self, 369 &self,
370 position: FilePosition, 370 position: FilePosition,
371 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 371 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
372 self.with_db(|db| goto_definition::goto_definition(db, position)) 372 self.with_db(|db| goto_definition::goto_definition(db, position))
373 } 373 }
374 374
@@ -376,7 +376,7 @@ impl Analysis {
376 pub fn goto_implementation( 376 pub fn goto_implementation(
377 &self, 377 &self,
378 position: FilePosition, 378 position: FilePosition,
379 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 379 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
380 self.with_db(|db| goto_implementation::goto_implementation(db, position)) 380 self.with_db(|db| goto_implementation::goto_implementation(db, position))
381 } 381 }
382 382
@@ -384,7 +384,7 @@ impl Analysis {
384 pub fn goto_type_definition( 384 pub fn goto_type_definition(
385 &self, 385 &self,
386 position: FilePosition, 386 position: FilePosition,
387 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 387 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
388 self.with_db(|db| goto_type_definition::goto_type_definition(db, position)) 388 self.with_db(|db| goto_type_definition::goto_type_definition(db, position))
389 } 389 }
390 390
@@ -393,12 +393,12 @@ impl Analysis {
393 &self, 393 &self,
394 position: FilePosition, 394 position: FilePosition,
395 search_scope: Option<SearchScope>, 395 search_scope: Option<SearchScope>,
396 ) -> Cancelable<Option<ReferenceSearchResult>> { 396 ) -> Cancellable<Option<ReferenceSearchResult>> {
397 self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope)) 397 self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope))
398 } 398 }
399 399
400 /// Finds all methods and free functions for the file. Does not return tests! 400 /// Finds all methods and free functions for the file. Does not return tests!
401 pub fn find_all_methods(&self, file_id: FileId) -> Cancelable<Vec<FileRange>> { 401 pub fn find_all_methods(&self, file_id: FileId) -> Cancellable<Vec<FileRange>> {
402 self.with_db(|db| fn_references::find_all_methods(db, file_id)) 402 self.with_db(|db| fn_references::find_all_methods(db, file_id))
403 } 403 }
404 404
@@ -408,7 +408,7 @@ impl Analysis {
408 position: FilePosition, 408 position: FilePosition,
409 links_in_hover: bool, 409 links_in_hover: bool,
410 markdown: bool, 410 markdown: bool,
411 ) -> Cancelable<Option<RangeInfo<HoverResult>>> { 411 ) -> Cancellable<Option<RangeInfo<HoverResult>>> {
412 self.with_db(|db| hover::hover(db, position, links_in_hover, markdown)) 412 self.with_db(|db| hover::hover(db, position, links_in_hover, markdown))
413 } 413 }
414 414
@@ -416,12 +416,12 @@ impl Analysis {
416 pub fn external_docs( 416 pub fn external_docs(
417 &self, 417 &self,
418 position: FilePosition, 418 position: FilePosition,
419 ) -> Cancelable<Option<doc_links::DocumentationLink>> { 419 ) -> Cancellable<Option<doc_links::DocumentationLink>> {
420 self.with_db(|db| doc_links::external_docs(db, &position)) 420 self.with_db(|db| doc_links::external_docs(db, &position))
421 } 421 }
422 422
423 /// Computes parameter information for the given call expression. 423 /// Computes parameter information for the given call expression.
424 pub fn call_info(&self, position: FilePosition) -> Cancelable<Option<CallInfo>> { 424 pub fn call_info(&self, position: FilePosition) -> Cancellable<Option<CallInfo>> {
425 self.with_db(|db| ide_db::call_info::call_info(db, position)) 425 self.with_db(|db| ide_db::call_info::call_info(db, position))
426 } 426 }
427 427
@@ -429,42 +429,42 @@ impl Analysis {
429 pub fn call_hierarchy( 429 pub fn call_hierarchy(
430 &self, 430 &self,
431 position: FilePosition, 431 position: FilePosition,
432 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 432 ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
433 self.with_db(|db| call_hierarchy::call_hierarchy(db, position)) 433 self.with_db(|db| call_hierarchy::call_hierarchy(db, position))
434 } 434 }
435 435
436 /// Computes incoming calls for the given file position. 436 /// Computes incoming calls for the given file position.
437 pub fn incoming_calls(&self, position: FilePosition) -> Cancelable<Option<Vec<CallItem>>> { 437 pub fn incoming_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
438 self.with_db(|db| call_hierarchy::incoming_calls(db, position)) 438 self.with_db(|db| call_hierarchy::incoming_calls(db, position))
439 } 439 }
440 440
441 /// Computes incoming calls for the given file position. 441 /// Computes incoming calls for the given file position.
442 pub fn outgoing_calls(&self, position: FilePosition) -> Cancelable<Option<Vec<CallItem>>> { 442 pub fn outgoing_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
443 self.with_db(|db| call_hierarchy::outgoing_calls(db, position)) 443 self.with_db(|db| call_hierarchy::outgoing_calls(db, position))
444 } 444 }
445 445
446 /// Returns a `mod name;` declaration which created the current module. 446 /// Returns a `mod name;` declaration which created the current module.
447 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { 447 pub fn parent_module(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> {
448 self.with_db(|db| parent_module::parent_module(db, position)) 448 self.with_db(|db| parent_module::parent_module(db, position))
449 } 449 }
450 450
451 /// Returns crates this file belongs too. 451 /// Returns crates this file belongs too.
452 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 452 pub fn crate_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
453 self.with_db(|db| parent_module::crate_for(db, file_id)) 453 self.with_db(|db| parent_module::crate_for(db, file_id))
454 } 454 }
455 455
456 /// Returns the edition of the given crate. 456 /// Returns the edition of the given crate.
457 pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> { 457 pub fn crate_edition(&self, crate_id: CrateId) -> Cancellable<Edition> {
458 self.with_db(|db| db.crate_graph()[crate_id].edition) 458 self.with_db(|db| db.crate_graph()[crate_id].edition)
459 } 459 }
460 460
461 /// Returns the root file of the given crate. 461 /// Returns the root file of the given crate.
462 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { 462 pub fn crate_root(&self, crate_id: CrateId) -> Cancellable<FileId> {
463 self.with_db(|db| db.crate_graph()[crate_id].root_file_id) 463 self.with_db(|db| db.crate_graph()[crate_id].root_file_id)
464 } 464 }
465 465
466 /// Returns the set of possible targets to run for the current file. 466 /// Returns the set of possible targets to run for the current file.
467 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { 467 pub fn runnables(&self, file_id: FileId) -> Cancellable<Vec<Runnable>> {
468 self.with_db(|db| runnables::runnables(db, file_id)) 468 self.with_db(|db| runnables::runnables(db, file_id))
469 } 469 }
470 470
@@ -473,24 +473,24 @@ impl Analysis {
473 &self, 473 &self,
474 position: FilePosition, 474 position: FilePosition,
475 search_scope: Option<SearchScope>, 475 search_scope: Option<SearchScope>,
476 ) -> Cancelable<Vec<Runnable>> { 476 ) -> Cancellable<Vec<Runnable>> {
477 self.with_db(|db| runnables::related_tests(db, position, search_scope)) 477 self.with_db(|db| runnables::related_tests(db, position, search_scope))
478 } 478 }
479 479
480 /// Computes syntax highlighting for the given file 480 /// Computes syntax highlighting for the given file
481 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HlRange>> { 481 pub fn highlight(&self, file_id: FileId) -> Cancellable<Vec<HlRange>> {
482 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) 482 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
483 } 483 }
484 484
485 /// Computes syntax highlighting for the given file range. 485 /// Computes syntax highlighting for the given file range.
486 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HlRange>> { 486 pub fn highlight_range(&self, frange: FileRange) -> Cancellable<Vec<HlRange>> {
487 self.with_db(|db| { 487 self.with_db(|db| {
488 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false) 488 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
489 }) 489 })
490 } 490 }
491 491
492 /// Computes syntax highlighting for the given file. 492 /// Computes syntax highlighting for the given file.
493 pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancelable<String> { 493 pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable<String> {
494 self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow)) 494 self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow))
495 } 495 }
496 496
@@ -499,7 +499,7 @@ impl Analysis {
499 &self, 499 &self,
500 config: &CompletionConfig, 500 config: &CompletionConfig,
501 position: FilePosition, 501 position: FilePosition,
502 ) -> Cancelable<Option<Vec<CompletionItem>>> { 502 ) -> Cancellable<Option<Vec<CompletionItem>>> {
503 self.with_db(|db| ide_completion::completions(db, config, position).map(Into::into)) 503 self.with_db(|db| ide_completion::completions(db, config, position).map(Into::into))
504 } 504 }
505 505
@@ -510,7 +510,7 @@ impl Analysis {
510 position: FilePosition, 510 position: FilePosition,
511 full_import_path: &str, 511 full_import_path: &str,
512 imported_name: String, 512 imported_name: String,
513 ) -> Cancelable<Vec<TextEdit>> { 513 ) -> Cancellable<Vec<TextEdit>> {
514 Ok(self 514 Ok(self
515 .with_db(|db| { 515 .with_db(|db| {
516 ide_completion::resolve_completion_edits( 516 ide_completion::resolve_completion_edits(
@@ -533,7 +533,7 @@ impl Analysis {
533 config: &AssistConfig, 533 config: &AssistConfig,
534 resolve: AssistResolveStrategy, 534 resolve: AssistResolveStrategy,
535 frange: FileRange, 535 frange: FileRange,
536 ) -> Cancelable<Vec<Assist>> { 536 ) -> Cancellable<Vec<Assist>> {
537 self.with_db(|db| { 537 self.with_db(|db| {
538 let ssr_assists = ssr::ssr_assists(db, &resolve, frange); 538 let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
539 let mut acc = Assist::get(db, config, resolve, frange); 539 let mut acc = Assist::get(db, config, resolve, frange);
@@ -548,7 +548,7 @@ impl Analysis {
548 config: &DiagnosticsConfig, 548 config: &DiagnosticsConfig,
549 resolve: AssistResolveStrategy, 549 resolve: AssistResolveStrategy,
550 file_id: FileId, 550 file_id: FileId,
551 ) -> Cancelable<Vec<Diagnostic>> { 551 ) -> Cancellable<Vec<Diagnostic>> {
552 self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id)) 552 self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id))
553 } 553 }
554 554
@@ -559,7 +559,7 @@ impl Analysis {
559 diagnostics_config: &DiagnosticsConfig, 559 diagnostics_config: &DiagnosticsConfig,
560 resolve: AssistResolveStrategy, 560 resolve: AssistResolveStrategy,
561 frange: FileRange, 561 frange: FileRange,
562 ) -> Cancelable<Vec<Assist>> { 562 ) -> Cancellable<Vec<Assist>> {
563 let include_fixes = match &assist_config.allowed { 563 let include_fixes = match &assist_config.allowed {
564 Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix), 564 Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix),
565 None => true, 565 None => true,
@@ -591,14 +591,14 @@ impl Analysis {
591 &self, 591 &self,
592 position: FilePosition, 592 position: FilePosition,
593 new_name: &str, 593 new_name: &str,
594 ) -> Cancelable<Result<SourceChange, RenameError>> { 594 ) -> Cancellable<Result<SourceChange, RenameError>> {
595 self.with_db(|db| references::rename::rename(db, position, new_name)) 595 self.with_db(|db| references::rename::rename(db, position, new_name))
596 } 596 }
597 597
598 pub fn prepare_rename( 598 pub fn prepare_rename(
599 &self, 599 &self,
600 position: FilePosition, 600 position: FilePosition,
601 ) -> Cancelable<Result<RangeInfo<()>, RenameError>> { 601 ) -> Cancellable<Result<RangeInfo<()>, RenameError>> {
602 self.with_db(|db| references::rename::prepare_rename(db, position)) 602 self.with_db(|db| references::rename::prepare_rename(db, position))
603 } 603 }
604 604
@@ -606,7 +606,7 @@ impl Analysis {
606 &self, 606 &self,
607 file_id: FileId, 607 file_id: FileId,
608 new_name_stem: &str, 608 new_name_stem: &str,
609 ) -> Cancelable<Option<SourceChange>> { 609 ) -> Cancellable<Option<SourceChange>> {
610 self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem)) 610 self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem))
611 } 611 }
612 612
@@ -616,7 +616,7 @@ impl Analysis {
616 parse_only: bool, 616 parse_only: bool,
617 resolve_context: FilePosition, 617 resolve_context: FilePosition,
618 selections: Vec<FileRange>, 618 selections: Vec<FileRange>,
619 ) -> Cancelable<Result<SourceChange, SsrError>> { 619 ) -> Cancellable<Result<SourceChange, SsrError>> {
620 self.with_db(|db| { 620 self.with_db(|db| {
621 let rule: ide_ssr::SsrRule = query.parse()?; 621 let rule: ide_ssr::SsrRule = query.parse()?;
622 let mut match_finder = 622 let mut match_finder =
@@ -631,11 +631,11 @@ impl Analysis {
631 &self, 631 &self,
632 file_id: FileId, 632 file_id: FileId,
633 config: AnnotationConfig, 633 config: AnnotationConfig,
634 ) -> Cancelable<Vec<Annotation>> { 634 ) -> Cancellable<Vec<Annotation>> {
635 self.with_db(|db| annotations::annotations(db, file_id, config)) 635 self.with_db(|db| annotations::annotations(db, file_id, config))
636 } 636 }
637 637
638 pub fn resolve_annotation(&self, annotation: Annotation) -> Cancelable<Annotation> { 638 pub fn resolve_annotation(&self, annotation: Annotation) -> Cancellable<Annotation> {
639 self.with_db(|db| annotations::resolve_annotation(db, annotation)) 639 self.with_db(|db| annotations::resolve_annotation(db, annotation))
640 } 640 }
641 641
@@ -643,16 +643,28 @@ impl Analysis {
643 &self, 643 &self,
644 range: FileRange, 644 range: FileRange,
645 direction: Direction, 645 direction: Direction,
646 ) -> Cancelable<Option<TextEdit>> { 646 ) -> Cancellable<Option<TextEdit>> {
647 self.with_db(|db| move_item::move_item(db, range, direction)) 647 self.with_db(|db| move_item::move_item(db, range, direction))
648 } 648 }
649 649
650 /// Performs an operation on that may be Canceled. 650 /// Performs an operation on the database that may be canceled.
651 fn with_db<F, T>(&self, f: F) -> Cancelable<T> 651 ///
652 /// rust-analyzer needs to be able to answer semantic questions about the
653 /// code while the code is being modified. A common problem is that a
654 /// long-running query is being calculated when a new change arrives.
655 ///
656 /// We can't just apply the change immediately: this will cause the pending
657 /// query to see inconsistent state (it will observe an absence of
658 /// repeatable read). So what we do is we **cancel** all pending queries
659 /// before applying the change.
660 ///
661 /// Salsa implements cancelation by unwinding with a special value and
662 /// catching it on the API boundary.
663 fn with_db<F, T>(&self, f: F) -> Cancellable<T>
652 where 664 where
653 F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, 665 F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe,
654 { 666 {
655 self.db.catch_canceled(f) 667 Cancelled::catch(|| f(&self.db))
656 } 668 }
657} 669}
658 670
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index 151bf3783..ffdcdc930 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -69,18 +69,25 @@ impl Completions {
69 items.into_iter().for_each(|item| self.add(item.into())) 69 items.into_iter().for_each(|item| self.add(item.into()))
70 } 70 }
71 71
72 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &hir::Type) { 72 pub(crate) fn add_field(
73 let item = render_field(RenderContext::new(ctx), field, ty); 73 &mut self,
74 ctx: &CompletionContext,
75 receiver: Option<hir::Name>,
76 field: hir::Field,
77 ty: &hir::Type,
78 ) {
79 let item = render_field(RenderContext::new(ctx), receiver, field, ty);
74 self.add(item); 80 self.add(item);
75 } 81 }
76 82
77 pub(crate) fn add_tuple_field( 83 pub(crate) fn add_tuple_field(
78 &mut self, 84 &mut self,
79 ctx: &CompletionContext, 85 ctx: &CompletionContext,
86 receiver: Option<hir::Name>,
80 field: usize, 87 field: usize,
81 ty: &hir::Type, 88 ty: &hir::Type,
82 ) { 89 ) {
83 let item = render_tuple_field(RenderContext::new(ctx), field, ty); 90 let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
84 self.add(item); 91 self.add(item);
85 } 92 }
86 93
@@ -132,9 +139,11 @@ impl Completions {
132 &mut self, 139 &mut self,
133 ctx: &CompletionContext, 140 ctx: &CompletionContext,
134 func: hir::Function, 141 func: hir::Function,
142 receiver: Option<hir::Name>,
135 local_name: Option<hir::Name>, 143 local_name: Option<hir::Name>,
136 ) { 144 ) {
137 if let Some(item) = render_method(RenderContext::new(ctx), None, local_name, func) { 145 if let Some(item) = render_method(RenderContext::new(ctx), None, receiver, local_name, func)
146 {
138 self.add(item) 147 self.add(item)
139 } 148 }
140 } 149 }
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index fd9738743..302c9ccbd 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -1,6 +1,7 @@
1//! Completes references after dot (fields and method calls). 1//! Completes references after dot (fields and method calls).
2 2
3use hir::{HasVisibility, Type}; 3use either::Either;
4use hir::{HasVisibility, ScopeDef};
4use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
5 6
6use crate::{context::CompletionContext, Completions}; 7use crate::{context::CompletionContext, Completions};
@@ -9,7 +10,7 @@ use crate::{context::CompletionContext, Completions};
9pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
10 let dot_receiver = match &ctx.dot_receiver { 11 let dot_receiver = match &ctx.dot_receiver {
11 Some(expr) => expr, 12 Some(expr) => expr,
12 _ => return, 13 _ => return complete_undotted_self(acc, ctx),
13 }; 14 };
14 15
15 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 16 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
@@ -20,12 +21,43 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
20 if ctx.is_call { 21 if ctx.is_call {
21 cov_mark::hit!(test_no_struct_field_completion_for_method_call); 22 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
22 } else { 23 } else {
23 complete_fields(acc, ctx, &receiver_ty); 24 complete_fields(ctx, &receiver_ty, |field, ty| match field {
25 Either::Left(field) => acc.add_field(ctx, None, field, &ty),
26 Either::Right(tuple_idx) => acc.add_tuple_field(ctx, None, tuple_idx, &ty),
27 });
24 } 28 }
25 complete_methods(acc, ctx, &receiver_ty); 29 complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None));
30}
31
32fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
33 if !ctx.is_trivial_path || !ctx.config.enable_self_on_the_fly {
34 return;
35 }
36 ctx.scope.process_all_names(&mut |name, def| {
37 if let ScopeDef::Local(local) = &def {
38 if local.is_self(ctx.db) {
39 let ty = local.ty(ctx.db);
40 complete_fields(ctx, &ty, |field, ty| match field {
41 either::Either::Left(field) => {
42 acc.add_field(ctx, Some(name.clone()), field, &ty)
43 }
44 either::Either::Right(tuple_idx) => {
45 acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty)
46 }
47 });
48 complete_methods(ctx, &ty, |func| {
49 acc.add_method(ctx, func, Some(name.clone()), None)
50 });
51 }
52 }
53 });
26} 54}
27 55
28fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 56fn complete_fields(
57 ctx: &CompletionContext,
58 receiver: &hir::Type,
59 mut f: impl FnMut(Either<hir::Field, usize>, hir::Type),
60) {
29 for receiver in receiver.autoderef(ctx.db) { 61 for receiver in receiver.autoderef(ctx.db) {
30 for (field, ty) in receiver.fields(ctx.db) { 62 for (field, ty) in receiver.fields(ctx.db) {
31 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { 63 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
@@ -33,16 +65,20 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
33 // field is editable, we should show the completion 65 // field is editable, we should show the completion
34 continue; 66 continue;
35 } 67 }
36 acc.add_field(ctx, field, &ty); 68 f(Either::Left(field), ty);
37 } 69 }
38 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { 70 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
39 // FIXME: Handle visibility 71 // FIXME: Handle visibility
40 acc.add_tuple_field(ctx, i, &ty); 72 f(Either::Right(i), ty);
41 } 73 }
42 } 74 }
43} 75}
44 76
45fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 77fn complete_methods(
78 ctx: &CompletionContext,
79 receiver: &hir::Type,
80 mut f: impl FnMut(hir::Function),
81) {
46 if let Some(krate) = ctx.krate { 82 if let Some(krate) = ctx.krate {
47 let mut seen_methods = FxHashSet::default(); 83 let mut seen_methods = FxHashSet::default();
48 let traits_in_scope = ctx.scope.traits_in_scope(); 84 let traits_in_scope = ctx.scope.traits_in_scope();
@@ -51,7 +87,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
51 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) 87 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
52 && seen_methods.insert(func.name(ctx.db)) 88 && seen_methods.insert(func.name(ctx.db))
53 { 89 {
54 acc.add_method(ctx, func, None); 90 f(func);
55 } 91 }
56 None::<()> 92 None::<()>
57 }); 93 });
@@ -484,4 +520,34 @@ impl S {
484 "#]], 520 "#]],
485 ); 521 );
486 } 522 }
523
524 #[test]
525 fn completes_bare_fields_and_methods_in_methods() {
526 check(
527 r#"
528struct Foo { field: i32 }
529
530impl Foo { fn foo(&self) { $0 } }"#,
531 expect![[r#"
532 lc self &Foo
533 sp Self
534 st Foo
535 fd self.field i32
536 me self.foo() fn(&self)
537 "#]],
538 );
539 check(
540 r#"
541struct Foo(i32);
542
543impl Foo { fn foo(&mut self) { $0 } }"#,
544 expect![[r#"
545 lc self &mut Foo
546 sp Self
547 st Foo
548 fd self.0 i32
549 me self.foo() fn(&mut self)
550 "#]],
551 );
552 }
487} 553}
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index 227c08d01..0ac47cdbe 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -39,7 +39,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
39 }; 39 };
40 40
41 for (field, ty) in missing_fields { 41 for (field, ty) in missing_fields {
42 acc.add_field(ctx, field, &ty); 42 acc.add_field(ctx, None, field, &ty);
43 } 43 }
44 44
45 Some(()) 45 Some(())
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 9db8516d0..20188a7dd 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -11,6 +11,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
11 if ctx.is_path_disallowed() || ctx.expects_item() { 11 if ctx.is_path_disallowed() || ctx.expects_item() {
12 return; 12 return;
13 } 13 }
14
14 if ctx.expects_assoc_item() { 15 if ctx.expects_assoc_item() {
15 ctx.scope.process_all_names(&mut |name, def| { 16 ctx.scope.process_all_names(&mut |name, def| {
16 if let ScopeDef::MacroDef(macro_def) = def { 17 if let ScopeDef::MacroDef(macro_def) = def {
@@ -32,6 +33,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
32 }); 33 });
33 return; 34 return;
34 } 35 }
36
35 if let Some(hir::Adt::Enum(e)) = 37 if let Some(hir::Adt::Enum(e)) =
36 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 38 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
37 { 39 {
diff --git a/crates/ide_completion/src/config.rs b/crates/ide_completion/src/config.rs
index d70ed6c1c..c300ce887 100644
--- a/crates/ide_completion/src/config.rs
+++ b/crates/ide_completion/src/config.rs
@@ -10,6 +10,7 @@ use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
10pub struct CompletionConfig { 10pub struct CompletionConfig {
11 pub enable_postfix_completions: bool, 11 pub enable_postfix_completions: bool,
12 pub enable_imports_on_the_fly: bool, 12 pub enable_imports_on_the_fly: bool,
13 pub enable_self_on_the_fly: bool,
13 pub add_call_parenthesis: bool, 14 pub add_call_parenthesis: bool,
14 pub add_call_argument_snippets: bool, 15 pub add_call_argument_snippets: bool,
15 pub snippet_cap: Option<SnippetCap>, 16 pub snippet_cap: Option<SnippetCap>,
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 425dd0247..a49a60711 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -25,18 +25,20 @@ use crate::{
25 25
26pub(crate) fn render_field<'a>( 26pub(crate) fn render_field<'a>(
27 ctx: RenderContext<'a>, 27 ctx: RenderContext<'a>,
28 receiver: Option<hir::Name>,
28 field: hir::Field, 29 field: hir::Field,
29 ty: &hir::Type, 30 ty: &hir::Type,
30) -> CompletionItem { 31) -> CompletionItem {
31 Render::new(ctx).render_field(field, ty) 32 Render::new(ctx).render_field(receiver, field, ty)
32} 33}
33 34
34pub(crate) fn render_tuple_field<'a>( 35pub(crate) fn render_tuple_field<'a>(
35 ctx: RenderContext<'a>, 36 ctx: RenderContext<'a>,
37 receiver: Option<hir::Name>,
36 field: usize, 38 field: usize,
37 ty: &hir::Type, 39 ty: &hir::Type,
38) -> CompletionItem { 40) -> CompletionItem {
39 Render::new(ctx).render_tuple_field(field, ty) 41 Render::new(ctx).render_tuple_field(receiver, field, ty)
40} 42}
41 43
42pub(crate) fn render_resolution<'a>( 44pub(crate) fn render_resolution<'a>(
@@ -126,11 +128,19 @@ impl<'a> Render<'a> {
126 Render { ctx } 128 Render { ctx }
127 } 129 }
128 130
129 fn render_field(&self, field: hir::Field, ty: &hir::Type) -> CompletionItem { 131 fn render_field(
132 &self,
133 receiver: Option<hir::Name>,
134 field: hir::Field,
135 ty: &hir::Type,
136 ) -> CompletionItem {
130 let is_deprecated = self.ctx.is_deprecated(field); 137 let is_deprecated = self.ctx.is_deprecated(field);
131 let name = field.name(self.ctx.db()).to_string(); 138 let name = field.name(self.ctx.db()).to_string();
132 let mut item = 139 let mut item = CompletionItem::new(
133 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name.clone()); 140 CompletionKind::Reference,
141 self.ctx.source_range(),
142 receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)),
143 );
134 item.kind(SymbolKind::Field) 144 item.kind(SymbolKind::Field)
135 .detail(ty.display(self.ctx.db()).to_string()) 145 .detail(ty.display(self.ctx.db()).to_string())
136 .set_documentation(field.docs(self.ctx.db())) 146 .set_documentation(field.docs(self.ctx.db()))
@@ -151,11 +161,17 @@ impl<'a> Render<'a> {
151 item.build() 161 item.build()
152 } 162 }
153 163
154 fn render_tuple_field(&self, field: usize, ty: &hir::Type) -> CompletionItem { 164 fn render_tuple_field(
165 &self,
166 receiver: Option<hir::Name>,
167 field: usize,
168 ty: &hir::Type,
169 ) -> CompletionItem {
155 let mut item = CompletionItem::new( 170 let mut item = CompletionItem::new(
156 CompletionKind::Reference, 171 CompletionKind::Reference,
157 self.ctx.source_range(), 172 self.ctx.source_range(),
158 field.to_string(), 173 receiver
174 .map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)),
159 ); 175 );
160 176
161 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); 177 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string());
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 63bd66926..3ec77ca0f 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -20,23 +20,25 @@ pub(crate) fn render_fn<'a>(
20 fn_: hir::Function, 20 fn_: hir::Function,
21) -> Option<CompletionItem> { 21) -> Option<CompletionItem> {
22 let _p = profile::span("render_fn"); 22 let _p = profile::span("render_fn");
23 Some(FunctionRender::new(ctx, local_name, fn_, false)?.render(import_to_add)) 23 Some(FunctionRender::new(ctx, None, local_name, fn_, false)?.render(import_to_add))
24} 24}
25 25
26pub(crate) fn render_method<'a>( 26pub(crate) fn render_method<'a>(
27 ctx: RenderContext<'a>, 27 ctx: RenderContext<'a>,
28 import_to_add: Option<ImportEdit>, 28 import_to_add: Option<ImportEdit>,
29 receiver: Option<hir::Name>,
29 local_name: Option<hir::Name>, 30 local_name: Option<hir::Name>,
30 fn_: hir::Function, 31 fn_: hir::Function,
31) -> Option<CompletionItem> { 32) -> Option<CompletionItem> {
32 let _p = profile::span("render_method"); 33 let _p = profile::span("render_method");
33 Some(FunctionRender::new(ctx, local_name, fn_, true)?.render(import_to_add)) 34 Some(FunctionRender::new(ctx, receiver, local_name, fn_, true)?.render(import_to_add))
34} 35}
35 36
36#[derive(Debug)] 37#[derive(Debug)]
37struct FunctionRender<'a> { 38struct FunctionRender<'a> {
38 ctx: RenderContext<'a>, 39 ctx: RenderContext<'a>,
39 name: String, 40 name: String,
41 receiver: Option<hir::Name>,
40 func: hir::Function, 42 func: hir::Function,
41 ast_node: Fn, 43 ast_node: Fn,
42 is_method: bool, 44 is_method: bool,
@@ -45,6 +47,7 @@ struct FunctionRender<'a> {
45impl<'a> FunctionRender<'a> { 47impl<'a> FunctionRender<'a> {
46 fn new( 48 fn new(
47 ctx: RenderContext<'a>, 49 ctx: RenderContext<'a>,
50 receiver: Option<hir::Name>,
48 local_name: Option<hir::Name>, 51 local_name: Option<hir::Name>,
49 fn_: hir::Function, 52 fn_: hir::Function,
50 is_method: bool, 53 is_method: bool,
@@ -52,11 +55,14 @@ impl<'a> FunctionRender<'a> {
52 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db())).to_string(); 55 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db())).to_string();
53 let ast_node = fn_.source(ctx.db())?.value; 56 let ast_node = fn_.source(ctx.db())?.value;
54 57
55 Some(FunctionRender { ctx, name, func: fn_, ast_node, is_method }) 58 Some(FunctionRender { ctx, name, receiver, func: fn_, ast_node, is_method })
56 } 59 }
57 60
58 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 61 fn render(mut self, import_to_add: Option<ImportEdit>) -> CompletionItem {
59 let params = self.params(); 62 let params = self.params();
63 if let Some(receiver) = &self.receiver {
64 self.name = format!("{}.{}", receiver, &self.name)
65 }
60 let mut item = CompletionItem::new( 66 let mut item = CompletionItem::new(
61 CompletionKind::Reference, 67 CompletionKind::Reference,
62 self.ctx.source_range(), 68 self.ctx.source_range(),
@@ -148,7 +154,7 @@ impl<'a> FunctionRender<'a> {
148 }; 154 };
149 155
150 let mut params_pats = Vec::new(); 156 let mut params_pats = Vec::new();
151 let params_ty = if self.ctx.completion.dot_receiver.is_some() { 157 let params_ty = if self.ctx.completion.dot_receiver.is_some() || self.receiver.is_some() {
152 self.func.method_params(self.ctx.db()).unwrap_or_default() 158 self.func.method_params(self.ctx.db()).unwrap_or_default()
153 } else { 159 } else {
154 if let Some(s) = ast_params.self_param() { 160 if let Some(s) = ast_params.self_param() {
@@ -255,6 +261,26 @@ fn bar(s: &S) {
255} 261}
256"#, 262"#,
257 ); 263 );
264
265 check_edit(
266 "self.foo",
267 r#"
268struct S {}
269impl S {
270 fn foo(&self, x: i32) {
271 $0
272 }
273}
274"#,
275 r#"
276struct S {}
277impl S {
278 fn foo(&self, x: i32) {
279 self.foo(${1:x})$0
280 }
281}
282"#,
283 );
258 } 284 }
259 285
260 #[test] 286 #[test]
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index 93c7c872c..b0a4b2026 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -19,6 +19,7 @@ use crate::{item::CompletionKind, CompletionConfig, CompletionItem};
19pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { 19pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
20 enable_postfix_completions: true, 20 enable_postfix_completions: true,
21 enable_imports_on_the_fly: true, 21 enable_imports_on_the_fly: true,
22 enable_self_on_the_fly: true,
22 add_call_parenthesis: true, 23 add_call_parenthesis: true,
23 add_call_argument_snippets: true, 24 add_call_argument_snippets: true,
24 snippet_cap: SnippetCap::new(true), 25 snippet_cap: SnippetCap::new(true),
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 88ee4a87d..1f900aef4 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -19,8 +19,7 @@ use std::{fmt, sync::Arc};
19 19
20use base_db::{ 20use base_db::{
21 salsa::{self, Durability}, 21 salsa::{self, Durability},
22 AnchoredPath, Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, 22 AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
23 SourceDatabase, Upcast,
24}; 23};
25use hir::db::{AstDatabase, DefDatabase, HirDatabase}; 24use hir::db::{AstDatabase, DefDatabase, HirDatabase};
26use rustc_hash::FxHashSet; 25use rustc_hash::FxHashSet;
@@ -80,20 +79,7 @@ impl FileLoader for RootDatabase {
80 } 79 }
81} 80}
82 81
83impl salsa::Database for RootDatabase { 82impl salsa::Database for RootDatabase {}
84 fn on_propagated_panic(&self) -> ! {
85 Canceled::throw()
86 }
87 fn salsa_event(&self, event: salsa::Event) {
88 match event.kind {
89 salsa::EventKind::DidValidateMemoizedValue { .. }
90 | salsa::EventKind::WillExecute { .. } => {
91 self.check_canceled();
92 }
93 _ => (),
94 }
95 }
96}
97 83
98impl Default for RootDatabase { 84impl Default for RootDatabase {
99 fn default() -> RootDatabase { 85 fn default() -> RootDatabase {
@@ -126,7 +112,7 @@ impl salsa::ParallelDatabase for RootDatabase {
126} 112}
127 113
128#[salsa::query_group(LineIndexDatabaseStorage)] 114#[salsa::query_group(LineIndexDatabaseStorage)]
129pub trait LineIndexDatabase: base_db::SourceDatabase + CheckCanceled { 115pub trait LineIndexDatabase: base_db::SourceDatabase {
130 fn line_index(&self, file_id: FileId) -> Arc<LineIndex>; 116 fn line_index(&self, file_id: FileId) -> Arc<LineIndex>;
131} 117}
132 118
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 0f5c4abc4..5c372a7e5 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -127,7 +127,7 @@ fn library_symbols(db: &dyn SymbolsDatabase) -> Arc<FxHashMap<SourceRootId, Symb
127} 127}
128 128
129fn file_symbols(db: &dyn SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> { 129fn file_symbols(db: &dyn SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> {
130 db.check_canceled(); 130 db.unwind_if_cancelled();
131 let parse = db.parse(file_id); 131 let parse = db.parse(file_id);
132 132
133 let symbols = source_file_to_file_symbols(&parse.tree(), file_id); 133 let symbols = source_file_to_file_symbols(&parse.tree(), file_id);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index a67b0bb25..ae78fd4f6 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -100,6 +100,9 @@ config_data! {
100 /// Toggles the additional completions that automatically add imports when completed. 100 /// Toggles the additional completions that automatically add imports when completed.
101 /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. 101 /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
102 completion_autoimport_enable: bool = "true", 102 completion_autoimport_enable: bool = "true",
103 /// Toggles the additional completions that automatically show method calls and field accesses
104 /// with `self` prefixed to them when inside a method.
105 completion_autoself_enable: bool = "true",
103 106
104 /// Whether to show native rust-analyzer diagnostics. 107 /// Whether to show native rust-analyzer diagnostics.
105 diagnostics_enable: bool = "true", 108 diagnostics_enable: bool = "true",
@@ -666,6 +669,7 @@ impl Config {
666 enable_postfix_completions: self.data.completion_postfix_enable, 669 enable_postfix_completions: self.data.completion_postfix_enable,
667 enable_imports_on_the_fly: self.data.completion_autoimport_enable 670 enable_imports_on_the_fly: self.data.completion_autoimport_enable
668 && completion_item_edit_resolve(&self.caps), 671 && completion_item_edit_resolve(&self.caps),
672 enable_self_on_the_fly: self.data.completion_autoself_enable,
669 add_call_parenthesis: self.data.completion_addCallParenthesis, 673 add_call_parenthesis: self.data.completion_addCallParenthesis,
670 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, 674 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets,
671 insert_use: self.insert_use_config(), 675 insert_use: self.insert_use_config(),
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs
index baf2199d9..2011a4132 100644
--- a/crates/rust-analyzer/src/dispatch.rs
+++ b/crates/rust-analyzer/src/dispatch.rs
@@ -5,7 +5,7 @@ use serde::{de::DeserializeOwned, Serialize};
5 5
6use crate::{ 6use crate::{
7 global_state::{GlobalState, GlobalStateSnapshot}, 7 global_state::{GlobalState, GlobalStateSnapshot},
8 lsp_utils::is_canceled, 8 lsp_utils::is_cancelled,
9 main_loop::Task, 9 main_loop::Task,
10 LspError, Result, 10 LspError, Result,
11}; 11};
@@ -132,7 +132,7 @@ where
132 Err(e) => match e.downcast::<LspError>() { 132 Err(e) => match e.downcast::<LspError>() {
133 Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), 133 Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message),
134 Err(e) => { 134 Err(e) => {
135 if is_canceled(&*e) { 135 if is_cancelled(&*e) {
136 lsp_server::Response::new_err( 136 lsp_server::Response::new_err(
137 id, 137 id,
138 lsp_server::ErrorCode::ContentModified as i32, 138 lsp_server::ErrorCode::ContentModified as i32,
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 6a36d29d4..ea9dbf7fc 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -7,7 +7,7 @@ use std::{sync::Arc, time::Instant};
7 7
8use crossbeam_channel::{unbounded, Receiver, Sender}; 8use crossbeam_channel::{unbounded, Receiver, Sender};
9use flycheck::FlycheckHandle; 9use flycheck::FlycheckHandle;
10use ide::{Analysis, AnalysisHost, Cancelable, Change, FileId}; 10use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
11use ide_db::base_db::{CrateId, VfsPath}; 11use ide_db::base_db::{CrateId, VfsPath};
12use lsp_types::{SemanticTokens, Url}; 12use lsp_types::{SemanticTokens, Url};
13use parking_lot::{Mutex, RwLock}; 13use parking_lot::{Mutex, RwLock};
@@ -280,7 +280,7 @@ impl GlobalStateSnapshot {
280 file_id_to_url(&self.vfs.read().0, id) 280 file_id_to_url(&self.vfs.read().0, id)
281 } 281 }
282 282
283 pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancelable<LineIndex> { 283 pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
284 let endings = self.vfs.read().1[&file_id]; 284 let endings = self.vfs.read().1[&file_id];
285 let index = self.analysis.file_line_index(file_id)?; 285 let index = self.analysis.file_line_index(file_id)?;
286 let res = LineIndex { index, endings, encoding: self.config.offset_encoding() }; 286 let res = LineIndex { index, endings, encoding: self.config.offset_encoding() };
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index 781073fe5..ec36a5f5c 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -132,6 +132,7 @@ fn integrated_completion_benchmark() {
132 let config = CompletionConfig { 132 let config = CompletionConfig {
133 enable_postfix_completions: true, 133 enable_postfix_completions: true,
134 enable_imports_on_the_fly: true, 134 enable_imports_on_the_fly: true,
135 enable_self_on_the_fly: true,
135 add_call_parenthesis: true, 136 add_call_parenthesis: true,
136 add_call_argument_snippets: true, 137 add_call_argument_snippets: true,
137 snippet_cap: SnippetCap::new(true), 138 snippet_cap: SnippetCap::new(true),
@@ -166,6 +167,7 @@ fn integrated_completion_benchmark() {
166 let config = CompletionConfig { 167 let config = CompletionConfig {
167 enable_postfix_completions: true, 168 enable_postfix_completions: true,
168 enable_imports_on_the_fly: true, 169 enable_imports_on_the_fly: true,
170 enable_self_on_the_fly: true,
169 add_call_parenthesis: true, 171 add_call_parenthesis: true,
170 add_call_argument_snippets: true, 172 add_call_argument_snippets: true,
171 snippet_cap: SnippetCap::new(true), 173 snippet_cap: SnippetCap::new(true),
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 73c4193e8..8000b5490 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -1,7 +1,7 @@
1//! Utilities for LSP-related boilerplate code. 1//! Utilities for LSP-related boilerplate code.
2use std::{error::Error, ops::Range, sync::Arc}; 2use std::{error::Error, ops::Range, sync::Arc};
3 3
4use ide_db::base_db::Canceled; 4use ide_db::base_db::Cancelled;
5use lsp_server::Notification; 5use lsp_server::Notification;
6 6
7use crate::{ 7use crate::{
@@ -10,8 +10,8 @@ use crate::{
10 line_index::{LineEndings, LineIndex, OffsetEncoding}, 10 line_index::{LineEndings, LineIndex, OffsetEncoding},
11}; 11};
12 12
13pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { 13pub(crate) fn is_cancelled(e: &(dyn Error + 'static)) -> bool {
14 e.downcast_ref::<Canceled>().is_some() 14 e.downcast_ref::<Cancelled>().is_some()
15} 15}
16 16
17pub(crate) fn notification_is<N: lsp_types::notification::Notification>( 17pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 008758ea0..31d8ea9e7 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -22,7 +22,7 @@ use crate::{
22 from_proto, 22 from_proto,
23 global_state::{file_id_to_url, url_to_file_id, GlobalState}, 23 global_state::{file_id_to_url, url_to_file_id, GlobalState},
24 handlers, lsp_ext, 24 handlers, lsp_ext,
25 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, 25 lsp_utils::{apply_document_changes, is_cancelled, notification_is, Progress},
26 reload::{BuildDataProgress, ProjectWorkspaceProgress}, 26 reload::{BuildDataProgress, ProjectWorkspaceProgress},
27 Result, 27 Result,
28}; 28};
@@ -752,7 +752,7 @@ impl GlobalState {
752 .filter_map(|file_id| { 752 .filter_map(|file_id| {
753 handlers::publish_diagnostics(&snapshot, file_id) 753 handlers::publish_diagnostics(&snapshot, file_id)
754 .map_err(|err| { 754 .map_err(|err| {
755 if !is_canceled(&*err) { 755 if !is_cancelled(&*err) {
756 log::error!("failed to compute diagnostics: {:?}", err); 756 log::error!("failed to compute diagnostics: {:?}", err);
757 } 757 }
758 () 758 ()
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index f5c8535a2..7428a3043 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -6,7 +6,7 @@ use std::{
6}; 6};
7 7
8use ide::{ 8use ide::{
9 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancelable, CompletionItem, 9 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancellable, CompletionItem,
10 CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, 10 CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
11 Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, 11 Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
12 InlayKind, InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, 12 InlayKind, InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable,
@@ -727,7 +727,7 @@ pub(crate) fn snippet_text_document_edit(
727pub(crate) fn snippet_text_document_ops( 727pub(crate) fn snippet_text_document_ops(
728 snap: &GlobalStateSnapshot, 728 snap: &GlobalStateSnapshot,
729 file_system_edit: FileSystemEdit, 729 file_system_edit: FileSystemEdit,
730) -> Cancelable<Vec<lsp_ext::SnippetDocumentChangeOperation>> { 730) -> Cancellable<Vec<lsp_ext::SnippetDocumentChangeOperation>> {
731 let mut ops = Vec::new(); 731 let mut ops = Vec::new();
732 match file_system_edit { 732 match file_system_edit {
733 FileSystemEdit::CreateFile { dst, initial_contents } => { 733 FileSystemEdit::CreateFile { dst, initial_contents } => {
@@ -757,7 +757,7 @@ pub(crate) fn snippet_text_document_ops(
757 let new_uri = snap.anchored_path(&dst); 757 let new_uri = snap.anchored_path(&dst);
758 let mut rename_file = 758 let mut rename_file =
759 lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None }; 759 lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None };
760 if snap.analysis.is_library_file(src) == Ok(true) 760 if snap.analysis.is_library_file(src).ok() == Some(true)
761 && snap.config.change_annotation_support() 761 && snap.config.change_annotation_support()
762 { 762 {
763 rename_file.annotation_id = Some(outside_workspace_annotation_id()) 763 rename_file.annotation_id = Some(outside_workspace_annotation_id())
@@ -1178,6 +1178,7 @@ mod tests {
1178 &ide::CompletionConfig { 1178 &ide::CompletionConfig {
1179 enable_postfix_completions: true, 1179 enable_postfix_completions: true,
1180 enable_imports_on_the_fly: true, 1180 enable_imports_on_the_fly: true,
1181 enable_self_on_the_fly: true,
1181 add_call_parenthesis: true, 1182 add_call_parenthesis: true,
1182 add_call_argument_snippets: true, 1183 add_call_argument_snippets: true,
1183 snippet_cap: SnippetCap::new(true), 1184 snippet_cap: SnippetCap::new(true),
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md
index 39edf9e19..2624069a5 100644
--- a/docs/dev/architecture.md
+++ b/docs/dev/architecture.md
@@ -447,3 +447,8 @@ This is cheap enough to enable in production.
447 447
448Similarly, we save live object counting (`RA_COUNT=1`). 448Similarly, we save live object counting (`RA_COUNT=1`).
449It is not cheap enough to enable in prod, and this is a bug which should be fixed. 449It is not cheap enough to enable in prod, and this is a bug which should be fixed.
450
451### Configurability
452
453rust-analyzer strives to be as configurable as possible while offering reasonable defaults where no configuration exists yet.
454There will always be features that some people find more annoying than helpful, so giving the users the ability to tweak or disable these is a big part of offering a good user experience.
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 4a5782a57..dbd9a3503 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -136,6 +136,12 @@ Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
136Toggles the additional completions that automatically add imports when completed. 136Toggles the additional completions that automatically add imports when completed.
137Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. 137Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
138-- 138--
139[[rust-analyzer.completion.autoself.enable]]rust-analyzer.completion.autoself.enable (default: `true`)::
140+
141--
142Toggles the additional completions that automatically show method calls and field accesses
143with `self` prefixed to them when inside a method.
144--
139[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: 145[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`)::
140+ 146+
141-- 147--
diff --git a/editors/code/package.json b/editors/code/package.json
index 5b80cc1f9..42a06e137 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -572,6 +572,11 @@
572 "default": true, 572 "default": true,
573 "type": "boolean" 573 "type": "boolean"
574 }, 574 },
575 "rust-analyzer.completion.autoself.enable": {
576 "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses\nwith `self` prefixed to them when inside a method.",
577 "default": true,
578 "type": "boolean"
579 },
575 "rust-analyzer.diagnostics.enable": { 580 "rust-analyzer.diagnostics.enable": {
576 "markdownDescription": "Whether to show native rust-analyzer diagnostics.", 581 "markdownDescription": "Whether to show native rust-analyzer diagnostics.",
577 "default": true, 582 "default": true,