aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/Cargo.toml2
-rw-r--r--crates/assists/src/assist_config.rs3
-rw-r--r--crates/assists/src/ast_transform.rs3
-rw-r--r--crates/assists/src/handlers/auto_import.rs5
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs16
-rw-r--r--crates/assists/src/handlers/fill_match_arms.rs9
-rw-r--r--crates/assists/src/handlers/generate_from_impl_for_enum.rs3
-rw-r--r--crates/assists/src/handlers/merge_imports.rs6
-rw-r--r--crates/assists/src/handlers/qualify_path.rs2
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs4
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs6
-rw-r--r--crates/assists/src/utils.rs199
-rw-r--r--crates/completion/Cargo.toml1
-rw-r--r--crates/completion/src/completions/record.rs4
-rw-r--r--crates/completion/src/completions/unqualified_path.rs2
-rw-r--r--crates/completion/src/config.rs2
-rw-r--r--crates/completion/src/item.rs38
-rw-r--r--crates/completion/src/render.rs25
-rw-r--r--crates/completion/src/render/enum_variant.rs15
-rw-r--r--crates/completion/src/render/function.rs17
-rw-r--r--crates/completion/src/render/macro_.rs17
-rw-r--r--crates/hir/src/diagnostics.rs2
-rw-r--r--crates/hir_def/src/diagnostics.rs67
-rw-r--r--crates/hir_def/src/nameres.rs87
-rw-r--r--crates/hir_def/src/nameres/collector.rs27
-rw-r--r--crates/hir_expand/src/db.rs15
-rw-r--r--crates/hir_expand/src/diagnostics.rs9
-rw-r--r--crates/hir_expand/src/lib.rs2
-rw-r--r--crates/hir_expand/src/proc_macro.rs2
-rw-r--r--crates/ide/src/diagnostics.rs9
-rw-r--r--crates/ide/src/inlay_hints.rs4
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/ide_db/Cargo.toml3
-rw-r--r--crates/ide_db/src/helpers.rs203
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs (renamed from crates/assists/src/utils/insert_use.rs)9
-rw-r--r--crates/ide_db/src/lib.rs1
-rw-r--r--crates/mbe/src/lib.rs2
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/config.rs6
-rw-r--r--crates/rust-analyzer/src/main_loop.rs104
-rw-r--r--crates/rust-analyzer/src/reload.rs2
-rw-r--r--crates/syntax/Cargo.toml1
-rw-r--r--crates/syntax/src/algo.rs13
43 files changed, 593 insertions, 360 deletions
diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml
index 108f656e9..3fd8327d6 100644
--- a/crates/assists/Cargo.toml
+++ b/crates/assists/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14itertools = "0.9.0" 14itertools = "0.9.0"
15either = "1.5.3" 15either = "1.6.1"
16 16
17stdx = { path = "../stdx", version = "0.0.0" } 17stdx = { path = "../stdx", version = "0.0.0" }
18syntax = { path = "../syntax", version = "0.0.0" } 18syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs
index b24527ec4..786224cfa 100644
--- a/crates/assists/src/assist_config.rs
+++ b/crates/assists/src/assist_config.rs
@@ -5,8 +5,9 @@
5//! assists if we are allowed to. 5//! assists if we are allowed to.
6 6
7use hir::PrefixKind; 7use hir::PrefixKind;
8use ide_db::helpers::insert_use::MergeBehaviour;
8 9
9use crate::{utils::MergeBehaviour, AssistKind}; 10use crate::AssistKind;
10 11
11#[derive(Clone, Debug, PartialEq, Eq)] 12#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct AssistConfig { 13pub struct AssistConfig {
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
index ac72f3f02..66e4634b1 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -1,5 +1,6 @@
1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. 1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
2use hir::{HirDisplay, PathResolution, SemanticsScope}; 2use hir::{HirDisplay, PathResolution, SemanticsScope};
3use ide_db::helpers::mod_path_to_ast;
3use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
4use syntax::{ 5use syntax::{
5 algo::SyntaxRewriter, 6 algo::SyntaxRewriter,
@@ -7,8 +8,6 @@ use syntax::{
7 SyntaxNode, 8 SyntaxNode,
8}; 9};
9 10
10use crate::utils::mod_path_to_ast;
11
12pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { 11pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
13 SyntaxRewriter::from_fn(|element| match element { 12 SyntaxRewriter::from_fn(|element| match element {
14 syntax::SyntaxElement::Node(n) => { 13 syntax::SyntaxElement::Node(n) => {
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index d665837a2..bd5bba646 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -1,8 +1,11 @@
1use ide_db::helpers::{
2 insert_use::{insert_use, ImportScope},
3 mod_path_to_ast,
4};
1use syntax::ast; 5use syntax::ast;
2 6
3use crate::{ 7use crate::{
4 utils::import_assets::{ImportAssets, ImportCandidate}, 8 utils::import_assets::{ImportAssets, ImportCandidate},
5 utils::{insert_use, mod_path_to_ast, ImportScope},
6 AssistContext, AssistId, AssistKind, Assists, GroupLabel, 9 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
7}; 10};
8 11
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index cac77c49b..d85767b4e 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -2,6 +2,10 @@ use std::iter;
2 2
3use either::Either; 3use either::Either;
4use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; 4use hir::{AsName, EnumVariant, Module, ModuleDef, Name};
5use ide_db::helpers::{
6 insert_use::{insert_use, ImportScope},
7 mod_path_to_ast,
8};
5use ide_db::{defs::Definition, search::Reference, RootDatabase}; 9use ide_db::{defs::Definition, search::Reference, RootDatabase};
6use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
7use syntax::{ 11use syntax::{
@@ -10,10 +14,7 @@ use syntax::{
10 SourceFile, SyntaxElement, SyntaxNode, T, 14 SourceFile, SyntaxElement, SyntaxNode, T,
11}; 15};
12 16
13use crate::{ 17use crate::{AssistContext, AssistId, AssistKind, Assists};
14 utils::{insert_use, mod_path_to_ast, ImportScope},
15 AssistContext, AssistId, AssistKind, Assists,
16};
17 18
18// Assist: extract_struct_from_enum_variant 19// Assist: extract_struct_from_enum_variant
19// 20//
@@ -236,10 +237,9 @@ fn update_reference(
236 237
237#[cfg(test)] 238#[cfg(test)]
238mod tests { 239mod tests {
239 use crate::{ 240 use ide_db::helpers::FamousDefs;
240 tests::{check_assist, check_assist_not_applicable}, 241
241 utils::FamousDefs, 242 use crate::tests::{check_assist, check_assist_not_applicable};
242 };
243 243
244 use super::*; 244 use super::*;
245 245
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs
index eda45f5b3..ef12ef0cf 100644
--- a/crates/assists/src/handlers/fill_match_arms.rs
+++ b/crates/assists/src/handlers/fill_match_arms.rs
@@ -1,13 +1,14 @@
1use std::iter; 1use std::iter;
2 2
3use hir::{Adt, HasSource, ModuleDef, Semantics}; 3use hir::{Adt, HasSource, ModuleDef, Semantics};
4use ide_db::helpers::{mod_path_to_ast, FamousDefs};
4use ide_db::RootDatabase; 5use ide_db::RootDatabase;
5use itertools::Itertools; 6use itertools::Itertools;
6use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; 7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
7use test_utils::mark; 8use test_utils::mark;
8 9
9use crate::{ 10use crate::{
10 utils::{mod_path_to_ast, render_snippet, Cursor, FamousDefs}, 11 utils::{render_snippet, Cursor},
11 AssistContext, AssistId, AssistKind, Assists, 12 AssistContext, AssistId, AssistKind, Assists,
12}; 13};
13 14
@@ -212,12 +213,10 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O
212 213
213#[cfg(test)] 214#[cfg(test)]
214mod tests { 215mod tests {
216 use ide_db::helpers::FamousDefs;
215 use test_utils::mark; 217 use test_utils::mark;
216 218
217 use crate::{ 219 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
218 tests::{check_assist, check_assist_not_applicable, check_assist_target},
219 utils::FamousDefs,
220 };
221 220
222 use super::fill_match_arms; 221 use super::fill_match_arms;
223 222
diff --git a/crates/assists/src/handlers/generate_from_impl_for_enum.rs b/crates/assists/src/handlers/generate_from_impl_for_enum.rs
index 674e5a175..3c374e5d9 100644
--- a/crates/assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/assists/src/handlers/generate_from_impl_for_enum.rs
@@ -1,8 +1,9 @@
1use ide_db::helpers::FamousDefs;
1use ide_db::RootDatabase; 2use ide_db::RootDatabase;
2use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
3use test_utils::mark; 4use test_utils::mark;
4 5
5use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
6 7
7// Assist: generate_from_impl_for_enum 8// Assist: generate_from_impl_for_enum
8// 9//
diff --git a/crates/assists/src/handlers/merge_imports.rs b/crates/assists/src/handlers/merge_imports.rs
index fd9c9e03c..b7e853994 100644
--- a/crates/assists/src/handlers/merge_imports.rs
+++ b/crates/assists/src/handlers/merge_imports.rs
@@ -1,3 +1,4 @@
1use ide_db::helpers::insert_use::{try_merge_imports, try_merge_trees, MergeBehaviour};
1use syntax::{ 2use syntax::{
2 algo::{neighbor, SyntaxRewriter}, 3 algo::{neighbor, SyntaxRewriter},
3 ast, AstNode, 4 ast, AstNode,
@@ -5,10 +6,7 @@ use syntax::{
5 6
6use crate::{ 7use crate::{
7 assist_context::{AssistContext, Assists}, 8 assist_context::{AssistContext, Assists},
8 utils::{ 9 utils::next_prev,
9 insert_use::{try_merge_imports, try_merge_trees},
10 next_prev, MergeBehaviour,
11 },
12 AssistId, AssistKind, 10 AssistId, AssistKind,
13}; 11};
14 12
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index d5bc4e574..6f9810fe8 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -1,6 +1,7 @@
1use std::iter; 1use std::iter;
2 2
3use hir::AsName; 3use hir::AsName;
4use ide_db::helpers::mod_path_to_ast;
4use ide_db::RootDatabase; 5use ide_db::RootDatabase;
5use syntax::{ 6use syntax::{
6 ast, 7 ast,
@@ -12,7 +13,6 @@ use test_utils::mark;
12use crate::{ 13use crate::{
13 assist_context::{AssistContext, Assists}, 14 assist_context::{AssistContext, Assists},
14 utils::import_assets::{ImportAssets, ImportCandidate}, 15 utils::import_assets::{ImportAssets, ImportCandidate},
15 utils::mod_path_to_ast,
16 AssistId, AssistKind, GroupLabel, 16 AssistId, AssistKind, GroupLabel,
17}; 17};
18 18
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
index 453a6cebf..4d6a1956b 100644
--- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,3 +1,4 @@
1use ide_db::helpers::mod_path_to_ast;
1use ide_db::imports_locator; 2use ide_db::imports_locator;
2use itertools::Itertools; 3use itertools::Itertools;
3use syntax::{ 4use syntax::{
@@ -10,8 +11,7 @@ use syntax::{
10use crate::{ 11use crate::{
11 assist_context::{AssistBuilder, AssistContext, Assists}, 12 assist_context::{AssistBuilder, AssistContext, Assists},
12 utils::{ 13 utils::{
13 add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor, 14 add_trait_assoc_items_to_impl, filter_assoc_items, render_snippet, Cursor, DefaultMethods,
14 DefaultMethods,
15 }, 15 },
16 AssistId, AssistKind, 16 AssistId, AssistKind,
17}; 17};
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index a66db9ae3..8bdf9eea5 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -1,10 +1,8 @@
1use ide_db::helpers::insert_use::{insert_use, ImportScope};
1use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; 2use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode};
2use test_utils::mark; 3use test_utils::mark;
3 4
4use crate::{ 5use crate::{AssistContext, AssistId, AssistKind, Assists};
5 utils::{insert_use, ImportScope},
6 AssistContext, AssistId, AssistKind, Assists,
7};
8 6
9// Assist: replace_qualified_name_with_use 7// Assist: replace_qualified_name_with_use
10// 8//
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 66c0cdd5f..01f5c291f 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -1,10 +1,9 @@
1//! Assorted functions shared by several assists. 1//! Assorted functions shared by several assists.
2pub(crate) mod insert_use;
3pub(crate) mod import_assets; 2pub(crate) mod import_assets;
4 3
5use std::ops; 4use std::ops;
6 5
7use hir::{Crate, Enum, HasSource, Module, ScopeDef, Semantics, Trait}; 6use hir::HasSource;
8use ide_db::RootDatabase; 7use ide_db::RootDatabase;
9use itertools::Itertools; 8use itertools::Itertools;
10use syntax::{ 9use syntax::{
@@ -22,29 +21,6 @@ use crate::{
22 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 21 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
23}; 22};
24 23
25pub use insert_use::{insert_use, ImportScope, MergeBehaviour};
26
27pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
28 let mut segments = Vec::new();
29 let mut is_abs = false;
30 match path.kind {
31 hir::PathKind::Plain => {}
32 hir::PathKind::Super(0) => segments.push(make::path_segment_self()),
33 hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())),
34 hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => {
35 segments.push(make::path_segment_crate())
36 }
37 hir::PathKind::Abs => is_abs = true,
38 }
39
40 segments.extend(
41 path.segments
42 .iter()
43 .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))),
44 );
45 make::path_from_segments(segments, is_abs)
46}
47
48pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { 24pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
49 extract_trivial_expression(&block) 25 extract_trivial_expression(&block)
50 .filter(|expr| !expr.syntax().text().contains_char('\n')) 26 .filter(|expr| !expr.syntax().text().contains_char('\n'))
@@ -259,179 +235,6 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
259 } 235 }
260} 236}
261 237
262/// Helps with finding well-know things inside the standard library. This is
263/// somewhat similar to the known paths infra inside hir, but it different; We
264/// want to make sure that IDE specific paths don't become interesting inside
265/// the compiler itself as well.
266pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Crate>);
267
268#[allow(non_snake_case)]
269impl FamousDefs<'_, '_> {
270 pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
271pub mod convert {
272 pub trait From<T> {
273 fn from(t: T) -> Self;
274 }
275}
276
277pub mod default {
278 pub trait Default {
279 fn default() -> Self;
280 }
281}
282
283pub mod iter {
284 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
285 mod traits {
286 pub(crate) mod iterator {
287 use crate::option::Option;
288 pub trait Iterator {
289 type Item;
290 fn next(&mut self) -> Option<Self::Item>;
291 fn by_ref(&mut self) -> &mut Self {
292 self
293 }
294 fn take(self, n: usize) -> crate::iter::Take<Self> {
295 crate::iter::Take { inner: self }
296 }
297 }
298
299 impl<I: Iterator> Iterator for &mut I {
300 type Item = I::Item;
301 fn next(&mut self) -> Option<I::Item> {
302 (**self).next()
303 }
304 }
305 }
306 pub(crate) mod collect {
307 pub trait IntoIterator {
308 type Item;
309 }
310 }
311 }
312
313 pub use self::sources::*;
314 pub(crate) mod sources {
315 use super::Iterator;
316 use crate::option::Option::{self, *};
317 pub struct Repeat<A> {
318 element: A,
319 }
320
321 pub fn repeat<T>(elt: T) -> Repeat<T> {
322 Repeat { element: elt }
323 }
324
325 impl<A> Iterator for Repeat<A> {
326 type Item = A;
327
328 fn next(&mut self) -> Option<A> {
329 None
330 }
331 }
332 }
333
334 pub use self::adapters::*;
335 pub(crate) mod adapters {
336 use super::Iterator;
337 use crate::option::Option::{self, *};
338 pub struct Take<I> { pub(crate) inner: I }
339 impl<I> Iterator for Take<I> where I: Iterator {
340 type Item = <I as Iterator>::Item;
341 fn next(&mut self) -> Option<<I as Iterator>::Item> {
342 None
343 }
344 }
345 }
346}
347
348pub mod option {
349 pub enum Option<T> { None, Some(T)}
350}
351
352pub mod prelude {
353 pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default};
354}
355#[prelude_import]
356pub use prelude::*;
357"#;
358
359 pub fn core(&self) -> Option<Crate> {
360 self.find_crate("core")
361 }
362
363 pub(crate) fn core_convert_From(&self) -> Option<Trait> {
364 self.find_trait("core:convert:From")
365 }
366
367 pub(crate) fn core_option_Option(&self) -> Option<Enum> {
368 self.find_enum("core:option:Option")
369 }
370
371 pub fn core_default_Default(&self) -> Option<Trait> {
372 self.find_trait("core:default:Default")
373 }
374
375 pub fn core_iter_Iterator(&self) -> Option<Trait> {
376 self.find_trait("core:iter:traits:iterator:Iterator")
377 }
378
379 pub fn core_iter(&self) -> Option<Module> {
380 self.find_module("core:iter")
381 }
382
383 fn find_trait(&self, path: &str) -> Option<Trait> {
384 match self.find_def(path)? {
385 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
386 _ => None,
387 }
388 }
389
390 fn find_enum(&self, path: &str) -> Option<Enum> {
391 match self.find_def(path)? {
392 hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it),
393 _ => None,
394 }
395 }
396
397 fn find_module(&self, path: &str) -> Option<Module> {
398 match self.find_def(path)? {
399 hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it),
400 _ => None,
401 }
402 }
403
404 fn find_crate(&self, name: &str) -> Option<Crate> {
405 let krate = self.1?;
406 let db = self.0.db;
407 let res =
408 krate.dependencies(db).into_iter().find(|dep| dep.name.to_string() == name)?.krate;
409 Some(res)
410 }
411
412 fn find_def(&self, path: &str) -> Option<ScopeDef> {
413 let db = self.0.db;
414 let mut path = path.split(':');
415 let trait_ = path.next_back()?;
416 let std_crate = path.next()?;
417 let std_crate = self.find_crate(std_crate)?;
418 let mut module = std_crate.root_module(db);
419 for segment in path {
420 module = module.children(db).find_map(|child| {
421 let name = child.name(db)?;
422 if name.to_string() == segment {
423 Some(child)
424 } else {
425 None
426 }
427 })?;
428 }
429 let def =
430 module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1;
431 Some(def)
432 }
433}
434
435pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { 238pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
436 [Direction::Next, Direction::Prev].iter().copied() 239 [Direction::Next, Direction::Prev].iter().copied()
437} 240}
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml
index e7df9d955..35e169a28 100644
--- a/crates/completion/Cargo.toml
+++ b/crates/completion/Cargo.toml
@@ -15,7 +15,6 @@ log = "0.4.8"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16either = "1.6.1" 16either = "1.6.1"
17 17
18assists = { path = "../assists", version = "0.0.0" }
19stdx = { path = "../stdx", version = "0.0.0" } 18stdx = { path = "../stdx", version = "0.0.0" }
20syntax = { path = "../syntax", version = "0.0.0" } 19syntax = { path = "../syntax", version = "0.0.0" }
21text_edit = { path = "../text_edit", version = "0.0.0" } 20text_edit = { path = "../text_edit", version = "0.0.0" }
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs
index 2049b9d09..eaa44c97d 100644
--- a/crates/completion/src/completions/record.rs
+++ b/crates/completion/src/completions/record.rs
@@ -1,5 +1,5 @@
1//! Complete fields in record literals and patterns. 1//! Complete fields in record literals and patterns.
2use assists::utils::FamousDefs; 2use ide_db::helpers::FamousDefs;
3use syntax::ast::Expr; 3use syntax::ast::Expr;
4 4
5use crate::{ 5use crate::{
@@ -45,8 +45,8 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
45 45
46#[cfg(test)] 46#[cfg(test)]
47mod tests { 47mod tests {
48 use assists::utils::FamousDefs;
49 use expect_test::{expect, Expect}; 48 use expect_test::{expect, Expect};
49 use ide_db::helpers::FamousDefs;
50 50
51 use crate::{test_utils::completion_list, CompletionKind}; 51 use crate::{test_utils::completion_list, CompletionKind};
52 52
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 3bd776905..81691cd7f 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -1,8 +1,8 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use assists::utils::ImportScope;
4use either::Either; 3use either::Either;
5use hir::{Adt, ModuleDef, ScopeDef, Type}; 4use hir::{Adt, ModuleDef, ScopeDef, Type};
5use ide_db::helpers::insert_use::ImportScope;
6use ide_db::imports_locator; 6use ide_db::imports_locator;
7use syntax::AstNode; 7use syntax::AstNode;
8use test_utils::mark; 8use test_utils::mark;
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index f50735372..654a76f7b 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -4,7 +4,7 @@
4//! module, and we use to statically check that we only produce snippet 4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to. 5//! completions if we are allowed to.
6 6
7use assists::utils::MergeBehaviour; 7use ide_db::helpers::insert_use::MergeBehaviour;
8 8
9#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct CompletionConfig { 10pub struct CompletionConfig {
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index b13c3f376..e85549fef 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -2,8 +2,11 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour};
6use hir::{Documentation, ModPath, Mutability}; 5use hir::{Documentation, ModPath, Mutability};
6use ide_db::helpers::{
7 insert_use::{self, ImportScope, MergeBehaviour},
8 mod_path_to_ast,
9};
7use syntax::{algo, TextRange}; 10use syntax::{algo, TextRange};
8use text_edit::TextEdit; 11use text_edit::TextEdit;
9 12
@@ -201,7 +204,7 @@ impl CompletionItem {
201 trigger_call_info: None, 204 trigger_call_info: None,
202 score: None, 205 score: None,
203 ref_match: None, 206 ref_match: None,
204 import_data: None, 207 import_to_add: None,
205 } 208 }
206 } 209 }
207 210
@@ -255,13 +258,21 @@ impl CompletionItem {
255 } 258 }
256} 259}
257 260
261/// An extra import to add after the completion is applied.
262#[derive(Clone)]
263pub(crate) struct ImportToAdd {
264 pub(crate) import_path: ModPath,
265 pub(crate) import_scope: ImportScope,
266 pub(crate) merge_behaviour: Option<MergeBehaviour>,
267}
268
258/// A helper to make `CompletionItem`s. 269/// A helper to make `CompletionItem`s.
259#[must_use] 270#[must_use]
260#[derive(Clone)] 271#[derive(Clone)]
261pub(crate) struct Builder { 272pub(crate) struct Builder {
262 source_range: TextRange, 273 source_range: TextRange,
263 completion_kind: CompletionKind, 274 completion_kind: CompletionKind,
264 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, 275 import_to_add: Option<ImportToAdd>,
265 label: String, 276 label: String,
266 insert_text: Option<String>, 277 insert_text: Option<String>,
267 insert_text_format: InsertTextFormat, 278 insert_text_format: InsertTextFormat,
@@ -278,14 +289,16 @@ pub(crate) struct Builder {
278 289
279impl Builder { 290impl Builder {
280 pub(crate) fn build(self) -> CompletionItem { 291 pub(crate) fn build(self) -> CompletionItem {
292 let _p = profile::span("item::Builder::build");
293
281 let mut label = self.label; 294 let mut label = self.label;
282 let mut lookup = self.lookup; 295 let mut lookup = self.lookup;
283 let mut insert_text = self.insert_text; 296 let mut insert_text = self.insert_text;
284 let mut text_edits = TextEdit::builder(); 297 let mut text_edits = TextEdit::builder();
285 298
286 if let Some((import_path, import_scope, merge_behaviour)) = self.import_data { 299 if let Some(import_data) = self.import_to_add {
287 let import = mod_path_to_ast(&import_path); 300 let import = mod_path_to_ast(&import_data.import_path);
288 let mut import_path_without_last_segment = import_path; 301 let mut import_path_without_last_segment = import_data.import_path;
289 let _ = import_path_without_last_segment.segments.pop(); 302 let _ = import_path_without_last_segment.segments.pop();
290 303
291 if !import_path_without_last_segment.segments.is_empty() { 304 if !import_path_without_last_segment.segments.is_empty() {
@@ -298,7 +311,11 @@ impl Builder {
298 label = format!("{}::{}", import_path_without_last_segment, label); 311 label = format!("{}::{}", import_path_without_last_segment, label);
299 } 312 }
300 313
301 let rewriter = insert_use(&import_scope, import, merge_behaviour); 314 let rewriter = insert_use::insert_use(
315 &import_data.import_scope,
316 import,
317 import_data.merge_behaviour,
318 );
302 if let Some(old_ast) = rewriter.rewrite_root() { 319 if let Some(old_ast) = rewriter.rewrite_root() {
303 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); 320 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits);
304 } 321 }
@@ -390,11 +407,8 @@ impl Builder {
390 self.trigger_call_info = Some(true); 407 self.trigger_call_info = Some(true);
391 self 408 self
392 } 409 }
393 pub(crate) fn import_data( 410 pub(crate) fn add_import(mut self, import_to_add: Option<ImportToAdd>) -> Builder {
394 mut self, 411 self.import_to_add = import_to_add;
395 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
396 ) -> Builder {
397 self.import_data = import_data;
398 self 412 self
399 } 413 }
400 pub(crate) fn set_ref_match( 414 pub(crate) fn set_ref_match(
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index bce02f577..504757a6a 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -9,15 +9,15 @@ pub(crate) mod type_alias;
9 9
10mod builder_ext; 10mod builder_ext;
11 11
12use assists::utils::{ImportScope, MergeBehaviour};
13use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; 12use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type};
13use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour};
14use ide_db::RootDatabase; 14use ide_db::RootDatabase;
15use syntax::TextRange; 15use syntax::TextRange;
16use test_utils::mark; 16use test_utils::mark;
17 17
18use crate::{ 18use crate::{
19 config::SnippetCap, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 19 config::SnippetCap, item::ImportToAdd, CompletionContext, CompletionItem, CompletionItemKind,
20 CompletionScore, 20 CompletionKind, CompletionScore,
21}; 21};
22 22
23use crate::render::{enum_variant::render_enum_variant, function::render_fn, macro_::render_macro}; 23use crate::render::{enum_variant::render_enum_variant, function::render_fn, macro_::render_macro};
@@ -48,15 +48,15 @@ pub(crate) fn render_resolution<'a>(
48 48
49pub(crate) fn render_resolution_with_import<'a>( 49pub(crate) fn render_resolution_with_import<'a>(
50 ctx: RenderContext<'a>, 50 ctx: RenderContext<'a>,
51 import: ModPath, 51 import_path: ModPath,
52 import_scope: ImportScope, 52 import_scope: ImportScope,
53 merge_behaviour: Option<MergeBehaviour>, 53 merge_behaviour: Option<MergeBehaviour>,
54 resolution: &ScopeDef, 54 resolution: &ScopeDef,
55) -> Option<CompletionItem> { 55) -> Option<CompletionItem> {
56 let local_name = import.segments.last()?.to_string(); 56 let local_name = import_path.segments.last()?.to_string();
57 Render::new(ctx).render_resolution( 57 Render::new(ctx).render_resolution(
58 local_name, 58 local_name,
59 Some((import, import_scope, merge_behaviour)), 59 Some(ImportToAdd { import_path, import_scope, merge_behaviour }),
60 resolution, 60 resolution,
61 ) 61 )
62} 62}
@@ -147,7 +147,7 @@ impl<'a> Render<'a> {
147 fn render_resolution( 147 fn render_resolution(
148 self, 148 self,
149 local_name: String, 149 local_name: String,
150 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, 150 import_to_add: Option<ImportToAdd>,
151 resolution: &ScopeDef, 151 resolution: &ScopeDef,
152 ) -> Option<CompletionItem> { 152 ) -> Option<CompletionItem> {
153 let _p = profile::span("render_resolution"); 153 let _p = profile::span("render_resolution");
@@ -160,15 +160,16 @@ impl<'a> Render<'a> {
160 160
161 let kind = match resolution { 161 let kind = match resolution {
162 ScopeDef::ModuleDef(Function(func)) => { 162 ScopeDef::ModuleDef(Function(func)) => {
163 let item = render_fn(self.ctx, import_data, Some(local_name), *func); 163 let item = render_fn(self.ctx, import_to_add, Some(local_name), *func);
164 return Some(item); 164 return Some(item);
165 } 165 }
166 ScopeDef::ModuleDef(EnumVariant(var)) => { 166 ScopeDef::ModuleDef(EnumVariant(var)) => {
167 let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None); 167 let item =
168 render_enum_variant(self.ctx, import_to_add, Some(local_name), *var, None);
168 return Some(item); 169 return Some(item);
169 } 170 }
170 ScopeDef::MacroDef(mac) => { 171 ScopeDef::MacroDef(mac) => {
171 let item = render_macro(self.ctx, import_data, local_name, *mac); 172 let item = render_macro(self.ctx, import_to_add, local_name, *mac);
172 return item; 173 return item;
173 } 174 }
174 175
@@ -193,7 +194,7 @@ impl<'a> Render<'a> {
193 local_name, 194 local_name,
194 ) 195 )
195 .kind(CompletionItemKind::UnresolvedReference) 196 .kind(CompletionItemKind::UnresolvedReference)
196 .import_data(import_data) 197 .add_import(import_to_add)
197 .build(); 198 .build();
198 return Some(item); 199 return Some(item);
199 } 200 }
@@ -248,7 +249,7 @@ impl<'a> Render<'a> {
248 249
249 let item = item 250 let item = item
250 .kind(kind) 251 .kind(kind)
251 .import_data(import_data) 252 .add_import(import_to_add)
252 .set_documentation(docs) 253 .set_documentation(docs)
253 .set_ref_match(ref_match) 254 .set_ref_match(ref_match)
254 .build(); 255 .build();
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
index 6070e9b1d..f4bd02f25 100644
--- a/crates/completion/src/render/enum_variant.rs
+++ b/crates/completion/src/render/enum_variant.rs
@@ -1,23 +1,23 @@
1//! Renderer for `enum` variants. 1//! Renderer for `enum` variants.
2 2
3use assists::utils::{ImportScope, MergeBehaviour};
4use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; 3use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
5use itertools::Itertools; 4use itertools::Itertools;
6use test_utils::mark; 5use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionItemKind, CompletionKind}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd},
10 render::{builder_ext::Params, RenderContext}, 9 render::{builder_ext::Params, RenderContext},
11}; 10};
12 11
13pub(crate) fn render_enum_variant<'a>( 12pub(crate) fn render_enum_variant<'a>(
14 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
15 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, 14 import_to_add: Option<ImportToAdd>,
16 local_name: Option<String>, 15 local_name: Option<String>,
17 variant: hir::EnumVariant, 16 variant: hir::EnumVariant,
18 path: Option<ModPath>, 17 path: Option<ModPath>,
19) -> CompletionItem { 18) -> CompletionItem {
20 EnumVariantRender::new(ctx, local_name, variant, path).render(import_data) 19 let _p = profile::span("render_enum_variant");
20 EnumVariantRender::new(ctx, local_name, variant, path).render(import_to_add)
21} 21}
22 22
23#[derive(Debug)] 23#[derive(Debug)]
@@ -62,10 +62,7 @@ impl<'a> EnumVariantRender<'a> {
62 } 62 }
63 } 63 }
64 64
65 fn render( 65 fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem {
66 self,
67 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
68 ) -> CompletionItem {
69 let mut builder = CompletionItem::new( 66 let mut builder = CompletionItem::new(
70 CompletionKind::Reference, 67 CompletionKind::Reference,
71 self.ctx.source_range(), 68 self.ctx.source_range(),
@@ -74,7 +71,7 @@ impl<'a> EnumVariantRender<'a> {
74 .kind(CompletionItemKind::EnumVariant) 71 .kind(CompletionItemKind::EnumVariant)
75 .set_documentation(self.variant.docs(self.ctx.db())) 72 .set_documentation(self.variant.docs(self.ctx.db()))
76 .set_deprecated(self.ctx.is_deprecated(self.variant)) 73 .set_deprecated(self.ctx.is_deprecated(self.variant))
77 .import_data(import_data) 74 .add_import(import_to_add)
78 .detail(self.detail()); 75 .detail(self.detail());
79 76
80 if self.variant_kind == StructKind::Tuple { 77 if self.variant_kind == StructKind::Tuple {
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index 9dd5cd18c..542383d7e 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -1,21 +1,21 @@
1//! Renderer for function calls. 1//! Renderer for function calls.
2 2
3use assists::utils::{ImportScope, MergeBehaviour}; 3use hir::{HasSource, Type};
4use hir::{HasSource, ModPath, Type};
5use syntax::{ast::Fn, display::function_declaration}; 4use syntax::{ast::Fn, display::function_declaration};
6 5
7use crate::{ 6use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind}, 7 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd},
9 render::{builder_ext::Params, RenderContext}, 8 render::{builder_ext::Params, RenderContext},
10}; 9};
11 10
12pub(crate) fn render_fn<'a>( 11pub(crate) fn render_fn<'a>(
13 ctx: RenderContext<'a>, 12 ctx: RenderContext<'a>,
14 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, 13 import_to_add: Option<ImportToAdd>,
15 local_name: Option<String>, 14 local_name: Option<String>,
16 fn_: hir::Function, 15 fn_: hir::Function,
17) -> CompletionItem { 16) -> CompletionItem {
18 FunctionRender::new(ctx, local_name, fn_).render(import_data) 17 let _p = profile::span("render_fn");
18 FunctionRender::new(ctx, local_name, fn_).render(import_to_add)
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug)]
@@ -38,10 +38,7 @@ impl<'a> FunctionRender<'a> {
38 FunctionRender { ctx, name, fn_, ast_node } 38 FunctionRender { ctx, name, fn_, ast_node }
39 } 39 }
40 40
41 fn render( 41 fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem {
42 self,
43 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
44 ) -> CompletionItem {
45 let params = self.params(); 42 let params = self.params();
46 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
47 .kind(self.kind()) 44 .kind(self.kind())
@@ -49,7 +46,7 @@ impl<'a> FunctionRender<'a> {
49 .set_deprecated(self.ctx.is_deprecated(self.fn_)) 46 .set_deprecated(self.ctx.is_deprecated(self.fn_))
50 .detail(self.detail()) 47 .detail(self.detail())
51 .add_call_parens(self.ctx.completion, self.name, params) 48 .add_call_parens(self.ctx.completion, self.name, params)
52 .import_data(import_data) 49 .add_import(import_to_add)
53 .build() 50 .build()
54 } 51 }
55 52
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index fead59e41..b4ab32c6e 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -1,22 +1,22 @@
1//! Renderer for macro invocations. 1//! Renderer for macro invocations.
2 2
3use assists::utils::{ImportScope, MergeBehaviour}; 3use hir::{Documentation, HasSource};
4use hir::{Documentation, HasSource, ModPath};
5use syntax::display::macro_label; 4use syntax::display::macro_label;
6use test_utils::mark; 5use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 item::{CompletionItem, CompletionItemKind, CompletionKind}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd},
10 render::RenderContext, 9 render::RenderContext,
11}; 10};
12 11
13pub(crate) fn render_macro<'a>( 12pub(crate) fn render_macro<'a>(
14 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
15 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>, 14 import_to_add: Option<ImportToAdd>,
16 name: String, 15 name: String,
17 macro_: hir::MacroDef, 16 macro_: hir::MacroDef,
18) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
19 MacroRender::new(ctx, name, macro_).render(import_data) 18 let _p = profile::span("render_macro");
19 MacroRender::new(ctx, name, macro_).render(import_to_add)
20} 20}
21 21
22#[derive(Debug)] 22#[derive(Debug)]
@@ -38,10 +38,7 @@ impl<'a> MacroRender<'a> {
38 MacroRender { ctx, name, macro_, docs, bra, ket } 38 MacroRender { ctx, name, macro_, docs, bra, ket }
39 } 39 }
40 40
41 fn render( 41 fn render(&self, import_to_add: Option<ImportToAdd>) -> Option<CompletionItem> {
42 &self,
43 import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
44 ) -> Option<CompletionItem> {
45 // FIXME: Currently proc-macro do not have ast-node, 42 // FIXME: Currently proc-macro do not have ast-node,
46 // such that it does not have source 43 // such that it does not have source
47 if self.macro_.is_proc_macro() { 44 if self.macro_.is_proc_macro() {
@@ -53,7 +50,7 @@ impl<'a> MacroRender<'a> {
53 .kind(CompletionItemKind::Macro) 50 .kind(CompletionItemKind::Macro)
54 .set_documentation(self.docs.clone()) 51 .set_documentation(self.docs.clone())
55 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 52 .set_deprecated(self.ctx.is_deprecated(self.macro_))
56 .import_data(import_data) 53 .add_import(import_to_add)
57 .detail(self.detail()); 54 .detail(self.detail());
58 55
59 let needs_bang = self.needs_bang(); 56 let needs_bang = self.needs_bang();
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index d9ad8db6f..eaf1a14ec 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -1,5 +1,5 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule}; 2pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule, UnresolvedProcMacro};
3pub use hir_expand::diagnostics::{ 3pub use hir_expand::diagnostics::{
4 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, 4 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
5}; 5};
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index b221b290c..c71266dc0 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -6,7 +6,7 @@ use stdx::format_to;
6use cfg::{CfgExpr, CfgOptions, DnfExpr}; 6use cfg::{CfgExpr, CfgOptions, DnfExpr};
7use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; 7use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
8use hir_expand::{HirFileId, InFile}; 8use hir_expand::{HirFileId, InFile};
9use syntax::{ast, AstPtr, SyntaxNodePtr}; 9use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
10 10
11use crate::{db::DefDatabase, DefWithBodyId}; 11use crate::{db::DefDatabase, DefWithBodyId};
12 12
@@ -127,3 +127,68 @@ impl Diagnostic for InactiveCode {
127 self 127 self
128 } 128 }
129} 129}
130
131// Diagnostic: unresolved-proc-macro
132//
133// This diagnostic is shown when a procedural macro can not be found. This usually means that
134// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
135// but can also indicate project setup problems.
136#[derive(Debug, Clone, Eq, PartialEq)]
137pub struct UnresolvedProcMacro {
138 pub file: HirFileId,
139 pub node: SyntaxNodePtr,
140 /// If the diagnostic can be pinpointed more accurately than via `node`, this is the `TextRange`
141 /// to use instead.
142 pub precise_location: Option<TextRange>,
143 pub macro_name: Option<String>,
144}
145
146impl Diagnostic for UnresolvedProcMacro {
147 fn code(&self) -> DiagnosticCode {
148 DiagnosticCode("unresolved-proc-macro")
149 }
150
151 fn message(&self) -> String {
152 match &self.macro_name {
153 Some(name) => format!("proc macro `{}` not expanded", name),
154 None => "proc macro not expanded".to_string(),
155 }
156 }
157
158 fn display_source(&self) -> InFile<SyntaxNodePtr> {
159 InFile::new(self.file, self.node.clone())
160 }
161
162 fn as_any(&self) -> &(dyn Any + Send + 'static) {
163 self
164 }
165}
166
167// Diagnostic: macro-error
168//
169// This diagnostic is shown for macro expansion errors.
170#[derive(Debug, Clone, Eq, PartialEq)]
171pub struct MacroError {
172 pub file: HirFileId,
173 pub node: SyntaxNodePtr,
174 pub message: String,
175}
176
177impl Diagnostic for MacroError {
178 fn code(&self) -> DiagnosticCode {
179 DiagnosticCode("macro-error")
180 }
181 fn message(&self) -> String {
182 self.message.clone()
183 }
184 fn display_source(&self) -> InFile<SyntaxNodePtr> {
185 InFile::new(self.file, self.node.clone())
186 }
187 fn as_any(&self) -> &(dyn Any + Send + 'static) {
188 self
189 }
190 fn is_experimental(&self) -> bool {
191 // Newly added and not very well-tested, might contain false positives.
192 true
193 }
194}
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 202a7dcb6..ffd0381d4 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -286,8 +286,9 @@ mod diagnostics {
286 use cfg::{CfgExpr, CfgOptions}; 286 use cfg::{CfgExpr, CfgOptions};
287 use hir_expand::diagnostics::DiagnosticSink; 287 use hir_expand::diagnostics::DiagnosticSink;
288 use hir_expand::hygiene::Hygiene; 288 use hir_expand::hygiene::Hygiene;
289 use hir_expand::InFile; 289 use hir_expand::{InFile, MacroCallKind};
290 use syntax::{ast, AstPtr}; 290 use syntax::ast::AttrsOwner;
291 use syntax::{ast, AstNode, AstPtr, SyntaxKind, SyntaxNodePtr};
291 292
292 use crate::path::ModPath; 293 use crate::path::ModPath;
293 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; 294 use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId};
@@ -301,6 +302,10 @@ mod diagnostics {
301 UnresolvedImport { ast: AstId<ast::Use>, index: usize }, 302 UnresolvedImport { ast: AstId<ast::Use>, index: usize },
302 303
303 UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions }, 304 UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
305
306 UnresolvedProcMacro { ast: MacroCallKind },
307
308 MacroError { ast: MacroCallKind, message: String },
304 } 309 }
305 310
306 #[derive(Debug, PartialEq, Eq)] 311 #[derive(Debug, PartialEq, Eq)]
@@ -348,6 +353,18 @@ mod diagnostics {
348 Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } } 353 Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
349 } 354 }
350 355
356 pub(super) fn unresolved_proc_macro(container: LocalModuleId, ast: MacroCallKind) -> Self {
357 Self { in_module: container, kind: DiagnosticKind::UnresolvedProcMacro { ast } }
358 }
359
360 pub(super) fn macro_error(
361 container: LocalModuleId,
362 ast: MacroCallKind,
363 message: String,
364 ) -> Self {
365 Self { in_module: container, kind: DiagnosticKind::MacroError { ast, message } }
366 }
367
351 pub(super) fn add_to( 368 pub(super) fn add_to(
352 &self, 369 &self,
353 db: &dyn DefDatabase, 370 db: &dyn DefDatabase,
@@ -407,6 +424,72 @@ mod diagnostics {
407 opts: opts.clone(), 424 opts: opts.clone(),
408 }); 425 });
409 } 426 }
427
428 DiagnosticKind::UnresolvedProcMacro { ast } => {
429 let mut precise_location = None;
430 let (file, ast, name) = match ast {
431 MacroCallKind::FnLike(ast) => {
432 let node = ast.to_node(db.upcast());
433 (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None)
434 }
435 MacroCallKind::Attr(ast, name) => {
436 let node = ast.to_node(db.upcast());
437
438 // Compute the precise location of the macro name's token in the derive
439 // list.
440 // FIXME: This does not handle paths to the macro, but neither does the
441 // rest of r-a.
442 let derive_attrs =
443 node.attrs().filter_map(|attr| match attr.as_simple_call() {
444 Some((name, args)) if name == "derive" => Some(args),
445 _ => None,
446 });
447 'outer: for attr in derive_attrs {
448 let tokens =
449 attr.syntax().children_with_tokens().filter_map(|elem| {
450 match elem {
451 syntax::NodeOrToken::Node(_) => None,
452 syntax::NodeOrToken::Token(tok) => Some(tok),
453 }
454 });
455 for token in tokens {
456 if token.kind() == SyntaxKind::IDENT
457 && token.to_string() == *name
458 {
459 precise_location = Some(token.text_range());
460 break 'outer;
461 }
462 }
463 }
464
465 (
466 ast.file_id,
467 SyntaxNodePtr::from(AstPtr::new(&node)),
468 Some(name.clone()),
469 )
470 }
471 };
472 sink.push(UnresolvedProcMacro {
473 file,
474 node: ast,
475 precise_location,
476 macro_name: name,
477 });
478 }
479
480 DiagnosticKind::MacroError { ast, message } => {
481 let (file, ast) = match ast {
482 MacroCallKind::FnLike(ast) => {
483 let node = ast.to_node(db.upcast());
484 (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
485 }
486 MacroCallKind::Attr(ast, _) => {
487 let node = ast.to_node(db.upcast());
488 (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
489 }
490 };
491 sink.push(MacroError { file, node: ast, message: message.clone() });
492 }
410 } 493 }
411 } 494 }
412 } 495 }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 5ed9073e0..19cd713ba 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -7,7 +7,6 @@ use std::iter;
7 7
8use base_db::{CrateId, FileId, ProcMacroId}; 8use base_db::{CrateId, FileId, ProcMacroId};
9use cfg::{CfgExpr, CfgOptions}; 9use cfg::{CfgExpr, CfgOptions};
10use hir_expand::InFile;
11use hir_expand::{ 10use hir_expand::{
12 ast_id_map::FileAstId, 11 ast_id_map::FileAstId,
13 builtin_derive::find_builtin_derive, 12 builtin_derive::find_builtin_derive,
@@ -16,6 +15,7 @@ use hir_expand::{
16 proc_macro::ProcMacroExpander, 15 proc_macro::ProcMacroExpander,
17 HirFileId, MacroCallId, MacroDefId, MacroDefKind, 16 HirFileId, MacroCallId, MacroDefId, MacroDefKind,
18}; 17};
18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 20use syntax::ast;
21use test_utils::mark; 21use test_utils::mark;
@@ -812,7 +812,30 @@ impl DefCollector<'_> {
812 log::warn!("macro expansion is too deep"); 812 log::warn!("macro expansion is too deep");
813 return; 813 return;
814 } 814 }
815 let file_id: HirFileId = macro_call_id.as_file(); 815 let file_id = macro_call_id.as_file();
816
817 // First, fetch the raw expansion result for purposes of error reporting. This goes through
818 // `macro_expand_error` to avoid depending on the full expansion result (to improve
819 // incrementality).
820 let err = self.db.macro_expand_error(macro_call_id);
821 if let Some(err) = err {
822 if let MacroCallId::LazyMacro(id) = macro_call_id {
823 let loc: MacroCallLoc = self.db.lookup_intern_macro(id);
824
825 let diag = match err {
826 hir_expand::ExpandError::UnresolvedProcMacro => {
827 // Missing proc macros are non-fatal, so they are handled specially.
828 DefDiagnostic::unresolved_proc_macro(module_id, loc.kind)
829 }
830 _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
831 };
832
833 self.def_map.diagnostics.push(diag);
834 }
835 // FIXME: Handle eager macros.
836 }
837
838 // Then, fetch and process the item tree. This will reuse the expansion result from above.
816 let item_tree = self.db.item_tree(file_id); 839 let item_tree = self.db.item_tree(file_id);
817 let mod_dir = self.mod_dirs[&module_id].clone(); 840 let mod_dir = self.mod_dirs[&module_id].clone();
818 ModCollector { 841 ModCollector {
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 46ebdbc74..ff50bfd82 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -3,7 +3,7 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use base_db::{salsa, SourceDatabase}; 5use base_db::{salsa, SourceDatabase};
6use mbe::{ExpandResult, MacroRules}; 6use mbe::{ExpandError, ExpandResult, MacroRules};
7use parser::FragmentKind; 7use parser::FragmentKind;
8use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; 8use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode};
9 9
@@ -81,6 +81,9 @@ pub trait AstDatabase: SourceDatabase {
81 ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>; 81 ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
82 fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; 82 fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
83 83
84 /// Firewall query that returns the error from the `macro_expand` query.
85 fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
86
84 #[salsa::interned] 87 #[salsa::interned]
85 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; 88 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
86 89
@@ -171,6 +174,10 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
171 macro_expand_with_arg(db, id, None) 174 macro_expand_with_arg(db, id, None)
172} 175}
173 176
177fn macro_expand_error(db: &dyn AstDatabase, macro_call: MacroCallId) -> Option<ExpandError> {
178 db.macro_expand(macro_call).err
179}
180
174fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { 181fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
175 let lazy_id = match id { 182 let lazy_id = match id {
176 MacroCallId::LazyMacro(id) => id, 183 MacroCallId::LazyMacro(id) => id,
@@ -189,6 +196,7 @@ fn macro_expand_with_arg(
189 id: MacroCallId, 196 id: MacroCallId,
190 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 197 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
191) -> ExpandResult<Option<Arc<tt::Subtree>>> { 198) -> ExpandResult<Option<Arc<tt::Subtree>>> {
199 let _p = profile::span("macro_expand");
192 let lazy_id = match id { 200 let lazy_id = match id {
193 MacroCallId::LazyMacro(id) => id, 201 MacroCallId::LazyMacro(id) => id,
194 MacroCallId::EagerMacro(id) => { 202 MacroCallId::EagerMacro(id) => {
@@ -276,14 +284,15 @@ fn parse_macro_with_arg(
276 macro_file: MacroFile, 284 macro_file: MacroFile,
277 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>, 285 arg: Option<Arc<(tt::Subtree, mbe::TokenMap)>>,
278) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { 286) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
279 let _p = profile::span("parse_macro_query");
280
281 let macro_call_id = macro_file.macro_call_id; 287 let macro_call_id = macro_file.macro_call_id;
282 let result = if let Some(arg) = arg { 288 let result = if let Some(arg) = arg {
283 macro_expand_with_arg(db, macro_call_id, Some(arg)) 289 macro_expand_with_arg(db, macro_call_id, Some(arg))
284 } else { 290 } else {
285 db.macro_expand(macro_call_id) 291 db.macro_expand(macro_call_id)
286 }; 292 };
293
294 let _p = profile::span("parse_macro_expansion");
295
287 if let Some(err) = &result.err { 296 if let Some(err) = &result.err {
288 // Note: 297 // Note:
289 // The final goal we would like to make all parse_macro success, 298 // The final goal we would like to make all parse_macro success,
diff --git a/crates/hir_expand/src/diagnostics.rs b/crates/hir_expand/src/diagnostics.rs
index 1043c6aeb..bf0b85ce9 100644
--- a/crates/hir_expand/src/diagnostics.rs
+++ b/crates/hir_expand/src/diagnostics.rs
@@ -5,7 +5,7 @@
5//! 5//!
6//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating 6//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating
7//! a `DiagnosticSink`, you supply a callback which can react to a `dyn 7//! a `DiagnosticSink`, you supply a callback which can react to a `dyn
8//! Diagnostic` or to any concrete diagnostic (downcasting is sued internally). 8//! Diagnostic` or to any concrete diagnostic (downcasting is used internally).
9//! 9//!
10//! Because diagnostics store file offsets, it's a bad idea to store them 10//! Because diagnostics store file offsets, it's a bad idea to store them
11//! directly in salsa. For this reason, every hir subsytem defines it's own 11//! directly in salsa. For this reason, every hir subsytem defines it's own
@@ -32,7 +32,12 @@ impl DiagnosticCode {
32pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { 32pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
33 fn code(&self) -> DiagnosticCode; 33 fn code(&self) -> DiagnosticCode;
34 fn message(&self) -> String; 34 fn message(&self) -> String;
35 /// Used in highlighting and related purposes 35 /// Source element that triggered the diagnostics.
36 ///
37 /// Note that this should reflect "semantics", rather than specific span we
38 /// want to highlight. When rendering the diagnostics into an error message,
39 /// the IDE will fetch the `SyntaxNode` and will narrow the span
40 /// appropriately.
36 fn display_source(&self) -> InFile<SyntaxNodePtr>; 41 fn display_source(&self) -> InFile<SyntaxNodePtr>;
37 fn as_any(&self) -> &(dyn Any + Send + 'static); 42 fn as_any(&self) -> &(dyn Any + Send + 'static);
38 fn is_experimental(&self) -> bool { 43 fn is_experimental(&self) -> bool {
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index d5ba691b7..6dad2507b 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -255,7 +255,7 @@ pub enum MacroDefKind {
255pub struct MacroCallLoc { 255pub struct MacroCallLoc {
256 pub(crate) def: MacroDefId, 256 pub(crate) def: MacroDefId,
257 pub(crate) krate: CrateId, 257 pub(crate) krate: CrateId,
258 pub(crate) kind: MacroCallKind, 258 pub kind: MacroCallKind,
259} 259}
260 260
261#[derive(Debug, Clone, PartialEq, Eq, Hash)] 261#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 7505cb061..97edf0fb6 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -50,7 +50,7 @@ impl ProcMacroExpander {
50 50
51 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) 51 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
52 } 52 }
53 None => Err(err!("Unresolved proc macro")), 53 None => Err(mbe::ExpandError::UnresolvedProcMacro),
54 } 54 }
55 } 55 }
56} 56}
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 3df73ed4f..9d3d88289 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -142,6 +142,15 @@ pub(crate) fn diagnostics(
142 .with_code(Some(d.code())), 142 .with_code(Some(d.code())),
143 ); 143 );
144 }) 144 })
145 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
146 // Use more accurate position if available.
147 let display_range =
148 d.precise_location.unwrap_or_else(|| sema.diagnostics_display_range(d).range);
149
150 // FIXME: it would be nice to tell the user whether proc macros are currently disabled
151 res.borrow_mut()
152 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
153 })
145 // Only collect experimental diagnostics when they're enabled. 154 // Only collect experimental diagnostics when they're enabled.
146 .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) 155 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
147 .filter(|diag| !config.disabled.contains(diag.code().as_str())); 156 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 6cfb22e13..65df7979c 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,6 +1,6 @@
1use assists::utils::FamousDefs;
2use either::Either; 1use either::Either;
3use hir::{known, Callable, HirDisplay, Semantics}; 2use hir::{known, Callable, HirDisplay, Semantics};
3use ide_db::helpers::FamousDefs;
4use ide_db::RootDatabase; 4use ide_db::RootDatabase;
5use stdx::to_lower_snake_case; 5use stdx::to_lower_snake_case;
6use syntax::{ 6use syntax::{
@@ -427,8 +427,8 @@ fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir:
427 427
428#[cfg(test)] 428#[cfg(test)]
429mod tests { 429mod tests {
430 use assists::utils::FamousDefs;
431 use expect_test::{expect, Expect}; 430 use expect_test::{expect, Expect};
431 use ide_db::helpers::FamousDefs;
432 use test_utils::extract_annotations; 432 use test_utils::extract_annotations;
433 433
434 use crate::{fixture, inlay_hints::InlayHintsConfig}; 434 use crate::{fixture, inlay_hints::InlayHintsConfig};
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 6288f7ea7..5244bdd61 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -87,9 +87,7 @@ pub use ide_db::{
87 search::{Reference, ReferenceAccess, ReferenceKind}, 87 search::{Reference, ReferenceAccess, ReferenceKind},
88}; 88};
89 89
90pub use assists::{ 90pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist};
91 utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist,
92};
93pub use hir::{Documentation, Semantics}; 91pub use hir::{Documentation, Semantics};
94pub use ide_db::base_db::{ 92pub use ide_db::base_db::{
95 Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, 93 Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index 72a9212f1..0ad6e1000 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -18,7 +18,8 @@ rayon = "1.5.0"
18fst = { version = "0.4", default-features = false } 18fst = { version = "0.4", default-features = false }
19rustc-hash = "1.1.0" 19rustc-hash = "1.1.0"
20once_cell = "1.3.1" 20once_cell = "1.3.1"
21either = "1.5.3" 21either = "1.6.1"
22itertools = "0.9.0"
22 23
23stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
24syntax = { path = "../syntax", version = "0.0.0" } 25syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
new file mode 100644
index 000000000..d988588ff
--- /dev/null
+++ b/crates/ide_db/src/helpers.rs
@@ -0,0 +1,203 @@
1//! A module with ide helpers for high-level ide features.
2use crate::RootDatabase;
3use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait};
4use syntax::ast::{self, make};
5
6pub mod insert_use;
7
8/// Converts the mod path struct into its ast representation.
9pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
10 let _p = profile::span("mod_path_to_ast");
11
12 let mut segments = Vec::new();
13 let mut is_abs = false;
14 match path.kind {
15 hir::PathKind::Plain => {}
16 hir::PathKind::Super(0) => segments.push(make::path_segment_self()),
17 hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())),
18 hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => {
19 segments.push(make::path_segment_crate())
20 }
21 hir::PathKind::Abs => is_abs = true,
22 }
23
24 segments.extend(
25 path.segments
26 .iter()
27 .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))),
28 );
29 make::path_from_segments(segments, is_abs)
30}
31
32/// Helps with finding well-know things inside the standard library. This is
33/// somewhat similar to the known paths infra inside hir, but it different; We
34/// want to make sure that IDE specific paths don't become interesting inside
35/// the compiler itself as well.
36pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Crate>);
37
38#[allow(non_snake_case)]
39impl FamousDefs<'_, '_> {
40 pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
41pub mod convert {
42 pub trait From<T> {
43 fn from(t: T) -> Self;
44 }
45}
46
47pub mod default {
48 pub trait Default {
49 fn default() -> Self;
50 }
51}
52
53pub mod iter {
54 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
55 mod traits {
56 pub(crate) mod iterator {
57 use crate::option::Option;
58 pub trait Iterator {
59 type Item;
60 fn next(&mut self) -> Option<Self::Item>;
61 fn by_ref(&mut self) -> &mut Self {
62 self
63 }
64 fn take(self, n: usize) -> crate::iter::Take<Self> {
65 crate::iter::Take { inner: self }
66 }
67 }
68
69 impl<I: Iterator> Iterator for &mut I {
70 type Item = I::Item;
71 fn next(&mut self) -> Option<I::Item> {
72 (**self).next()
73 }
74 }
75 }
76 pub(crate) mod collect {
77 pub trait IntoIterator {
78 type Item;
79 }
80 }
81 }
82
83 pub use self::sources::*;
84 pub(crate) mod sources {
85 use super::Iterator;
86 use crate::option::Option::{self, *};
87 pub struct Repeat<A> {
88 element: A,
89 }
90
91 pub fn repeat<T>(elt: T) -> Repeat<T> {
92 Repeat { element: elt }
93 }
94
95 impl<A> Iterator for Repeat<A> {
96 type Item = A;
97
98 fn next(&mut self) -> Option<A> {
99 None
100 }
101 }
102 }
103
104 pub use self::adapters::*;
105 pub(crate) mod adapters {
106 use super::Iterator;
107 use crate::option::Option::{self, *};
108 pub struct Take<I> { pub(crate) inner: I }
109 impl<I> Iterator for Take<I> where I: Iterator {
110 type Item = <I as Iterator>::Item;
111 fn next(&mut self) -> Option<<I as Iterator>::Item> {
112 None
113 }
114 }
115 }
116}
117
118pub mod option {
119 pub enum Option<T> { None, Some(T)}
120}
121
122pub mod prelude {
123 pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default};
124}
125#[prelude_import]
126pub use prelude::*;
127"#;
128
129 pub fn core(&self) -> Option<Crate> {
130 self.find_crate("core")
131 }
132
133 pub fn core_convert_From(&self) -> Option<Trait> {
134 self.find_trait("core:convert:From")
135 }
136
137 pub fn core_option_Option(&self) -> Option<Enum> {
138 self.find_enum("core:option:Option")
139 }
140
141 pub fn core_default_Default(&self) -> Option<Trait> {
142 self.find_trait("core:default:Default")
143 }
144
145 pub fn core_iter_Iterator(&self) -> Option<Trait> {
146 self.find_trait("core:iter:traits:iterator:Iterator")
147 }
148
149 pub fn core_iter(&self) -> Option<Module> {
150 self.find_module("core:iter")
151 }
152
153 fn find_trait(&self, path: &str) -> Option<Trait> {
154 match self.find_def(path)? {
155 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
156 _ => None,
157 }
158 }
159
160 fn find_enum(&self, path: &str) -> Option<Enum> {
161 match self.find_def(path)? {
162 hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it),
163 _ => None,
164 }
165 }
166
167 fn find_module(&self, path: &str) -> Option<Module> {
168 match self.find_def(path)? {
169 hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it),
170 _ => None,
171 }
172 }
173
174 fn find_crate(&self, name: &str) -> Option<Crate> {
175 let krate = self.1?;
176 let db = self.0.db;
177 let res =
178 krate.dependencies(db).into_iter().find(|dep| dep.name.to_string() == name)?.krate;
179 Some(res)
180 }
181
182 fn find_def(&self, path: &str) -> Option<ScopeDef> {
183 let db = self.0.db;
184 let mut path = path.split(':');
185 let trait_ = path.next_back()?;
186 let std_crate = path.next()?;
187 let std_crate = self.find_crate(std_crate)?;
188 let mut module = std_crate.root_module(db);
189 for segment in path {
190 module = module.children(db).find_map(|child| {
191 let name = child.name(db)?;
192 if name.to_string() == segment {
193 Some(child)
194 } else {
195 None
196 }
197 })?;
198 }
199 let def =
200 module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1;
201 Some(def)
202 }
203}
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 423782a0e..67e800fad 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -1,8 +1,8 @@
1//! Handle syntactic aspects of inserting a new `use`. 1//! Handle syntactic aspects of inserting a new `use`.
2use std::{cmp::Ordering, iter::successors}; 2use std::{cmp::Ordering, iter::successors};
3 3
4use crate::RootDatabase;
4use hir::Semantics; 5use hir::Semantics;
5use ide_db::RootDatabase;
6use itertools::{EitherOrBoth, Itertools}; 6use itertools::{EitherOrBoth, Itertools};
7use syntax::{ 7use syntax::{
8 algo::SyntaxRewriter, 8 algo::SyntaxRewriter,
@@ -22,7 +22,7 @@ pub enum ImportScope {
22} 22}
23 23
24impl ImportScope { 24impl ImportScope {
25 pub(crate) fn from(syntax: SyntaxNode) -> Option<Self> { 25 pub fn from(syntax: SyntaxNode) -> Option<Self> {
26 if let Some(module) = ast::Module::cast(syntax.clone()) { 26 if let Some(module) = ast::Module::cast(syntax.clone()) {
27 module.item_list().map(ImportScope::Module) 27 module.item_list().map(ImportScope::Module)
28 } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) { 28 } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) {
@@ -95,6 +95,7 @@ pub fn insert_use<'a>(
95 path: ast::Path, 95 path: ast::Path,
96 merge: Option<MergeBehaviour>, 96 merge: Option<MergeBehaviour>,
97) -> SyntaxRewriter<'a> { 97) -> SyntaxRewriter<'a> {
98 let _p = profile::span("insert_use");
98 let mut rewriter = SyntaxRewriter::default(); 99 let mut rewriter = SyntaxRewriter::default();
99 let use_item = make::use_(make::use_tree(path.clone(), None, None, false)); 100 let use_item = make::use_(make::use_tree(path.clone(), None, None, false));
100 // merge into existing imports if possible 101 // merge into existing imports if possible
@@ -179,7 +180,7 @@ fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -
179 } 180 }
180} 181}
181 182
182pub(crate) fn try_merge_imports( 183pub fn try_merge_imports(
183 lhs: &ast::Use, 184 lhs: &ast::Use,
184 rhs: &ast::Use, 185 rhs: &ast::Use,
185 merge_behaviour: MergeBehaviour, 186 merge_behaviour: MergeBehaviour,
@@ -194,7 +195,7 @@ pub(crate) fn try_merge_imports(
194 Some(lhs.with_use_tree(merged)) 195 Some(lhs.with_use_tree(merged))
195} 196}
196 197
197pub(crate) fn try_merge_trees( 198pub fn try_merge_trees(
198 lhs: &ast::UseTree, 199 lhs: &ast::UseTree,
199 rhs: &ast::UseTree, 200 rhs: &ast::UseTree,
200 merge: MergeBehaviour, 201 merge: MergeBehaviour,
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 05139a651..fceaa089a 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -13,6 +13,7 @@ pub mod source_change;
13pub mod ty_filter; 13pub mod ty_filter;
14pub mod traits; 14pub mod traits;
15pub mod call_info; 15pub mod call_info;
16pub mod helpers;
16 17
17use std::{fmt, sync::Arc}; 18use std::{fmt, sync::Arc};
18 19
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 2d0763c47..3ad609a00 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -35,6 +35,7 @@ pub enum ExpandError {
35 ConversionError, 35 ConversionError,
36 InvalidRepeat, 36 InvalidRepeat,
37 ProcMacroError(tt::ExpansionError), 37 ProcMacroError(tt::ExpansionError),
38 UnresolvedProcMacro,
38 Other(String), 39 Other(String),
39} 40}
40 41
@@ -53,6 +54,7 @@ impl fmt::Display for ExpandError {
53 ExpandError::ConversionError => f.write_str("could not convert tokens"), 54 ExpandError::ConversionError => f.write_str("could not convert tokens"),
54 ExpandError::InvalidRepeat => f.write_str("invalid repeat expression"), 55 ExpandError::InvalidRepeat => f.write_str("invalid repeat expression"),
55 ExpandError::ProcMacroError(e) => e.fmt(f), 56 ExpandError::ProcMacroError(e) => e.fmt(f),
57 ExpandError::UnresolvedProcMacro => f.write_str("unresolved proc macro"),
56 ExpandError::Other(e) => f.write_str(e), 58 ExpandError::Other(e) => f.write_str(e),
57 } 59 }
58 } 60 }
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 436f5041b..08559b53a 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -39,6 +39,7 @@ tracing-tree = { version = "0.1.4" }
39stdx = { path = "../stdx", version = "0.0.0" } 39stdx = { path = "../stdx", version = "0.0.0" }
40flycheck = { path = "../flycheck", version = "0.0.0" } 40flycheck = { path = "../flycheck", version = "0.0.0" }
41ide = { path = "../ide", version = "0.0.0" } 41ide = { path = "../ide", version = "0.0.0" }
42ide_db = { path = "../ide_db", version = "0.0.0" }
42profile = { path = "../profile", version = "0.0.0" } 43profile = { path = "../profile", version = "0.0.0" }
43project_model = { path = "../project_model", version = "0.0.0" } 44project_model = { path = "../project_model", version = "0.0.0" }
44syntax = { path = "../syntax", version = "0.0.0" } 45syntax = { path = "../syntax", version = "0.0.0" }
@@ -49,7 +50,6 @@ cfg = { path = "../cfg", version = "0.0.0" }
49toolchain = { path = "../toolchain", version = "0.0.0" } 50toolchain = { path = "../toolchain", version = "0.0.0" }
50 51
51# This should only be used in CLI 52# This should only be used in CLI
52ide_db = { path = "../ide_db", version = "0.0.0" }
53ssr = { path = "../ssr", version = "0.0.0" } 53ssr = { path = "../ssr", version = "0.0.0" }
54hir = { path = "../hir", version = "0.0.0" } 54hir = { path = "../hir", version = "0.0.0" }
55hir_def = { path = "../hir_def", version = "0.0.0" } 55hir_def = { path = "../hir_def", version = "0.0.0" }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index a334cdb11..30299a465 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -11,10 +11,8 @@ use std::{convert::TryFrom, ffi::OsString, path::PathBuf};
11 11
12use flycheck::FlycheckConfig; 12use flycheck::FlycheckConfig;
13use hir::PrefixKind; 13use hir::PrefixKind;
14use ide::{ 14use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig};
15 AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, 15use ide_db::helpers::insert_use::MergeBehaviour;
16 MergeBehaviour,
17};
18use lsp_types::{ClientCapabilities, MarkupKind}; 16use lsp_types::{ClientCapabilities, MarkupKind};
19use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; 17use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
20use rustc_hash::FxHashSet; 18use rustc_hash::FxHashSet;
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 866d1d176..f349b0810 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -289,55 +289,69 @@ impl GlobalState {
289 } 289 }
290 } 290 }
291 } 291 }
292 Event::Flycheck(task) => match task { 292 Event::Flycheck(mut task) => {
293 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { 293 let _p = profile::span("GlobalState::handle_event/flycheck");
294 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 294 loop {
295 &self.config.diagnostics_map, 295 match task {
296 &diagnostic, 296 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
297 &workspace_root, 297 let diagnostics =
298 ); 298 crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
299 for diag in diagnostics { 299 &self.config.diagnostics_map,
300 match url_to_file_id(&self.vfs.read().0, &diag.url) { 300 &diagnostic,
301 Ok(file_id) => self.diagnostics.add_check_diagnostic( 301 &workspace_root,
302 file_id, 302 );
303 diag.diagnostic, 303 for diag in diagnostics {
304 diag.fixes, 304 match url_to_file_id(&self.vfs.read().0, &diag.url) {
305 ), 305 Ok(file_id) => self.diagnostics.add_check_diagnostic(
306 Err(err) => { 306 file_id,
307 log::error!("File with cargo diagnostic not found in VFS: {}", err); 307 diag.diagnostic,
308 } 308 diag.fixes,
309 }; 309 ),
310 } 310 Err(err) => {
311 } 311 log::error!(
312 312 "File with cargo diagnostic not found in VFS: {}",
313 flycheck::Message::Progress { id, progress } => { 313 err
314 let (state, message) = match progress { 314 );
315 flycheck::Progress::DidStart => { 315 }
316 self.diagnostics.clear_check(); 316 };
317 (Progress::Begin, None)
318 }
319 flycheck::Progress::DidCheckCrate(target) => {
320 (Progress::Report, Some(target))
321 }
322 flycheck::Progress::DidCancel => (Progress::End, None),
323 flycheck::Progress::DidFinish(result) => {
324 if let Err(err) = result {
325 log::error!("cargo check failed: {}", err)
326 } 317 }
327 (Progress::End, None)
328 } 318 }
329 };
330 319
331 // When we're running multiple flychecks, we have to include a disambiguator in 320 flycheck::Message::Progress { id, progress } => {
332 // the title, or the editor complains. Note that this is a user-facing string. 321 let (state, message) = match progress {
333 let title = if self.flycheck.len() == 1 { 322 flycheck::Progress::DidStart => {
334 "cargo check".to_string() 323 self.diagnostics.clear_check();
335 } else { 324 (Progress::Begin, None)
336 format!("cargo check (#{})", id + 1) 325 }
337 }; 326 flycheck::Progress::DidCheckCrate(target) => {
338 self.report_progress(&title, state, message, None); 327 (Progress::Report, Some(target))
328 }
329 flycheck::Progress::DidCancel => (Progress::End, None),
330 flycheck::Progress::DidFinish(result) => {
331 if let Err(err) = result {
332 log::error!("cargo check failed: {}", err)
333 }
334 (Progress::End, None)
335 }
336 };
337
338 // When we're running multiple flychecks, we have to include a disambiguator in
339 // the title, or the editor complains. Note that this is a user-facing string.
340 let title = if self.flycheck.len() == 1 {
341 "cargo check".to_string()
342 } else {
343 format!("cargo check (#{})", id + 1)
344 };
345 self.report_progress(&title, state, message, None);
346 }
347 }
348 // Coalesce many flycheck updates into a single loop turn
349 task = match self.flycheck_receiver.try_recv() {
350 Ok(task) => task,
351 Err(_) => break,
352 }
339 } 353 }
340 }, 354 }
341 } 355 }
342 356
343 let state_changed = self.process_changes(); 357 let state_changed = self.process_changes();
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 001bf5949..b2d35f535 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -205,7 +205,7 @@ impl GlobalState {
205 } 205 }
206 let res = vfs.file_id(&vfs_path); 206 let res = vfs.file_id(&vfs_path);
207 if res.is_none() { 207 if res.is_none() {
208 log::error!("failed to load {}", path.display()) 208 log::warn!("failed to load {}", path.display())
209 } 209 }
210 res 210 res
211 }; 211 };
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 1fe907753..ce62babc3 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -28,6 +28,7 @@ stdx = { path = "../stdx", version = "0.0.0" }
28text_edit = { path = "../text_edit", version = "0.0.0" } 28text_edit = { path = "../text_edit", version = "0.0.0" }
29parser = { path = "../parser", version = "0.0.0" } 29parser = { path = "../parser", version = "0.0.0" }
30test_utils = { path = "../test_utils", version = "0.0.0" } 30test_utils = { path = "../test_utils", version = "0.0.0" }
31profile = { path = "../profile", version = "0.0.0" }
31 32
32[dev-dependencies] 33[dev-dependencies]
33walkdir = "2.3.1" 34walkdir = "2.3.1"
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 320c430c9..ee89d9867 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -127,6 +127,8 @@ pub struct TreeDiff {
127 127
128impl TreeDiff { 128impl TreeDiff {
129 pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { 129 pub fn into_text_edit(&self, builder: &mut TextEditBuilder) {
130 let _p = profile::span("into_text_edit");
131
130 for (anchor, to) in self.insertions.iter() { 132 for (anchor, to) in self.insertions.iter() {
131 let offset = match anchor { 133 let offset = match anchor {
132 TreeDiffInsertPos::After(it) => it.text_range().end(), 134 TreeDiffInsertPos::After(it) => it.text_range().end(),
@@ -154,6 +156,8 @@ impl TreeDiff {
154/// 156///
155/// This function tries to find a fine-grained diff. 157/// This function tries to find a fine-grained diff.
156pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { 158pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
159 let _p = profile::span("diff");
160
157 let mut diff = TreeDiff { 161 let mut diff = TreeDiff {
158 replacements: FxHashMap::default(), 162 replacements: FxHashMap::default(),
159 insertions: FxIndexMap::default(), 163 insertions: FxIndexMap::default(),
@@ -467,6 +471,8 @@ impl<'a> SyntaxRewriter<'a> {
467 } 471 }
468 472
469 pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { 473 pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode {
474 let _p = profile::span("rewrite");
475
470 if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() { 476 if self.f.is_none() && self.replacements.is_empty() && self.insertions.is_empty() {
471 return node.clone(); 477 return node.clone();
472 } 478 }
@@ -483,6 +489,7 @@ impl<'a> SyntaxRewriter<'a> {
483 /// 489 ///
484 /// Returns `None` when there are no replacements. 490 /// Returns `None` when there are no replacements.
485 pub fn rewrite_root(&self) -> Option<SyntaxNode> { 491 pub fn rewrite_root(&self) -> Option<SyntaxNode> {
492 let _p = profile::span("rewrite_root");
486 fn element_to_node_or_parent(element: &SyntaxElement) -> SyntaxNode { 493 fn element_to_node_or_parent(element: &SyntaxElement) -> SyntaxNode {
487 match element { 494 match element {
488 SyntaxElement::Node(it) => it.clone(), 495 SyntaxElement::Node(it) => it.clone(),
@@ -517,6 +524,8 @@ impl<'a> SyntaxRewriter<'a> {
517 } 524 }
518 525
519 fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { 526 fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode {
527 let _p = profile::span("rewrite_children");
528
520 // FIXME: this could be made much faster. 529 // FIXME: this could be made much faster.
521 let mut new_children = Vec::new(); 530 let mut new_children = Vec::new();
522 if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) { 531 if let Some(elements) = self.insertions(&InsertPos::FirstChildOf(node.clone())) {
@@ -533,6 +542,8 @@ impl<'a> SyntaxRewriter<'a> {
533 acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, 542 acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
534 element: &SyntaxElement, 543 element: &SyntaxElement,
535 ) { 544 ) {
545 let _p = profile::span("rewrite_self");
546
536 if let Some(replacement) = self.replacement(&element) { 547 if let Some(replacement) = self.replacement(&element) {
537 match replacement { 548 match replacement {
538 Replacement::Single(element) => acc.push(element_to_green(element)), 549 Replacement::Single(element) => acc.push(element_to_green(element)),
@@ -588,6 +599,8 @@ fn with_children(
588 parent: &SyntaxNode, 599 parent: &SyntaxNode,
589 new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, 600 new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
590) -> SyntaxNode { 601) -> SyntaxNode {
602 let _p = profile::span("with_children");
603
591 let len = new_children.iter().map(|it| it.text_len()).sum::<TextSize>(); 604 let len = new_children.iter().map(|it| it.text_len()).sum::<TextSize>();
592 let new_node = rowan::GreenNode::new(rowan::SyntaxKind(parent.kind() as u16), new_children); 605 let new_node = rowan::GreenNode::new(rowan::SyntaxKind(parent.kind() as u16), new_children);
593 let new_root_node = parent.replace_with(new_node); 606 let new_root_node = parent.replace_with(new_node);