diff options
45 files changed, 609 insertions, 361 deletions
diff --git a/Cargo.lock b/Cargo.lock index 051d9e734..f50e9edd6 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -253,7 +253,6 @@ dependencies = [ | |||
253 | name = "completion" | 253 | name = "completion" |
254 | version = "0.0.0" | 254 | version = "0.0.0" |
255 | dependencies = [ | 255 | dependencies = [ |
256 | "assists", | ||
257 | "base_db", | 256 | "base_db", |
258 | "either", | 257 | "either", |
259 | "expect-test", | 258 | "expect-test", |
@@ -682,6 +681,7 @@ dependencies = [ | |||
682 | "expect-test", | 681 | "expect-test", |
683 | "fst", | 682 | "fst", |
684 | "hir", | 683 | "hir", |
684 | "itertools", | ||
685 | "log", | 685 | "log", |
686 | "once_cell", | 686 | "once_cell", |
687 | "profile", | 687 | "profile", |
@@ -1658,6 +1658,7 @@ dependencies = [ | |||
1658 | "itertools", | 1658 | "itertools", |
1659 | "once_cell", | 1659 | "once_cell", |
1660 | "parser", | 1660 | "parser", |
1661 | "profile", | ||
1661 | "rayon", | 1662 | "rayon", |
1662 | "rowan", | 1663 | "rowan", |
1663 | "rustc-ap-rustc_lexer", | 1664 | "rustc-ap-rustc_lexer", |
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] |
13 | rustc-hash = "1.1.0" | 13 | rustc-hash = "1.1.0" |
14 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
15 | either = "1.5.3" | 15 | either = "1.6.1" |
16 | 16 | ||
17 | stdx = { path = "../stdx", version = "0.0.0" } | 17 | stdx = { path = "../stdx", version = "0.0.0" } |
18 | syntax = { path = "../syntax", version = "0.0.0" } | 18 | syntax = { 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 | ||
7 | use hir::PrefixKind; | 7 | use hir::PrefixKind; |
8 | use ide_db::helpers::insert_use::MergeBehaviour; | ||
8 | 9 | ||
9 | use crate::{utils::MergeBehaviour, AssistKind}; | 10 | use crate::AssistKind; |
10 | 11 | ||
11 | #[derive(Clone, Debug, PartialEq, Eq)] | 12 | #[derive(Clone, Debug, PartialEq, Eq)] |
12 | pub struct AssistConfig { | 13 | pub 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. |
2 | use hir::{HirDisplay, PathResolution, SemanticsScope}; | 2 | use hir::{HirDisplay, PathResolution, SemanticsScope}; |
3 | use ide_db::helpers::mod_path_to_ast; | ||
3 | use rustc_hash::FxHashMap; | 4 | use rustc_hash::FxHashMap; |
4 | use syntax::{ | 5 | use syntax::{ |
5 | algo::SyntaxRewriter, | 6 | algo::SyntaxRewriter, |
@@ -7,8 +8,6 @@ use syntax::{ | |||
7 | SyntaxNode, | 8 | SyntaxNode, |
8 | }; | 9 | }; |
9 | 10 | ||
10 | use crate::utils::mod_path_to_ast; | ||
11 | |||
12 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { | 11 | pub 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 @@ | |||
1 | use ide_db::helpers::{ | ||
2 | insert_use::{insert_use, ImportScope}, | ||
3 | mod_path_to_ast, | ||
4 | }; | ||
1 | use syntax::ast; | 5 | use syntax::ast; |
2 | 6 | ||
3 | use crate::{ | 7 | use 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 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; | 4 | use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; |
5 | use ide_db::helpers::{ | ||
6 | insert_use::{insert_use, ImportScope}, | ||
7 | mod_path_to_ast, | ||
8 | }; | ||
5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; | 9 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; |
6 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
7 | use syntax::{ | 11 | use syntax::{ |
@@ -10,10 +14,7 @@ use syntax::{ | |||
10 | SourceFile, SyntaxElement, SyntaxNode, T, | 14 | SourceFile, SyntaxElement, SyntaxNode, T, |
11 | }; | 15 | }; |
12 | 16 | ||
13 | use crate::{ | 17 | use 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)] |
238 | mod tests { | 239 | mod 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 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use hir::{Adt, HasSource, ModuleDef, Semantics}; | 3 | use hir::{Adt, HasSource, ModuleDef, Semantics}; |
4 | use ide_db::helpers::{mod_path_to_ast, FamousDefs}; | ||
4 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
5 | use itertools::Itertools; | 6 | use itertools::Itertools; |
6 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | 7 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; |
7 | use test_utils::mark; | 8 | use test_utils::mark; |
8 | 9 | ||
9 | use crate::{ | 10 | use 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)] |
214 | mod tests { | 215 | mod 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 @@ | |||
1 | use ide_db::helpers::FamousDefs; | ||
1 | use ide_db::RootDatabase; | 2 | use ide_db::RootDatabase; |
2 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
3 | use test_utils::mark; | 4 | use test_utils::mark; |
4 | 5 | ||
5 | use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists}; | 6 | use 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 @@ | |||
1 | use ide_db::helpers::insert_use::{try_merge_imports, try_merge_trees, MergeBehaviour}; | ||
1 | use syntax::{ | 2 | use syntax::{ |
2 | algo::{neighbor, SyntaxRewriter}, | 3 | algo::{neighbor, SyntaxRewriter}, |
3 | ast, AstNode, | 4 | ast, AstNode, |
@@ -5,10 +6,7 @@ use syntax::{ | |||
5 | 6 | ||
6 | use crate::{ | 7 | use 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 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use hir::AsName; | 3 | use hir::AsName; |
4 | use ide_db::helpers::mod_path_to_ast; | ||
4 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
5 | use syntax::{ | 6 | use syntax::{ |
6 | ast, | 7 | ast, |
@@ -12,7 +13,6 @@ use test_utils::mark; | |||
12 | use crate::{ | 13 | use 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 @@ | |||
1 | use ide_db::helpers::mod_path_to_ast; | ||
1 | use ide_db::imports_locator; | 2 | use ide_db::imports_locator; |
2 | use itertools::Itertools; | 3 | use itertools::Itertools; |
3 | use syntax::{ | 4 | use syntax::{ |
@@ -10,8 +11,7 @@ use syntax::{ | |||
10 | use crate::{ | 11 | use 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 @@ | |||
1 | use ide_db::helpers::insert_use::{insert_use, ImportScope}; | ||
1 | use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; | 2 | use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; |
2 | use test_utils::mark; | 3 | use test_utils::mark; |
3 | 4 | ||
4 | use crate::{ | 5 | use 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. |
2 | pub(crate) mod insert_use; | ||
3 | pub(crate) mod import_assets; | 2 | pub(crate) mod import_assets; |
4 | 3 | ||
5 | use std::ops; | 4 | use std::ops; |
6 | 5 | ||
7 | use hir::{Crate, Enum, HasSource, Module, ScopeDef, Semantics, Trait}; | 6 | use hir::HasSource; |
8 | use ide_db::RootDatabase; | 7 | use ide_db::RootDatabase; |
9 | use itertools::Itertools; | 8 | use itertools::Itertools; |
10 | use syntax::{ | 9 | use 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 | ||
25 | pub use insert_use::{insert_use, ImportScope, MergeBehaviour}; | ||
26 | |||
27 | pub 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 | |||
48 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { | 24 | pub(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. | ||
266 | pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Crate>); | ||
267 | |||
268 | #[allow(non_snake_case)] | ||
269 | impl FamousDefs<'_, '_> { | ||
270 | pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core | ||
271 | pub mod convert { | ||
272 | pub trait From<T> { | ||
273 | fn from(t: T) -> Self; | ||
274 | } | ||
275 | } | ||
276 | |||
277 | pub mod default { | ||
278 | pub trait Default { | ||
279 | fn default() -> Self; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | pub 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 | |||
348 | pub mod option { | ||
349 | pub enum Option<T> { None, Some(T)} | ||
350 | } | ||
351 | |||
352 | pub mod prelude { | ||
353 | pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default}; | ||
354 | } | ||
355 | #[prelude_import] | ||
356 | pub 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 | |||
435 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { | 238 | pub(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" | |||
15 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
16 | either = "1.6.1" | 16 | either = "1.6.1" |
17 | 17 | ||
18 | assists = { path = "../assists", version = "0.0.0" } | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | 18 | stdx = { path = "../stdx", version = "0.0.0" } |
20 | syntax = { path = "../syntax", version = "0.0.0" } | 19 | syntax = { path = "../syntax", version = "0.0.0" } |
21 | text_edit = { path = "../text_edit", version = "0.0.0" } | 20 | text_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. |
2 | use assists::utils::FamousDefs; | 2 | use ide_db::helpers::FamousDefs; |
3 | use syntax::ast::Expr; | 3 | use syntax::ast::Expr; |
4 | 4 | ||
5 | use crate::{ | 5 | use crate::{ |
@@ -45,8 +45,8 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> | |||
45 | 45 | ||
46 | #[cfg(test)] | 46 | #[cfg(test)] |
47 | mod tests { | 47 | mod 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 | ||
3 | use assists::utils::ImportScope; | ||
4 | use either::Either; | 3 | use either::Either; |
5 | use hir::{Adt, ModuleDef, ScopeDef, Type}; | 4 | use hir::{Adt, ModuleDef, ScopeDef, Type}; |
5 | use ide_db::helpers::insert_use::ImportScope; | ||
6 | use ide_db::imports_locator; | 6 | use ide_db::imports_locator; |
7 | use syntax::AstNode; | 7 | use syntax::AstNode; |
8 | use test_utils::mark; | 8 | use 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 | ||
7 | use assists::utils::MergeBehaviour; | 7 | use ide_db::helpers::insert_use::MergeBehaviour; |
8 | 8 | ||
9 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
10 | pub struct CompletionConfig { | 10 | pub 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 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour}; | ||
6 | use hir::{Documentation, ModPath, Mutability}; | 5 | use hir::{Documentation, ModPath, Mutability}; |
6 | use ide_db::helpers::{ | ||
7 | insert_use::{self, ImportScope, MergeBehaviour}, | ||
8 | mod_path_to_ast, | ||
9 | }; | ||
7 | use syntax::{algo, TextRange}; | 10 | use syntax::{algo, TextRange}; |
8 | use text_edit::TextEdit; | 11 | use 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)] | ||
263 | pub(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)] |
261 | pub(crate) struct Builder { | 272 | pub(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 | ||
279 | impl Builder { | 290 | impl 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 | ||
10 | mod builder_ext; | 10 | mod builder_ext; |
11 | 11 | ||
12 | use assists::utils::{ImportScope, MergeBehaviour}; | ||
13 | use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; | 12 | use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; |
13 | use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour}; | ||
14 | use ide_db::RootDatabase; | 14 | use ide_db::RootDatabase; |
15 | use syntax::TextRange; | 15 | use syntax::TextRange; |
16 | use test_utils::mark; | 16 | use test_utils::mark; |
17 | 17 | ||
18 | use crate::{ | 18 | use 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 | ||
23 | use crate::render::{enum_variant::render_enum_variant, function::render_fn, macro_::render_macro}; | 23 | use 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 | ||
49 | pub(crate) fn render_resolution_with_import<'a>( | 49 | pub(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 | ||
3 | use assists::utils::{ImportScope, MergeBehaviour}; | ||
4 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; | 3 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; |
5 | use itertools::Itertools; | 4 | use itertools::Itertools; |
6 | use test_utils::mark; | 5 | use test_utils::mark; |
7 | 6 | ||
8 | use crate::{ | 7 | use 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 | ||
13 | pub(crate) fn render_enum_variant<'a>( | 12 | pub(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 | ||
3 | use assists::utils::{ImportScope, MergeBehaviour}; | 3 | use hir::{HasSource, Type}; |
4 | use hir::{HasSource, ModPath, Type}; | ||
5 | use syntax::{ast::Fn, display::function_declaration}; | 4 | use syntax::{ast::Fn, display::function_declaration}; |
6 | 5 | ||
7 | use crate::{ | 6 | use 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 | ||
12 | pub(crate) fn render_fn<'a>( | 11 | pub(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 | ||
3 | use assists::utils::{ImportScope, MergeBehaviour}; | 3 | use hir::{Documentation, HasSource}; |
4 | use hir::{Documentation, HasSource, ModPath}; | ||
5 | use syntax::display::macro_label; | 4 | use syntax::display::macro_label; |
6 | use test_utils::mark; | 5 | use test_utils::mark; |
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | 8 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, |
10 | render::RenderContext, | 9 | render::RenderContext, |
11 | }; | 10 | }; |
12 | 11 | ||
13 | pub(crate) fn render_macro<'a>( | 12 | pub(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 |
2 | pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule}; | 2 | pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule, UnresolvedProcMacro}; |
3 | pub use hir_expand::diagnostics::{ | 3 | pub 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; | |||
6 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; | 6 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; |
7 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; | 7 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; |
8 | use hir_expand::{HirFileId, InFile}; | 8 | use hir_expand::{HirFileId, InFile}; |
9 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | 9 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; |
10 | 10 | ||
11 | use crate::{db::DefDatabase, DefWithBodyId}; | 11 | use 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)] | ||
137 | pub 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 | |||
146 | impl 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)] | ||
171 | pub struct MacroError { | ||
172 | pub file: HirFileId, | ||
173 | pub node: SyntaxNodePtr, | ||
174 | pub message: String, | ||
175 | } | ||
176 | |||
177 | impl 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 | ||
8 | use base_db::{CrateId, FileId, ProcMacroId}; | 8 | use base_db::{CrateId, FileId, ProcMacroId}; |
9 | use cfg::{CfgExpr, CfgOptions}; | 9 | use cfg::{CfgExpr, CfgOptions}; |
10 | use hir_expand::InFile; | ||
11 | use hir_expand::{ | 10 | use 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 | }; |
18 | use hir_expand::{InFile, MacroCallLoc}; | ||
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
20 | use syntax::ast; | 20 | use syntax::ast; |
21 | use test_utils::mark; | 21 | use 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 @@ | |||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use base_db::{salsa, SourceDatabase}; | 5 | use base_db::{salsa, SourceDatabase}; |
6 | use mbe::{ExpandResult, MacroRules}; | 6 | use mbe::{ExpandError, ExpandResult, MacroRules}; |
7 | use parser::FragmentKind; | 7 | use parser::FragmentKind; |
8 | use syntax::{algo::diff, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; | 8 | use 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 | ||
177 | fn macro_expand_error(db: &dyn AstDatabase, macro_call: MacroCallId) -> Option<ExpandError> { | ||
178 | db.macro_expand(macro_call).err | ||
179 | } | ||
180 | |||
174 | fn expander(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> { | 181 | fn 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 { | |||
32 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | 32 | pub 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 { | |||
255 | pub struct MacroCallLoc { | 255 | pub 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 @@ | |||
1 | use assists::utils::FamousDefs; | ||
2 | use either::Either; | 1 | use either::Either; |
3 | use hir::{known, Callable, HirDisplay, Semantics}; | 2 | use hir::{known, Callable, HirDisplay, Semantics}; |
3 | use ide_db::helpers::FamousDefs; | ||
4 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
5 | use stdx::to_lower_snake_case; | 5 | use stdx::to_lower_snake_case; |
6 | use syntax::{ | 6 | use syntax::{ |
@@ -427,8 +427,8 @@ fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir: | |||
427 | 427 | ||
428 | #[cfg(test)] | 428 | #[cfg(test)] |
429 | mod tests { | 429 | mod 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 | ||
90 | pub use assists::{ | 90 | pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist}; |
91 | utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist, | ||
92 | }; | ||
93 | pub use hir::{Documentation, Semantics}; | 91 | pub use hir::{Documentation, Semantics}; |
94 | pub use ide_db::base_db::{ | 92 | pub 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" | |||
18 | fst = { version = "0.4", default-features = false } | 18 | fst = { version = "0.4", default-features = false } |
19 | rustc-hash = "1.1.0" | 19 | rustc-hash = "1.1.0" |
20 | once_cell = "1.3.1" | 20 | once_cell = "1.3.1" |
21 | either = "1.5.3" | 21 | either = "1.6.1" |
22 | itertools = "0.9.0" | ||
22 | 23 | ||
23 | stdx = { path = "../stdx", version = "0.0.0" } | 24 | stdx = { path = "../stdx", version = "0.0.0" } |
24 | syntax = { path = "../syntax", version = "0.0.0" } | 25 | syntax = { 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. | ||
2 | use crate::RootDatabase; | ||
3 | use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; | ||
4 | use syntax::ast::{self, make}; | ||
5 | |||
6 | pub mod insert_use; | ||
7 | |||
8 | /// Converts the mod path struct into its ast representation. | ||
9 | pub 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. | ||
36 | pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Crate>); | ||
37 | |||
38 | #[allow(non_snake_case)] | ||
39 | impl FamousDefs<'_, '_> { | ||
40 | pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core | ||
41 | pub mod convert { | ||
42 | pub trait From<T> { | ||
43 | fn from(t: T) -> Self; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | pub mod default { | ||
48 | pub trait Default { | ||
49 | fn default() -> Self; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | pub 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 | |||
118 | pub mod option { | ||
119 | pub enum Option<T> { None, Some(T)} | ||
120 | } | ||
121 | |||
122 | pub mod prelude { | ||
123 | pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default}; | ||
124 | } | ||
125 | #[prelude_import] | ||
126 | pub 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`. |
2 | use std::{cmp::Ordering, iter::successors}; | 2 | use std::{cmp::Ordering, iter::successors}; |
3 | 3 | ||
4 | use crate::RootDatabase; | ||
4 | use hir::Semantics; | 5 | use hir::Semantics; |
5 | use ide_db::RootDatabase; | ||
6 | use itertools::{EitherOrBoth, Itertools}; | 6 | use itertools::{EitherOrBoth, Itertools}; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | algo::SyntaxRewriter, | 8 | algo::SyntaxRewriter, |
@@ -22,7 +22,7 @@ pub enum ImportScope { | |||
22 | } | 22 | } |
23 | 23 | ||
24 | impl ImportScope { | 24 | impl 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 | ||
182 | pub(crate) fn try_merge_imports( | 183 | pub 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 | ||
197 | pub(crate) fn try_merge_trees( | 198 | pub 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; | |||
13 | pub mod ty_filter; | 13 | pub mod ty_filter; |
14 | pub mod traits; | 14 | pub mod traits; |
15 | pub mod call_info; | 15 | pub mod call_info; |
16 | pub mod helpers; | ||
16 | 17 | ||
17 | use std::{fmt, sync::Arc}; | 18 | use 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" } | |||
39 | stdx = { path = "../stdx", version = "0.0.0" } | 39 | stdx = { path = "../stdx", version = "0.0.0" } |
40 | flycheck = { path = "../flycheck", version = "0.0.0" } | 40 | flycheck = { path = "../flycheck", version = "0.0.0" } |
41 | ide = { path = "../ide", version = "0.0.0" } | 41 | ide = { path = "../ide", version = "0.0.0" } |
42 | ide_db = { path = "../ide_db", version = "0.0.0" } | ||
42 | profile = { path = "../profile", version = "0.0.0" } | 43 | profile = { path = "../profile", version = "0.0.0" } |
43 | project_model = { path = "../project_model", version = "0.0.0" } | 44 | project_model = { path = "../project_model", version = "0.0.0" } |
44 | syntax = { path = "../syntax", version = "0.0.0" } | 45 | syntax = { path = "../syntax", version = "0.0.0" } |
@@ -49,7 +50,6 @@ cfg = { path = "../cfg", version = "0.0.0" } | |||
49 | toolchain = { path = "../toolchain", version = "0.0.0" } | 50 | toolchain = { path = "../toolchain", version = "0.0.0" } |
50 | 51 | ||
51 | # This should only be used in CLI | 52 | # This should only be used in CLI |
52 | ide_db = { path = "../ide_db", version = "0.0.0" } | ||
53 | ssr = { path = "../ssr", version = "0.0.0" } | 53 | ssr = { path = "../ssr", version = "0.0.0" } |
54 | hir = { path = "../hir", version = "0.0.0" } | 54 | hir = { path = "../hir", version = "0.0.0" } |
55 | hir_def = { path = "../hir_def", version = "0.0.0" } | 55 | hir_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 | ||
12 | use flycheck::FlycheckConfig; | 12 | use flycheck::FlycheckConfig; |
13 | use hir::PrefixKind; | 13 | use hir::PrefixKind; |
14 | use ide::{ | 14 | use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig}; |
15 | AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, | 15 | use ide_db::helpers::insert_use::MergeBehaviour; |
16 | MergeBehaviour, | ||
17 | }; | ||
18 | use lsp_types::{ClientCapabilities, MarkupKind}; | 16 | use lsp_types::{ClientCapabilities, MarkupKind}; |
19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; | 17 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; |
20 | use rustc_hash::FxHashSet; | 18 | use 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" } | |||
28 | text_edit = { path = "../text_edit", version = "0.0.0" } | 28 | text_edit = { path = "../text_edit", version = "0.0.0" } |
29 | parser = { path = "../parser", version = "0.0.0" } | 29 | parser = { path = "../parser", version = "0.0.0" } |
30 | test_utils = { path = "../test_utils", version = "0.0.0" } | 30 | test_utils = { path = "../test_utils", version = "0.0.0" } |
31 | profile = { path = "../profile", version = "0.0.0" } | ||
31 | 32 | ||
32 | [dev-dependencies] | 33 | [dev-dependencies] |
33 | walkdir = "2.3.1" | 34 | walkdir = "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 | ||
128 | impl TreeDiff { | 128 | impl 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. |
156 | pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | 158 | pub 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); |
diff --git a/docs/user/generated_diagnostic.adoc b/docs/user/generated_diagnostic.adoc index 34c4f98a3..ec8581a03 100644 --- a/docs/user/generated_diagnostic.adoc +++ b/docs/user/generated_diagnostic.adoc | |||
@@ -17,6 +17,12 @@ This diagnostic is shown for code with inactive `#[cfg]` attributes. | |||
17 | This diagnostic is triggered if item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. | 17 | This diagnostic is triggered if item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. |
18 | 18 | ||
19 | 19 | ||
20 | === macro-error | ||
21 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L167[diagnostics.rs] | ||
22 | |||
23 | This diagnostic is shown for macro expansion errors. | ||
24 | |||
25 | |||
20 | === mismatched-arg-count | 26 | === mismatched-arg-count |
21 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_ty/src/diagnostics.rs#L267[diagnostics.rs] | 27 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_ty/src/diagnostics.rs#L267[diagnostics.rs] |
22 | 28 | ||
@@ -103,3 +109,11 @@ This diagnostic is triggered if rust-analyzer is unable to discover imported mod | |||
103 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L18[diagnostics.rs] | 109 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L18[diagnostics.rs] |
104 | 110 | ||
105 | This diagnostic is triggered if rust-analyzer is unable to discover referred module. | 111 | This diagnostic is triggered if rust-analyzer is unable to discover referred module. |
112 | |||
113 | |||
114 | === unresolved-proc-macro | ||
115 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/hir_def/src/diagnostics.rs#L131[diagnostics.rs] | ||
116 | |||
117 | This diagnostic is shown when a procedural macro can not be found. This usually means that | ||
118 | procedural macro support is simply disabled (and hence is only a weak hint instead of an error), | ||
119 | but can also indicate project setup problems. | ||