aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assist_context.rs39
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs2
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs103
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs2
-rw-r--r--crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs320
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs4
-rw-r--r--crates/ra_assists/src/handlers/introduce_named_lifetime.rs19
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs37
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs164
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_assists/src/tests/generated.rs15
-rw-r--r--crates/ra_db/src/input.rs20
-rw-r--r--crates/ra_db/src/lib.rs79
-rw-r--r--crates/ra_flycheck/src/lib.rs24
-rw-r--r--crates/ra_hir/src/code_model.rs26
-rw-r--r--crates/ra_hir/src/db.rs14
-rw-r--r--crates/ra_hir/src/semantics.rs3
-rw-r--r--crates/ra_hir/src/source_analyzer.rs3
-rw-r--r--crates/ra_hir_def/Cargo.toml3
-rw-r--r--crates/ra_hir_def/src/attr.rs8
-rw-r--r--crates/ra_hir_def/src/body.rs2
-rw-r--r--crates/ra_hir_def/src/data.rs8
-rw-r--r--crates/ra_hir_def/src/db.rs18
-rw-r--r--crates/ra_hir_def/src/docs.rs50
-rw-r--r--crates/ra_hir_def/src/find_path.rs247
-rw-r--r--crates/ra_hir_def/src/import_map.rs679
-rw-r--r--crates/ra_hir_def/src/item_scope.rs23
-rw-r--r--crates/ra_hir_def/src/lib.rs12
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs39
-rw-r--r--crates/ra_hir_def/src/nameres/mod_resolution.rs2
-rw-r--r--crates/ra_hir_def/src/path.rs35
-rw-r--r--crates/ra_hir_def/src/per_ns.rs10
-rw-r--r--crates/ra_hir_def/src/test_db.rs23
-rw-r--r--crates/ra_hir_expand/Cargo.toml1
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs31
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs38
-rw-r--r--crates/ra_hir_expand/src/eager.rs32
-rw-r--r--crates/ra_hir_expand/src/lib.rs11
-rw-r--r--crates/ra_hir_expand/src/name.rs1
-rw-r--r--crates/ra_hir_expand/src/test_db.rs20
-rw-r--r--crates/ra_hir_ty/Cargo.toml4
-rw-r--r--crates/ra_hir_ty/src/db.rs20
-rw-r--r--crates/ra_hir_ty/src/display.rs39
-rw-r--r--crates/ra_hir_ty/src/expr.rs10
-rw-r--r--crates/ra_hir_ty/src/infer.rs52
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs28
-rw-r--r--crates/ra_hir_ty/src/infer/path.rs3
-rw-r--r--crates/ra_hir_ty/src/lib.rs104
-rw-r--r--crates/ra_hir_ty/src/lower.rs214
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs12
-rw-r--r--crates/ra_hir_ty/src/primitive.rs54
-rw-r--r--crates/ra_hir_ty/src/test_db.rs22
-rw-r--r--crates/ra_hir_ty/src/tests/display_source_code.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/method_resolution.rs54
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs112
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs174
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs52
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/interner.rs2
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/mapping.rs74
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/tls.rs5
-rw-r--r--crates/ra_ide/src/completion.rs78
-rw-r--r--crates/ra_ide/src/diagnostics.rs75
-rw-r--r--crates/ra_ide/src/display/function_signature.rs13
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs66
-rw-r--r--crates/ra_ide/src/goto_definition.rs26
-rw-r--r--crates/ra_ide/src/hover.rs411
-rw-r--r--crates/ra_ide/src/inlay_hints.rs7
-rw-r--r--crates/ra_ide/src/lib.rs43
-rw-r--r--crates/ra_ide/src/references/rename.rs6
-rw-r--r--crates/ra_ide/src/runnables.rs342
-rw-r--r--crates/ra_ide/src/snapshots/highlight_doctest.html71
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html2
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html5
-rw-r--r--crates/ra_ide/src/snapshots/highlight_unsafe.html49
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html6
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html2
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs148
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs168
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs8
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs123
-rw-r--r--crates/ra_ide/src/typing.rs44
-rw-r--r--crates/ra_ide_db/src/change.rs11
-rw-r--r--crates/ra_ide_db/src/defs.rs59
-rw-r--r--crates/ra_ide_db/src/imports_locator.rs46
-rw-r--r--crates/ra_ide_db/src/lib.rs37
-rw-r--r--crates/ra_ide_db/src/source_change.rs23
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs53
-rw-r--r--crates/ra_parser/src/grammar.rs7
-rw-r--r--crates/ra_parser/src/grammar/items.rs20
-rw-r--r--crates/ra_parser/src/grammar/paths.rs2
-rw-r--r--crates/ra_parser/src/grammar/patterns.rs8
-rw-r--r--crates/ra_parser/src/grammar/type_params.rs6
-rw-r--r--crates/ra_parser/src/grammar/types.rs16
-rw-r--r--crates/ra_proc_macro_srv/Cargo.toml1
-rw-r--r--crates/ra_proc_macro_srv/src/tests/utils.rs3
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs2
-rw-r--r--crates/ra_project_model/src/json_project.rs55
-rw-r--r--crates/ra_project_model/src/lib.rs111
-rw-r--r--crates/ra_syntax/src/ast.rs8
-rw-r--r--crates/ra_syntax/src/ast/edit.rs13
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs276
-rw-r--r--crates/ra_syntax/src/ast/generated/tokens.rs8
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs52
-rw-r--r--crates/ra_syntax/src/ast/traits.rs13
-rw-r--r--crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast74
-rw-r--r--crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast15
-rw-r--r--crates/ra_syntax/test_data/parser/err/0043_default_const.rast40
-rw-r--r--crates/ra_syntax/test_data/parser/err/0043_default_const.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast240
-rw-r--r--crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs9
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast34
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast117
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs2
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast292
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast40
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast18
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast38
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0066_default_const.rast44
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0066_default_const.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast392
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs30
-rw-r--r--crates/rust-analyzer/Cargo.toml4
-rw-r--r--crates/rust-analyzer/src/bin/main.rs41
-rw-r--r--crates/rust-analyzer/src/caps.rs3
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs89
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs64
-rw-r--r--crates/rust-analyzer/src/config.rs84
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap1
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap3
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs4
-rw-r--r--crates/rust-analyzer/src/from_proto.rs8
-rw-r--r--crates/rust-analyzer/src/global_state.rs (renamed from crates/rust-analyzer/src/world.rs)72
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs80
-rw-r--r--crates/rust-analyzer/src/main_loop.rs150
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs802
-rw-r--r--crates/rust-analyzer/src/to_proto.rs125
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs198
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs31
-rw-r--r--crates/stdx/src/lib.rs5
-rw-r--r--crates/test_utils/Cargo.toml3
-rw-r--r--crates/test_utils/src/lib.rs6
148 files changed, 6455 insertions, 2458 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index 5b1a4680b..edd8255f4 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -1,5 +1,7 @@
1//! See `AssistContext` 1//! See `AssistContext`
2 2
3use std::mem;
4
3use algo::find_covering_element; 5use algo::find_covering_element;
4use hir::Semantics; 6use hir::Semantics;
5use ra_db::{FileId, FileRange}; 7use ra_db::{FileId, FileRange};
@@ -170,13 +172,32 @@ impl Assists {
170 172
171pub(crate) struct AssistBuilder { 173pub(crate) struct AssistBuilder {
172 edit: TextEditBuilder, 174 edit: TextEditBuilder,
173 file: FileId, 175 file_id: FileId,
174 is_snippet: bool, 176 is_snippet: bool,
177 edits: Vec<SourceFileEdit>,
175} 178}
176 179
177impl AssistBuilder { 180impl AssistBuilder {
178 pub(crate) fn new(file: FileId) -> AssistBuilder { 181 pub(crate) fn new(file_id: FileId) -> AssistBuilder {
179 AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } 182 AssistBuilder {
183 edit: TextEditBuilder::default(),
184 file_id,
185 is_snippet: false,
186 edits: Vec::new(),
187 }
188 }
189
190 pub(crate) fn edit_file(&mut self, file_id: FileId) {
191 self.file_id = file_id;
192 }
193
194 fn commit(&mut self) {
195 let edit = mem::take(&mut self.edit).finish();
196 if !edit.is_empty() {
197 let new_edit = SourceFileEdit { file_id: self.file_id, edit };
198 assert!(!self.edits.iter().any(|it| it.file_id == new_edit.file_id));
199 self.edits.push(new_edit);
200 }
180 } 201 }
181 202
182 /// Remove specified `range` of text. 203 /// Remove specified `range` of text.
@@ -234,21 +255,15 @@ impl AssistBuilder {
234 algo::diff(&node, &new).into_text_edit(&mut self.edit) 255 algo::diff(&node, &new).into_text_edit(&mut self.edit)
235 } 256 }
236 257
237 // FIXME: better API
238 pub(crate) fn set_file(&mut self, assist_file: FileId) {
239 self.file = assist_file;
240 }
241
242 // FIXME: kill this API 258 // FIXME: kill this API
243 /// Get access to the raw `TextEditBuilder`. 259 /// Get access to the raw `TextEditBuilder`.
244 pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { 260 pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
245 &mut self.edit 261 &mut self.edit
246 } 262 }
247 263
248 fn finish(self) -> SourceChange { 264 fn finish(mut self) -> SourceChange {
249 let edit = self.edit.finish(); 265 self.commit();
250 let source_file_edit = SourceFileEdit { file_id: self.file, edit }; 266 let mut res: SourceChange = mem::take(&mut self.edits).into();
251 let mut res: SourceChange = source_file_edit.into();
252 if self.is_snippet { 267 if self.is_snippet {
253 res.is_snippet = true; 268 res.is_snippet = true;
254 } 269 }
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index ab20c6649..90b06a625 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -195,7 +195,7 @@ struct Test<K, T = u8> {
195} 195}
196 196
197fn main() { 197fn main() {
198 let test<|> = Test { t: 23, k: 33 }; 198 let test<|> = Test { t: 23u8, k: 33 };
199}"#, 199}"#,
200 r#" 200 r#"
201struct Test<K, T = u8> { 201struct Test<K, T = u8> {
@@ -204,7 +204,7 @@ struct Test<K, T = u8> {
204} 204}
205 205
206fn main() { 206fn main() {
207 let test: Test<i32> = Test { t: 23, k: 33 }; 207 let test: Test<i32> = Test { t: 23u8, k: 33 };
208}"#, 208}"#,
209 ); 209 );
210 } 210 }
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index 24f931a85..1cfbd75aa 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -64,7 +64,7 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
64 let target = call.syntax().text_range(); 64 let target = call.syntax().text_range();
65 acc.add(AssistId("add_function"), "Add function", target, |builder| { 65 acc.add(AssistId("add_function"), "Add function", target, |builder| {
66 let function_template = function_builder.render(); 66 let function_template = function_builder.render();
67 builder.set_file(function_template.file); 67 builder.edit_file(function_template.file);
68 let new_fn = function_template.to_string(ctx.config.snippet_cap); 68 let new_fn = function_template.to_string(ctx.config.snippet_cap);
69 match ctx.config.snippet_cap { 69 match ctx.config.snippet_cap {
70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), 70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index edf96d50e..5092bf336 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -130,7 +130,7 @@ impl AutoImportAssets {
130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { 130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> {
131 let _p = profile("auto_import::search_for_imports"); 131 let _p = profile("auto_import::search_for_imports");
132 let current_crate = self.module_with_name_to_import.krate(); 132 let current_crate = self.module_with_name_to_import.krate();
133 ImportsLocator::new(db) 133 ImportsLocator::new(db, current_crate)
134 .find_imports(&self.get_search_query()) 134 .find_imports(&self.get_search_query())
135 .into_iter() 135 .into_iter()
136 .filter_map(|candidate| match &self.import_candidate { 136 .filter_map(|candidate| match &self.import_candidate {
@@ -841,4 +841,105 @@ fn main() {
841 ", 841 ",
842 ) 842 )
843 } 843 }
844
845 #[test]
846 fn dep_import() {
847 check_assist(
848 auto_import,
849 r"
850 //- /lib.rs crate:dep
851 pub struct Struct;
852
853 //- /main.rs crate:main deps:dep
854 fn main() {
855 Struct<|>
856 }",
857 r"use dep::Struct;
858
859fn main() {
860 Struct
861}
862",
863 );
864 }
865
866 #[test]
867 fn whole_segment() {
868 // Tests that only imports whose last segment matches the identifier get suggested.
869 check_assist(
870 auto_import,
871 r"
872 //- /lib.rs crate:dep
873 pub mod fmt {
874 pub trait Display {}
875 }
876
877 pub fn panic_fmt() {}
878
879 //- /main.rs crate:main deps:dep
880 struct S;
881
882 impl f<|>mt::Display for S {}",
883 r"use dep::fmt;
884
885struct S;
886impl fmt::Display for S {}
887",
888 );
889 }
890
891 #[test]
892 fn macro_generated() {
893 // Tests that macro-generated items are suggested from external crates.
894 check_assist(
895 auto_import,
896 r"
897 //- /lib.rs crate:dep
898
899 macro_rules! mac {
900 () => {
901 pub struct Cheese;
902 };
903 }
904
905 mac!();
906
907 //- /main.rs crate:main deps:dep
908
909 fn main() {
910 Cheese<|>;
911 }",
912 r"use dep::Cheese;
913
914fn main() {
915 Cheese;
916}
917",
918 );
919 }
920
921 #[test]
922 fn casing() {
923 // Tests that differently cased names don't interfere and we only suggest the matching one.
924 check_assist(
925 auto_import,
926 r"
927 //- /lib.rs crate:dep
928
929 pub struct FMT;
930 pub struct fmt;
931
932 //- /main.rs crate:main deps:dep
933
934 fn main() {
935 FMT<|>;
936 }",
937 r"use dep::FMT;
938
939fn main() {
940 FMT;
941}
942",
943 );
944 }
844} 945}
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index 4cc75a7ce..dfade7432 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -154,7 +154,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
154 parent_block: &ast::BlockExpr, 154 parent_block: &ast::BlockExpr,
155 if_expr: &ast::IfExpr, 155 if_expr: &ast::IfExpr,
156 ) -> SyntaxNode { 156 ) -> SyntaxNode {
157 let then_block_items = then_block.dedent(IndentLevel::from(1)); 157 let then_block_items = then_block.dedent(IndentLevel(1));
158 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); 158 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
159 let end_of_then = 159 let end_of_then =
160 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { 160 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
new file mode 100644
index 000000000..44db7917a
--- /dev/null
+++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -0,0 +1,320 @@
1use hir::{EnumVariant, Module, ModuleDef, Name};
2use ra_db::FileId;
3use ra_fmt::leading_indent;
4use ra_ide_db::{defs::Definition, search::Reference, RootDatabase};
5use ra_syntax::{
6 algo::find_node_at_offset,
7 ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner},
8 SourceFile, SyntaxNode, TextRange, TextSize,
9};
10use rustc_hash::FxHashSet;
11
12use crate::{
13 assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, Assists,
14};
15
16// Assist: extract_struct_from_enum_variant
17//
18// Extracts a struct from enum variant.
19//
20// ```
21// enum A { <|>One(u32, u32) }
22// ```
23// ->
24// ```
25// struct One(pub u32, pub u32);
26//
27// enum A { One(One) }
28// ```
29pub(crate) fn extract_struct_from_enum_variant(
30 acc: &mut Assists,
31 ctx: &AssistContext,
32) -> Option<()> {
33 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
34 let field_list = match variant.kind() {
35 ast::StructKind::Tuple(field_list) => field_list,
36 _ => return None,
37 };
38 let variant_name = variant.name()?.to_string();
39 let variant_hir = ctx.sema.to_def(&variant)?;
40 if existing_struct_def(ctx.db, &variant_name, &variant_hir) {
41 return None;
42 }
43 let enum_ast = variant.parent_enum();
44 let visibility = enum_ast.visibility();
45 let enum_hir = ctx.sema.to_def(&enum_ast)?;
46 let variant_hir_name = variant_hir.name(ctx.db);
47 let enum_module_def = ModuleDef::from(enum_hir);
48 let current_module = enum_hir.module(ctx.db);
49 let target = variant.syntax().text_range();
50 acc.add(
51 AssistId("extract_struct_from_enum_variant"),
52 "Extract struct from enum variant",
53 target,
54 |builder| {
55 let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir));
56 let res = definition.find_usages(&ctx.db, None);
57 let start_offset = variant.parent_enum().syntax().text_range().start();
58 let mut visited_modules_set = FxHashSet::default();
59 visited_modules_set.insert(current_module);
60 for reference in res {
61 let source_file = ctx.sema.parse(reference.file_range.file_id);
62 update_reference(
63 ctx,
64 builder,
65 reference,
66 &source_file,
67 &enum_module_def,
68 &variant_hir_name,
69 &mut visited_modules_set,
70 );
71 }
72 extract_struct_def(
73 builder,
74 enum_ast.syntax(),
75 &variant_name,
76 &field_list.to_string(),
77 start_offset,
78 ctx.frange.file_id,
79 &visibility,
80 );
81 let list_range = field_list.syntax().text_range();
82 update_variant(builder, &variant_name, ctx.frange.file_id, list_range);
83 },
84 )
85}
86
87fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool {
88 variant
89 .parent_enum(db)
90 .module(db)
91 .scope(db, None)
92 .into_iter()
93 .any(|(name, _)| name.to_string() == variant_name.to_string())
94}
95
96fn insert_import(
97 ctx: &AssistContext,
98 builder: &mut AssistBuilder,
99 path: &ast::PathExpr,
100 module: &Module,
101 enum_module_def: &ModuleDef,
102 variant_hir_name: &Name,
103) -> Option<()> {
104 let db = ctx.db;
105 let mod_path = module.find_use_path(db, enum_module_def.clone());
106 if let Some(mut mod_path) = mod_path {
107 mod_path.segments.pop();
108 mod_path.segments.push(variant_hir_name.clone());
109 insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder());
110 }
111 Some(())
112}
113
114fn extract_struct_def(
115 builder: &mut AssistBuilder,
116 enum_ast: &SyntaxNode,
117 variant_name: &str,
118 variant_list: &str,
119 start_offset: TextSize,
120 file_id: FileId,
121 visibility: &Option<ast::Visibility>,
122) -> Option<()> {
123 let visibility_string = if let Some(visibility) = visibility {
124 format!("{} ", visibility.to_string())
125 } else {
126 "".to_string()
127 };
128 let indent = if let Some(indent) = leading_indent(enum_ast) {
129 indent.to_string()
130 } else {
131 "".to_string()
132 };
133 let struct_def = format!(
134 r#"{}struct {}{};
135
136{}"#,
137 visibility_string,
138 variant_name,
139 list_with_visibility(variant_list),
140 indent
141 );
142 builder.edit_file(file_id);
143 builder.insert(start_offset, struct_def);
144 Some(())
145}
146
147fn update_variant(
148 builder: &mut AssistBuilder,
149 variant_name: &str,
150 file_id: FileId,
151 list_range: TextRange,
152) -> Option<()> {
153 let inside_variant_range = TextRange::new(
154 list_range.start().checked_add(TextSize::from(1))?,
155 list_range.end().checked_sub(TextSize::from(1))?,
156 );
157 builder.edit_file(file_id);
158 builder.replace(inside_variant_range, variant_name);
159 Some(())
160}
161
162fn update_reference(
163 ctx: &AssistContext,
164 builder: &mut AssistBuilder,
165 reference: Reference,
166 source_file: &SourceFile,
167 enum_module_def: &ModuleDef,
168 variant_hir_name: &Name,
169 visited_modules_set: &mut FxHashSet<Module>,
170) -> Option<()> {
171 let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
172 source_file.syntax(),
173 reference.file_range.range.start(),
174 )?;
175 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
176 let list = call.arg_list()?;
177 let segment = path_expr.path()?.segment()?;
178 let module = ctx.sema.scope(&path_expr.syntax()).module()?;
179 let list_range = list.syntax().text_range();
180 let inside_list_range = TextRange::new(
181 list_range.start().checked_add(TextSize::from(1))?,
182 list_range.end().checked_sub(TextSize::from(1))?,
183 );
184 builder.edit_file(reference.file_range.file_id);
185 if !visited_modules_set.contains(&module) {
186 if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name)
187 .is_some()
188 {
189 visited_modules_set.insert(module);
190 }
191 }
192 builder.replace(inside_list_range, format!("{}{}", segment, list));
193 Some(())
194}
195
196fn list_with_visibility(list: &str) -> String {
197 list.split(',')
198 .map(|part| {
199 let index = if part.chars().next().unwrap() == '(' { 1usize } else { 0 };
200 let mut mod_part = part.trim().to_string();
201 mod_part.insert_str(index, "pub ");
202 mod_part
203 })
204 .collect::<Vec<String>>()
205 .join(", ")
206}
207
208#[cfg(test)]
209mod tests {
210
211 use crate::{
212 tests::{check_assist, check_assist_not_applicable},
213 utils::FamousDefs,
214 };
215
216 use super::*;
217
218 #[test]
219 fn test_extract_struct_several_fields() {
220 check_assist(
221 extract_struct_from_enum_variant,
222 "enum A { <|>One(u32, u32) }",
223 r#"struct One(pub u32, pub u32);
224
225enum A { One(One) }"#,
226 );
227 }
228
229 #[test]
230 fn test_extract_struct_one_field() {
231 check_assist(
232 extract_struct_from_enum_variant,
233 "enum A { <|>One(u32) }",
234 r#"struct One(pub u32);
235
236enum A { One(One) }"#,
237 );
238 }
239
240 #[test]
241 fn test_extract_struct_pub_visibility() {
242 check_assist(
243 extract_struct_from_enum_variant,
244 "pub enum A { <|>One(u32, u32) }",
245 r#"pub struct One(pub u32, pub u32);
246
247pub enum A { One(One) }"#,
248 );
249 }
250
251 #[test]
252 fn test_extract_struct_with_complex_imports() {
253 check_assist(
254 extract_struct_from_enum_variant,
255 r#"mod my_mod {
256 fn another_fn() {
257 let m = my_other_mod::MyEnum::MyField(1, 1);
258 }
259
260 pub mod my_other_mod {
261 fn another_fn() {
262 let m = MyEnum::MyField(1, 1);
263 }
264
265 pub enum MyEnum {
266 <|>MyField(u8, u8),
267 }
268 }
269}
270
271fn another_fn() {
272 let m = my_mod::my_other_mod::MyEnum::MyField(1, 1);
273}"#,
274 r#"use my_mod::my_other_mod::MyField;
275
276mod my_mod {
277 use my_other_mod::MyField;
278
279 fn another_fn() {
280 let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
281 }
282
283 pub mod my_other_mod {
284 fn another_fn() {
285 let m = MyEnum::MyField(MyField(1, 1));
286 }
287
288 pub struct MyField(pub u8, pub u8);
289
290 pub enum MyEnum {
291 MyField(MyField),
292 }
293 }
294}
295
296fn another_fn() {
297 let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1));
298}"#,
299 );
300 }
301
302 fn check_not_applicable(ra_fixture: &str) {
303 let fixture =
304 format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
305 check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
306 }
307
308 #[test]
309 fn test_extract_enum_not_applicable_for_element_with_no_fields() {
310 check_not_applicable("enum A { <|>One }");
311 }
312
313 #[test]
314 fn test_extract_enum_not_applicable_if_struct_exists() {
315 check_not_applicable(
316 r#"struct One;
317 enum A { <|>One(u8) }"#,
318 );
319 }
320}
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
index 9ec42f568..531b3560f 100644
--- a/crates/ra_assists/src/handlers/fix_visibility.rs
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -63,7 +63,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
63 }; 63 };
64 64
65 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 65 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| {
66 builder.set_file(target_file); 66 builder.edit_file(target_file);
67 match ctx.config.snippet_cap { 67 match ctx.config.snippet_cap {
68 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 68 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
69 None => builder.insert(offset, format!("{} ", missing_visibility)), 69 None => builder.insert(offset, format!("{} ", missing_visibility)),
@@ -106,7 +106,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
106 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); 106 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
107 107
108 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 108 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| {
109 builder.set_file(target_file); 109 builder.edit_file(target_file);
110 match ctx.config.snippet_cap { 110 match ctx.config.snippet_cap {
111 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 111 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
112 None => builder.insert(offset, format!("{} ", missing_visibility)), 112 None => builder.insert(offset, format!("{} ", missing_visibility)),
diff --git a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
index beb5b7366..28fcbc9ba 100644
--- a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
@@ -41,8 +41,6 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -
41 if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) { 41 if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) {
42 generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range()) 42 generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range())
43 } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) { 43 } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) {
44 // only allow naming the last anonymous lifetime
45 lifetime_token.next_token().filter(|tok| tok.kind() == SyntaxKind::R_ANGLE)?;
46 generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range()) 44 generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range())
47 } else { 45 } else {
48 None 46 None
@@ -191,6 +189,23 @@ mod tests {
191 } 189 }
192 190
193 #[test] 191 #[test]
192 fn test_impl_with_other_type_param() {
193 check_assist(
194 introduce_named_lifetime,
195 "impl<I> fmt::Display for SepByBuilder<'_<|>, I>
196 where
197 I: Iterator,
198 I::Item: fmt::Display,
199 {",
200 "impl<I, 'a> fmt::Display for SepByBuilder<'a, I>
201 where
202 I: Iterator,
203 I::Item: fmt::Display,
204 {",
205 )
206 }
207
208 #[test]
194 fn test_example_case_cursor_before_tick() { 209 fn test_example_case_cursor_before_tick() {
195 check_assist( 210 check_assist(
196 introduce_named_lifetime, 211 introduce_named_lifetime,
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
index e016f51c3..dfcd787de 100644
--- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
@@ -51,6 +51,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
51 acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { 51 acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| {
52 let match_expr = { 52 let match_expr = {
53 let then_arm = { 53 let then_arm = {
54 let then_block = then_block.reset_indent().indent(IndentLevel(1));
54 let then_expr = unwrap_trivial_block(then_block); 55 let then_expr = unwrap_trivial_block(then_block);
55 make::match_arm(vec![pat.clone()], then_expr) 56 make::match_arm(vec![pat.clone()], then_expr)
56 }; 57 };
@@ -64,8 +65,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
64 let else_expr = unwrap_trivial_block(else_block); 65 let else_expr = unwrap_trivial_block(else_block);
65 make::match_arm(vec![pattern], else_expr) 66 make::match_arm(vec![pattern], else_expr)
66 }; 67 };
67 make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) 68 let match_expr = make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]));
68 .indent(IndentLevel::from_node(if_expr.syntax())) 69 match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
69 }; 70 };
70 71
71 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); 72 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
@@ -213,4 +214,36 @@ fn foo(x: Result<i32, ()>) {
213 "#, 214 "#,
214 ); 215 );
215 } 216 }
217
218 #[test]
219 fn nested_indent() {
220 check_assist(
221 replace_if_let_with_match,
222 r#"
223fn main() {
224 if true {
225 <|>if let Ok(rel_path) = path.strip_prefix(root_path) {
226 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
227 Some((*id, rel_path))
228 } else {
229 None
230 }
231 }
232}
233"#,
234 r#"
235fn main() {
236 if true {
237 match path.strip_prefix(root_path) {
238 Ok(rel_path) => {
239 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
240 Some((*id, rel_path))
241 }
242 _ => None,
243 }
244 }
245}
246"#,
247 )
248 }
216} 249}
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs
index 8440c7d0f..1fb13f481 100644
--- a/crates/ra_assists/src/handlers/unwrap_block.rs
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -1,7 +1,10 @@
1use ra_fmt::unwrap_trivial_block; 1use ra_fmt::unwrap_trivial_block;
2use ra_syntax::{ 2use ra_syntax::{
3 ast::{self, ElseBranch, Expr, LoopBodyOwner}, 3 ast::{
4 match_ast, AstNode, TextRange, T, 4 self,
5 edit::{AstNodeEdit, IndentLevel},
6 },
7 AstNode, TextRange, T,
5}; 8};
6 9
7use crate::{AssistContext, AssistId, Assists}; 10use crate::{AssistContext, AssistId, Assists};
@@ -24,94 +27,73 @@ use crate::{AssistContext, AssistId, Assists};
24// } 27// }
25// ``` 28// ```
26pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 29pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
28 let block = ast::BlockExpr::cast(l_curly_token.parent())?;
29 let parent = block.syntax().parent()?;
30 let assist_id = AssistId("unwrap_block"); 30 let assist_id = AssistId("unwrap_block");
31 let assist_label = "Unwrap block"; 31 let assist_label = "Unwrap block";
32 32
33 let (expr, expr_to_unwrap) = match_ast! { 33 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
34 match parent { 34 let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
35 ast::ForExpr(for_expr) => { 35 let mut parent = block.syntax().parent()?;
36 let block_expr = for_expr.loop_body()?; 36 if ast::MatchArm::can_cast(parent.kind()) {
37 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; 37 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
38 (ast::Expr::ForExpr(for_expr), expr_to_unwrap) 38 }
39 },
40 ast::WhileExpr(while_expr) => {
41 let block_expr = while_expr.loop_body()?;
42 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
43 (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)
44 },
45 ast::LoopExpr(loop_expr) => {
46 let block_expr = loop_expr.loop_body()?;
47 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
48 (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)
49 },
50 ast::IfExpr(if_expr) => {
51 let mut resp = None;
52
53 let then_branch = if_expr.then_branch()?;
54 if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) {
55 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
56 // For `else if` blocks
57 let ancestor_then_branch = ancestor.then_branch()?;
58 let l_curly_token = then_branch.l_curly_token()?;
59
60 let target = then_branch.syntax().text_range();
61 return acc.add(assist_id, assist_label, target, |edit| {
62 let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
63 let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end());
64
65 edit.delete(range_to_del_rest);
66 edit.delete(range_to_del_else_if);
67 edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{']));
68 });
69 } else {
70 resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch)));
71 }
72 } else if let Some(else_branch) = if_expr.else_branch() {
73 match else_branch {
74 ElseBranch::Block(else_block) => {
75 let l_curly_token = else_block.l_curly_token()?;
76 if l_curly_token.text_range().contains_range(ctx.frange.range) {
77 let target = else_block.syntax().text_range();
78 return acc.add(assist_id, assist_label, target, |edit| {
79 let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
80
81 edit.delete(range_to_del);
82 edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{']));
83 });
84 }
85 },
86 ElseBranch::IfExpr(_) => {},
87 }
88 }
89 39
90 resp? 40 let parent = ast::Expr::cast(parent)?;
91 }, 41
92 _ => return None, 42 match parent.clone() {
43 ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
44 ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
45 ast::Expr::IfExpr(if_expr) => {
46 let then_branch = if_expr.then_branch()?;
47 if then_branch == block {
48 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
49 // For `else if` blocks
50 let ancestor_then_branch = ancestor.then_branch()?;
51
52 let target = then_branch.syntax().text_range();
53 return acc.add(assist_id, assist_label, target, |edit| {
54 let range_to_del_else_if = TextRange::new(
55 ancestor_then_branch.syntax().text_range().end(),
56 l_curly_token.text_range().start(),
57 );
58 let range_to_del_rest = TextRange::new(
59 then_branch.syntax().text_range().end(),
60 if_expr.syntax().text_range().end(),
61 );
62
63 edit.delete(range_to_del_rest);
64 edit.delete(range_to_del_else_if);
65 edit.replace(
66 target,
67 update_expr_string(then_branch.to_string(), &[' ', '{']),
68 );
69 });
70 }
71 } else {
72 let target = block.syntax().text_range();
73 return acc.add(assist_id, assist_label, target, |edit| {
74 let range_to_del = TextRange::new(
75 then_branch.syntax().text_range().end(),
76 l_curly_token.text_range().start(),
77 );
78
79 edit.delete(range_to_del);
80 edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
81 });
82 }
93 } 83 }
84 _ => return None,
94 }; 85 };
95 86
96 let target = expr_to_unwrap.syntax().text_range(); 87 let unwrapped = unwrap_trivial_block(block);
97 acc.add(assist_id, assist_label, target, |edit| { 88 let target = unwrapped.syntax().text_range();
98 edit.replace( 89 acc.add(assist_id, assist_label, target, |builder| {
99 expr.syntax().text_range(), 90 builder.replace(
100 update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), 91 parent.syntax().text_range(),
92 update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
101 ); 93 );
102 }) 94 })
103} 95}
104 96
105fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::Expr> {
106 let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
107
108 if cursor_in_range {
109 Some(unwrap_trivial_block(block))
110 } else {
111 None
112 }
113}
114
115fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { 97fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
116 let expr_string = expr_str.trim_start_matches(trim_start_pat); 98 let expr_string = expr_str.trim_start_matches(trim_start_pat);
117 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); 99 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
@@ -490,6 +472,30 @@ mod tests {
490 } 472 }
491 473
492 #[test] 474 #[test]
475 fn unwrap_match_arm() {
476 check_assist(
477 unwrap_block,
478 r#"
479fn main() {
480 match rel_path {
481 Ok(rel_path) => {<|>
482 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
483 Some((*id, rel_path))
484 }
485 Err(_) => None,
486 }
487}
488"#,
489 r#"
490fn main() {
491 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
492 Some((*id, rel_path))
493}
494"#,
495 );
496 }
497
498 #[test]
493 fn simple_if_in_while_bad_cursor_position() { 499 fn simple_if_in_while_bad_cursor_position() {
494 check_assist_not_applicable( 500 check_assist_not_applicable(
495 unwrap_block, 501 unwrap_block,
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index fb5d59a87..185428bd5 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -115,6 +115,7 @@ mod handlers {
115 mod change_return_type_to_result; 115 mod change_return_type_to_result;
116 mod change_visibility; 116 mod change_visibility;
117 mod early_return; 117 mod early_return;
118 mod extract_struct_from_enum_variant;
118 mod fill_match_arms; 119 mod fill_match_arms;
119 mod fix_visibility; 120 mod fix_visibility;
120 mod flip_binexpr; 121 mod flip_binexpr;
@@ -155,6 +156,7 @@ mod handlers {
155 change_return_type_to_result::change_return_type_to_result, 156 change_return_type_to_result::change_return_type_to_result,
156 change_visibility::change_visibility, 157 change_visibility::change_visibility,
157 early_return::convert_to_guarded_return, 158 early_return::convert_to_guarded_return,
159 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
158 fill_match_arms::fill_match_arms, 160 fill_match_arms::fill_match_arms,
159 fix_visibility::fix_visibility, 161 fix_visibility::fix_visibility,
160 flip_binexpr::flip_binexpr, 162 flip_binexpr::flip_binexpr,
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index d17504529..40a223727 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -338,6 +338,21 @@ fn main() {
338} 338}
339 339
340#[test] 340#[test]
341fn doctest_extract_struct_from_enum_variant() {
342 check_doc_test(
343 "extract_struct_from_enum_variant",
344 r#####"
345enum A { <|>One(u32, u32) }
346"#####,
347 r#####"
348struct One(pub u32, pub u32);
349
350enum A { One(One) }
351"#####,
352 )
353}
354
355#[test]
341fn doctest_fill_match_arms() { 356fn doctest_fill_match_arms() {
342 check_doc_test( 357 check_doc_test(
343 "fill_match_arms", 358 "fill_match_arms",
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 4d2d3b48a..bf26048f2 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -15,12 +15,10 @@ use std::{
15 15
16use ra_cfg::CfgOptions; 16use ra_cfg::CfgOptions;
17use ra_syntax::SmolStr; 17use ra_syntax::SmolStr;
18use rustc_hash::FxHashMap; 18use ra_tt::TokenExpander;
19use rustc_hash::FxHashSet; 19use rustc_hash::{FxHashMap, FxHashSet};
20 20
21use crate::{RelativePath, RelativePathBuf}; 21use crate::{RelativePath, RelativePathBuf};
22use fmt::Display;
23use ra_tt::TokenExpander;
24 22
25/// `FileId` is an integer which uniquely identifies a file. File paths are 23/// `FileId` is an integer which uniquely identifies a file. File paths are
26/// messy and system-dependent, so most of the code should work directly with 24/// messy and system-dependent, so most of the code should work directly with
@@ -111,7 +109,7 @@ impl CrateName {
111 } 109 }
112} 110}
113 111
114impl Display for CrateName { 112impl fmt::Display for CrateName {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 write!(f, "{}", self.0) 114 write!(f, "{}", self.0)
117 } 115 }
@@ -337,15 +335,11 @@ impl Env {
337} 335}
338 336
339impl ExternSource { 337impl ExternSource {
340 pub fn extern_path(&self, path: impl AsRef<Path>) -> Option<(ExternSourceId, RelativePathBuf)> { 338 pub fn extern_path(&self, path: &Path) -> Option<(ExternSourceId, RelativePathBuf)> {
341 let path = path.as_ref();
342 self.extern_paths.iter().find_map(|(root_path, id)| { 339 self.extern_paths.iter().find_map(|(root_path, id)| {
343 if let Ok(rel_path) = path.strip_prefix(root_path) { 340 let rel_path = path.strip_prefix(root_path).ok()?;
344 let rel_path = RelativePathBuf::from_path(rel_path).ok()?; 341 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
345 Some((*id, rel_path)) 342 Some((*id, rel_path))
346 } else {
347 None
348 }
349 }) 343 })
350 } 344 }
351 345
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index fd4280de2..80ddb6058 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -7,12 +7,13 @@ use std::{panic, sync::Arc};
7 7
8use ra_prof::profile; 8use ra_prof::profile;
9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; 9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize};
10use rustc_hash::FxHashSet;
10 11
11pub use crate::{ 12pub use crate::{
12 cancellation::Canceled, 13 cancellation::Canceled,
13 input::{ 14 input::{
14 CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, 15 CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource,
15 FileId, ProcMacroId, SourceRoot, SourceRootId, 16 ExternSourceId, FileId, ProcMacroId, SourceRoot, SourceRootId,
16 }, 17 },
17}; 18};
18pub use relative_path::{RelativePath, RelativePathBuf}; 19pub use relative_path::{RelativePath, RelativePathBuf};
@@ -89,15 +90,13 @@ pub const DEFAULT_LRU_CAP: usize = 128;
89pub trait FileLoader { 90pub trait FileLoader {
90 /// Text of the file. 91 /// Text of the file.
91 fn file_text(&self, file_id: FileId) -> Arc<String>; 92 fn file_text(&self, file_id: FileId) -> Arc<String>;
92 fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath) 93 /// Note that we intentionally accept a `&str` and not a `&Path` here. This
93 -> Option<FileId>; 94 /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such,
94 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; 95 /// so the input is guaranteed to be utf-8 string. We might introduce
95 96 /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we
96 fn resolve_extern_path( 97 /// get by with a `&str` for the time being.
97 &self, 98 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>;
98 extern_id: ExternSourceId, 99 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>;
99 relative_path: &RelativePath,
100 ) -> Option<FileId>;
101} 100}
102 101
103/// Database which stores all significant input facts: source code and project 102/// Database which stores all significant input facts: source code and project
@@ -135,16 +134,21 @@ pub trait SourceDatabaseExt: SourceDatabase {
135 #[salsa::input] 134 #[salsa::input]
136 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; 135 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
137 136
138 fn source_root_crates(&self, id: SourceRootId) -> Arc<Vec<CrateId>>; 137 fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>;
139} 138}
140 139
141fn source_root_crates( 140fn source_root_crates(
142 db: &(impl SourceDatabaseExt + SourceDatabase), 141 db: &(impl SourceDatabaseExt + SourceDatabase),
143 id: SourceRootId, 142 id: SourceRootId,
144) -> Arc<Vec<CrateId>> { 143) -> Arc<FxHashSet<CrateId>> {
145 let root = db.source_root(id);
146 let graph = db.crate_graph(); 144 let graph = db.crate_graph();
147 let res = root.walk().filter_map(|it| graph.crate_id_for_crate_root(it)).collect::<Vec<_>>(); 145 let res = graph
146 .iter()
147 .filter(|&krate| {
148 let root_file = graph[krate].root_file_id;
149 db.file_source_root(root_file) == id
150 })
151 .collect::<FxHashSet<_>>();
148 Arc::new(res) 152 Arc::new(res)
149} 153}
150 154
@@ -155,33 +159,30 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
155 fn file_text(&self, file_id: FileId) -> Arc<String> { 159 fn file_text(&self, file_id: FileId) -> Arc<String> {
156 SourceDatabaseExt::file_text(self.0, file_id) 160 SourceDatabaseExt::file_text(self.0, file_id)
157 } 161 }
158 fn resolve_relative_path( 162 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
159 &self, 163 // FIXME: this *somehow* should be platform agnostic...
160 anchor: FileId, 164 if std::path::Path::new(path).is_absolute() {
161 relative_path: &RelativePath, 165 let krate = *self.relevant_crates(anchor).iter().next()?;
162 ) -> Option<FileId> { 166 let (extern_source_id, relative_file) =
163 let path = { 167 self.0.crate_graph()[krate].extern_source.extern_path(path.as_ref())?;
164 let mut path = self.0.file_relative_path(anchor); 168
165 assert!(path.pop()); 169 let source_root = self.0.source_root(SourceRootId(extern_source_id.0));
166 path.push(relative_path); 170 source_root.file_by_relative_path(&relative_file)
167 path.normalize() 171 } else {
168 }; 172 let rel_path = {
169 let source_root = self.0.file_source_root(anchor); 173 let mut rel_path = self.0.file_relative_path(anchor);
170 let source_root = self.0.source_root(source_root); 174 assert!(rel_path.pop());
171 source_root.file_by_relative_path(&path) 175 rel_path.push(path);
176 rel_path.normalize()
177 };
178 let source_root = self.0.file_source_root(anchor);
179 let source_root = self.0.source_root(source_root);
180 source_root.file_by_relative_path(&rel_path)
181 }
172 } 182 }
173 183
174 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 184 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
175 let source_root = self.0.file_source_root(file_id); 185 let source_root = self.0.file_source_root(file_id);
176 self.0.source_root_crates(source_root) 186 self.0.source_root_crates(source_root)
177 } 187 }
178
179 fn resolve_extern_path(
180 &self,
181 extern_id: ExternSourceId,
182 relative_path: &RelativePath,
183 ) -> Option<FileId> {
184 let source_root = self.0.source_root(SourceRootId(extern_id.0));
185 source_root.file_by_relative_path(&relative_path)
186 }
187} 188}
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs
index 041e38a9f..6c4170529 100644
--- a/crates/ra_flycheck/src/lib.rs
+++ b/crates/ra_flycheck/src/lib.rs
@@ -18,8 +18,17 @@ pub use cargo_metadata::diagnostic::{
18 18
19#[derive(Clone, Debug, PartialEq, Eq)] 19#[derive(Clone, Debug, PartialEq, Eq)]
20pub enum FlycheckConfig { 20pub enum FlycheckConfig {
21 CargoCommand { command: String, all_targets: bool, all_features: bool, extra_args: Vec<String> }, 21 CargoCommand {
22 CustomCommand { command: String, args: Vec<String> }, 22 command: String,
23 all_targets: bool,
24 all_features: bool,
25 features: Vec<String>,
26 extra_args: Vec<String>,
27 },
28 CustomCommand {
29 command: String,
30 args: Vec<String>,
31 },
23} 32}
24 33
25/// Flycheck wraps the shared state and communication machinery used for 34/// Flycheck wraps the shared state and communication machinery used for
@@ -188,7 +197,13 @@ impl FlycheckThread {
188 self.check_process = None; 197 self.check_process = None;
189 198
190 let mut cmd = match &self.config { 199 let mut cmd = match &self.config {
191 FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { 200 FlycheckConfig::CargoCommand {
201 command,
202 all_targets,
203 all_features,
204 extra_args,
205 features,
206 } => {
192 let mut cmd = Command::new(ra_toolchain::cargo()); 207 let mut cmd = Command::new(ra_toolchain::cargo());
193 cmd.arg(command); 208 cmd.arg(command);
194 cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) 209 cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
@@ -198,6 +213,9 @@ impl FlycheckThread {
198 } 213 }
199 if *all_features { 214 if *all_features {
200 cmd.arg("--all-features"); 215 cmd.arg("--all-features");
216 } else if !features.is_empty() {
217 cmd.arg("--features");
218 cmd.arg(features.join(" "));
201 } 219 }
202 cmd.args(extra_args); 220 cmd.args(extra_args);
203 cmd 221 cmd
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index e40aeffbc..1a9f6cc76 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -9,6 +9,7 @@ use hir_def::{
9 builtin_type::BuiltinType, 9 builtin_type::BuiltinType,
10 docs::Documentation, 10 docs::Documentation,
11 expr::{BindingAnnotation, Pat, PatId}, 11 expr::{BindingAnnotation, Pat, PatId},
12 import_map,
12 per_ns::PerNs, 13 per_ns::PerNs,
13 resolver::{HasResolver, Resolver}, 14 resolver::{HasResolver, Resolver},
14 type_ref::{Mutability, TypeRef}, 15 type_ref::{Mutability, TypeRef},
@@ -98,6 +99,23 @@ impl Crate {
98 db.crate_graph()[self.id].display_name.as_ref().cloned() 99 db.crate_graph()[self.id].display_name.as_ref().cloned()
99 } 100 }
100 101
102 pub fn query_external_importables(
103 self,
104 db: &dyn DefDatabase,
105 query: &str,
106 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
107 import_map::search_dependencies(
108 db,
109 self.into(),
110 import_map::Query::new(query).anchor_end().case_sensitive().limit(40),
111 )
112 .into_iter()
113 .map(|item| match item {
114 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
115 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
116 })
117 }
118
101 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { 119 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
102 db.crate_graph().iter().map(|id| Crate { id }).collect() 120 db.crate_graph().iter().map(|id| Crate { id }).collect()
103 } 121 }
@@ -637,6 +655,10 @@ impl Function {
637 db.function_data(self.id).params.clone() 655 db.function_data(self.id).params.clone()
638 } 656 }
639 657
658 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
659 db.function_data(self.id).is_unsafe
660 }
661
640 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 662 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
641 let _p = profile("Function::diagnostics"); 663 let _p = profile("Function::diagnostics");
642 let infer = db.infer(self.id.into()); 664 let infer = db.infer(self.id.into());
@@ -1190,6 +1212,10 @@ impl Type {
1190 ) 1212 )
1191 } 1213 }
1192 1214
1215 pub fn is_raw_ptr(&self) -> bool {
1216 matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }))
1217 }
1218
1193 pub fn contains_unknown(&self) -> bool { 1219 pub fn contains_unknown(&self) -> bool {
1194 return go(&self.ty.value); 1220 return go(&self.ty.value);
1195 1221
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index ec931b34f..b6b665de1 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -3,11 +3,11 @@
3pub use hir_def::db::{ 3pub use hir_def::db::{
4 AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, 4 AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery,
5 CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, 5 CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery,
6 ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternConstQuery, 6 ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
7 InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery, 7 InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
8 InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery, 8 InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
9 LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery, 9 InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery,
10 TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, 10 StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
11}; 11};
12pub use hir_expand::db::{ 12pub use hir_expand::db::{
13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, 13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery,
@@ -18,8 +18,8 @@ pub use hir_ty::db::{
18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, 18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase,
19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery, 19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery,
20 ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, 20 ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery,
21 InternTypeCtorQuery, InternTypeParamIdQuery, StructDatumQuery, TraitDatumQuery, 21 InternTypeCtorQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery,
22 TraitSolveQuery, TyQuery, ValueTyQuery, 22 TraitDatumQuery, TraitSolveQuery, TyQuery, ValueTyQuery,
23}; 23};
24 24
25#[test] 25#[test]
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 7c1f79f27..a232a5856 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -122,8 +122,9 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
122 let macro_call = 122 let macro_call =
123 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); 123 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call);
124 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); 124 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
125 let krate = sa.resolver.krate()?;
125 let macro_call_id = macro_call 126 let macro_call_id = macro_call
126 .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; 127 .as_call_id(self.db, krate, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?;
127 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) 128 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map)
128 } 129 }
129 130
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 4b509f07c..7c6bbea13 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -307,7 +307,8 @@ impl SourceAnalyzer {
307 db: &dyn HirDatabase, 307 db: &dyn HirDatabase,
308 macro_call: InFile<&ast::MacroCall>, 308 macro_call: InFile<&ast::MacroCall>,
309 ) -> Option<HirFileId> { 309 ) -> Option<HirFileId> {
310 let macro_call_id = macro_call.as_call_id(db.upcast(), |path| { 310 let krate = self.resolver.krate()?;
311 let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
311 self.resolver.resolve_path_as_macro(db.upcast(), &path) 312 self.resolver.resolve_path_as_macro(db.upcast(), &path)
312 })?; 313 })?;
313 Some(macro_call_id.as_file()) 314 Some(macro_call_id.as_file())
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index b85358308..ef1f65ee0 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -14,6 +14,9 @@ rustc-hash = "1.1.0"
14either = "1.5.3" 14either = "1.5.3"
15anymap = "0.12.1" 15anymap = "0.12.1"
16drop_bomb = "0.1.4" 16drop_bomb = "0.1.4"
17fst = { version = "0.4", default-features = false }
18itertools = "0.9.0"
19indexmap = "1.4.0"
17 20
18stdx = { path = "../stdx" } 21stdx = { path = "../stdx" }
19 22
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 8b6c0bede..2eeba0572 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -87,12 +87,18 @@ impl Attrs {
87 } 87 }
88 88
89 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { 89 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
90 let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map(
91 |docs_text| Attr {
92 input: Some(AttrInput::Literal(SmolStr::new(docs_text))),
93 path: ModPath::from(hir_expand::name!(doc)),
94 },
95 );
90 let mut attrs = owner.attrs().peekable(); 96 let mut attrs = owner.attrs().peekable();
91 let entries = if attrs.peek().is_none() { 97 let entries = if attrs.peek().is_none() {
92 // Avoid heap allocation 98 // Avoid heap allocation
93 None 99 None
94 } else { 100 } else {
95 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect()) 101 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect())
96 }; 102 };
97 Attrs { entries } 103 Attrs { entries }
98 } 104 }
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 273036cee..4f2350915 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -97,7 +97,7 @@ impl Expander {
97 97
98 let macro_call = InFile::new(self.current_file_id, &macro_call); 98 let macro_call = InFile::new(self.current_file_id, &macro_call);
99 99
100 if let Some(call_id) = macro_call.as_call_id(db, |path| { 100 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| {
101 if let Some(local_scope) = local_scope { 101 if let Some(local_scope) = local_scope {
102 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { 102 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
103 return Some(def); 103 return Some(def);
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index e2130d931..53599e74a 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -34,6 +34,7 @@ pub struct FunctionData {
34 /// True if the first param is `self`. This is relevant to decide whether this 34 /// True if the first param is `self`. This is relevant to decide whether this
35 /// can be called as a method. 35 /// can be called as a method.
36 pub has_self_param: bool, 36 pub has_self_param: bool,
37 pub is_unsafe: bool,
37 pub visibility: RawVisibility, 38 pub visibility: RawVisibility,
38} 39}
39 40
@@ -85,17 +86,20 @@ impl FunctionData {
85 ret_type 86 ret_type
86 }; 87 };
87 88
89 let is_unsafe = src.value.unsafe_token().is_some();
90
88 let vis_default = RawVisibility::default_for_container(loc.container); 91 let vis_default = RawVisibility::default_for_container(loc.container);
89 let visibility = 92 let visibility =
90 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); 93 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
91 94
92 let sig = FunctionData { name, params, ret_type, has_self_param, visibility, attrs }; 95 let sig =
96 FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs };
93 Arc::new(sig) 97 Arc::new(sig)
94 } 98 }
95} 99}
96 100
97fn desugar_future_path(orig: TypeRef) -> Path { 101fn desugar_future_path(orig: TypeRef) -> Path {
98 let path = path![std::future::Future]; 102 let path = path![core::future::Future];
99 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); 103 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
100 let mut last = GenericArgs::empty(); 104 let mut last = GenericArgs::empty();
101 last.bindings.push(AssociatedTypeBinding { 105 last.bindings.push(AssociatedTypeBinding {
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index 945a0025e..10cc26480 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -1,7 +1,7 @@
1//! Defines database & queries for name resolution. 1//! Defines database & queries for name resolution.
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use hir_expand::{db::AstDatabase, name::Name, HirFileId}; 4use hir_expand::{db::AstDatabase, HirFileId};
5use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; 5use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
6use ra_prof::profile; 6use ra_prof::profile;
7use ra_syntax::SmolStr; 7use ra_syntax::SmolStr;
@@ -12,13 +12,10 @@ use crate::{
12 body::{scope::ExprScopes, Body, BodySourceMap}, 12 body::{scope::ExprScopes, Body, BodySourceMap},
13 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, 13 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
14 docs::Documentation, 14 docs::Documentation,
15 find_path,
16 generics::GenericParams, 15 generics::GenericParams,
17 item_scope::ItemInNs, 16 import_map::ImportMap,
18 lang_item::{LangItemTarget, LangItems}, 17 lang_item::{LangItemTarget, LangItems},
19 nameres::{raw::RawItems, CrateDefMap}, 18 nameres::{raw::RawItems, CrateDefMap},
20 path::ModPath,
21 visibility::Visibility,
22 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, 19 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
23 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, 20 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
24 TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, 21 TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
@@ -113,15 +110,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
113 #[salsa::invoke(Documentation::documentation_query)] 110 #[salsa::invoke(Documentation::documentation_query)]
114 fn documentation(&self, def: AttrDefId) -> Option<Documentation>; 111 fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
115 112
116 #[salsa::invoke(find_path::importable_locations_of_query)] 113 #[salsa::invoke(ImportMap::import_map_query)]
117 fn importable_locations_of( 114 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
118 &self,
119 item: ItemInNs,
120 krate: CrateId,
121 ) -> Arc<[(ModuleId, Name, Visibility)]>;
122
123 #[salsa::invoke(find_path::find_path_inner_query)]
124 fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>;
125} 115}
126 116
127fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 117fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs
index b221ae1ce..2630b3d89 100644
--- a/crates/ra_hir_def/src/docs.rs
+++ b/crates/ra_hir_def/src/docs.rs
@@ -29,6 +29,13 @@ impl Documentation {
29 Documentation(s.into()) 29 Documentation(s.into())
30 } 30 }
31 31
32 pub fn from_ast<N>(node: &N) -> Option<Documentation>
33 where
34 N: ast::DocCommentsOwner + ast::AttrsOwner,
35 {
36 docs_from_ast(node)
37 }
38
32 pub fn as_str(&self) -> &str { 39 pub fn as_str(&self) -> &str {
33 &*self.0 40 &*self.0
34 } 41 }
@@ -70,6 +77,45 @@ impl Documentation {
70 } 77 }
71} 78}
72 79
73pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { 80pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
74 node.doc_comment_text().map(|it| Documentation::new(&it)) 81where
82 N: ast::DocCommentsOwner + ast::AttrsOwner,
83{
84 let doc_comment_text = node.doc_comment_text();
85 let doc_attr_text = expand_doc_attrs(node);
86 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
87 docs.map(|it| Documentation::new(&it))
88}
89
90fn merge_doc_comments_and_attrs(
91 doc_comment_text: Option<String>,
92 doc_attr_text: Option<String>,
93) -> Option<String> {
94 match (doc_comment_text, doc_attr_text) {
95 (Some(mut comment_text), Some(attr_text)) => {
96 comment_text.push_str("\n\n");
97 comment_text.push_str(&attr_text);
98 Some(comment_text)
99 }
100 (Some(comment_text), None) => Some(comment_text),
101 (None, Some(attr_text)) => Some(attr_text),
102 (None, None) => None,
103 }
104}
105
106fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
107 let mut docs = String::new();
108 for attr in owner.attrs() {
109 if let Some(("doc", value)) =
110 attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
111 {
112 docs.push_str(value);
113 docs.push_str("\n\n");
114 }
115 }
116 if docs.is_empty() {
117 None
118 } else {
119 Some(docs.trim_end_matches("\n\n").to_owned())
120 }
75} 121}
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index 4db798473..06701a830 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -1,9 +1,8 @@
1//! An algorithm to find a path to refer to a certain item. 1//! An algorithm to find a path to refer to a certain item.
2 2
3use std::sync::Arc;
4
5use hir_expand::name::{known, AsName, Name}; 3use hir_expand::name::{known, AsName, Name};
6use ra_prof::profile; 4use ra_prof::profile;
5use rustc_hash::FxHashSet;
7use test_utils::mark; 6use test_utils::mark;
8 7
9use crate::{ 8use crate::{
@@ -11,7 +10,7 @@ use crate::{
11 item_scope::ItemInNs, 10 item_scope::ItemInNs,
12 path::{ModPath, PathKind}, 11 path::{ModPath, PathKind},
13 visibility::Visibility, 12 visibility::Visibility,
14 CrateId, ModuleDefId, ModuleId, 13 ModuleDefId, ModuleId,
15}; 14};
16 15
17// FIXME: handle local items 16// FIXME: handle local items
@@ -20,7 +19,7 @@ use crate::{
20/// *from where* you're referring to the item, hence the `from` parameter. 19/// *from where* you're referring to the item, hence the `from` parameter.
21pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
22 let _p = profile("find_path"); 21 let _p = profile("find_path");
23 db.find_path_inner(item, from, MAX_PATH_LEN) 22 find_path_inner(db, item, from, MAX_PATH_LEN)
24} 23}
25 24
26const MAX_PATH_LEN: usize = 15; 25const MAX_PATH_LEN: usize = 15;
@@ -36,20 +35,9 @@ impl ModPath {
36 let first_segment = self.segments.first(); 35 let first_segment = self.segments.first();
37 first_segment == Some(&known::alloc) || first_segment == Some(&known::core) 36 first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
38 } 37 }
39
40 fn len(&self) -> usize {
41 self.segments.len()
42 + match self.kind {
43 PathKind::Plain => 0,
44 PathKind::Super(i) => i as usize,
45 PathKind::Crate => 1,
46 PathKind::Abs => 0,
47 PathKind::DollarCrate(_) => 1,
48 }
49 }
50} 38}
51 39
52pub(crate) fn find_path_inner_query( 40fn find_path_inner(
53 db: &dyn DefDatabase, 41 db: &dyn DefDatabase,
54 item: ItemInNs, 42 item: ItemInNs,
55 from: ModuleId, 43 from: ModuleId,
@@ -133,31 +121,67 @@ pub(crate) fn find_path_inner_query(
133 } 121 }
134 122
135 // - otherwise, look for modules containing (reexporting) it and import it from one of those 123 // - otherwise, look for modules containing (reexporting) it and import it from one of those
124
136 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; 125 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
137 let crate_attrs = db.attrs(crate_root.into()); 126 let crate_attrs = db.attrs(crate_root.into());
138 let prefer_no_std = crate_attrs.by_key("no_std").exists(); 127 let prefer_no_std = crate_attrs.by_key("no_std").exists();
139 let importable_locations = find_importable_locations(db, item, from);
140 let mut best_path = None; 128 let mut best_path = None;
141 let mut best_path_len = max_len; 129 let mut best_path_len = max_len;
142 for (module_id, name) in importable_locations {
143 let mut path = match db.find_path_inner(
144 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
145 from,
146 best_path_len - 1,
147 ) {
148 None => continue,
149 Some(path) => path,
150 };
151 path.segments.push(name);
152 130
153 let new_path = if let Some(best_path) = best_path { 131 if item.krate(db) == Some(from.krate) {
154 select_best_path(best_path, path, prefer_no_std) 132 // Item was defined in the same crate that wants to import it. It cannot be found in any
155 } else { 133 // dependency in this case.
156 path 134
157 }; 135 let local_imports = find_local_import_locations(db, item, from);
158 best_path_len = new_path.len(); 136 for (module_id, name) in local_imports {
159 best_path = Some(new_path); 137 if let Some(mut path) = find_path_inner(
138 db,
139 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
140 from,
141 best_path_len - 1,
142 ) {
143 path.segments.push(name);
144
145 let new_path = if let Some(best_path) = best_path {
146 select_best_path(best_path, path, prefer_no_std)
147 } else {
148 path
149 };
150 best_path_len = new_path.len();
151 best_path = Some(new_path);
152 }
153 }
154 } else {
155 // Item was defined in some upstream crate. This means that it must be exported from one,
156 // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
157 // that wants to import it here, but we always prefer to use the external path here.
158
159 let crate_graph = db.crate_graph();
160 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
161 let import_map = db.import_map(dep.crate_id);
162 import_map.import_info_for(item).and_then(|info| {
163 // Determine best path for containing module and append last segment from `info`.
164 let mut path = find_path_inner(
165 db,
166 ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
167 from,
168 best_path_len - 1,
169 )?;
170 path.segments.push(info.path.segments.last().unwrap().clone());
171 Some(path)
172 })
173 });
174
175 for path in extern_paths {
176 let new_path = if let Some(best_path) = best_path {
177 select_best_path(best_path, path, prefer_no_std)
178 } else {
179 path
180 };
181 best_path = Some(new_path);
182 }
160 } 183 }
184
161 best_path 185 best_path
162} 186}
163 187
@@ -185,69 +209,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
185 } 209 }
186} 210}
187 211
188fn find_importable_locations( 212/// Finds locations in `from.krate` from which `item` can be imported by `from`.
213fn find_local_import_locations(
189 db: &dyn DefDatabase, 214 db: &dyn DefDatabase,
190 item: ItemInNs, 215 item: ItemInNs,
191 from: ModuleId, 216 from: ModuleId,
192) -> Vec<(ModuleId, Name)> { 217) -> Vec<(ModuleId, Name)> {
193 let crate_graph = db.crate_graph(); 218 let _p = profile("find_local_import_locations");
194 let mut result = Vec::new(); 219
195 // We only look in the crate from which we are importing, and the direct 220 // `from` can import anything below `from` with visibility of at least `from`, and anything
196 // dependencies. We cannot refer to names from transitive dependencies 221 // above `from` with any visibility. That means we do not need to descend into private siblings
197 // directly (only through reexports in direct dependencies). 222 // of `from` (and similar).
198 for krate in Some(from.krate) 223
199 .into_iter() 224 let def_map = db.crate_def_map(from.krate);
200 .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) 225
201 { 226 // Compute the initial worklist. We start with all direct child modules of `from` as well as all
202 result.extend( 227 // of its (recursive) parent modules.
203 db.importable_locations_of(item, krate) 228 let data = &def_map.modules[from.local_id];
204 .iter() 229 let mut worklist = data
205 .filter(|(_, _, vis)| vis.is_visible_from(db, from)) 230 .children
206 .map(|(m, n, _)| (*m, n.clone())), 231 .values()
207 ); 232 .map(|child| ModuleId { krate: from.krate, local_id: *child })
208 } 233 .collect::<Vec<_>>();
209 result 234 let mut parent = data.parent;
210} 235 while let Some(p) = parent {
236 worklist.push(ModuleId { krate: from.krate, local_id: p });
237 parent = def_map.modules[p].parent;
238 }
239
240 let mut seen: FxHashSet<_> = FxHashSet::default();
241
242 let mut locations = Vec::new();
243 while let Some(module) = worklist.pop() {
244 if !seen.insert(module) {
245 continue; // already processed this module
246 }
247
248 let ext_def_map;
249 let data = if module.krate == from.krate {
250 &def_map[module.local_id]
251 } else {
252 // The crate might reexport a module defined in another crate.
253 ext_def_map = db.crate_def_map(module.krate);
254 &ext_def_map[module.local_id]
255 };
211 256
212/// Collects all locations from which we might import the item in a particular
213/// crate. These include the original definition of the item, and any
214/// non-private `use`s.
215///
216/// Note that the crate doesn't need to be the one in which the item is defined;
217/// it might be re-exported in other crates.
218pub(crate) fn importable_locations_of_query(
219 db: &dyn DefDatabase,
220 item: ItemInNs,
221 krate: CrateId,
222) -> Arc<[(ModuleId, Name, Visibility)]> {
223 let _p = profile("importable_locations_of_query");
224 let def_map = db.crate_def_map(krate);
225 let mut result = Vec::new();
226 for (local_id, data) in def_map.modules.iter() {
227 if let Some((name, vis)) = data.scope.name_of(item) { 257 if let Some((name, vis)) = data.scope.name_of(item) {
228 let is_private = if let Visibility::Module(private_to) = vis { 258 if vis.is_visible_from(db, from) {
229 private_to.local_id == local_id 259 let is_private = if let Visibility::Module(private_to) = vis {
230 } else { 260 private_to.local_id == module.local_id
231 false 261 } else {
232 }; 262 false
233 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { 263 };
234 data.scope.declarations().any(|it| it == module_def_id) 264 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
235 } else { 265 data.scope.declarations().any(|it| it == module_def_id)
236 false 266 } else {
237 }; 267 false
238 if is_private && !is_original_def { 268 };
269
239 // Ignore private imports. these could be used if we are 270 // Ignore private imports. these could be used if we are
240 // in a submodule of this module, but that's usually not 271 // in a submodule of this module, but that's usually not
241 // what the user wants; and if this module can import 272 // what the user wants; and if this module can import
242 // the item and we're a submodule of it, so can we. 273 // the item and we're a submodule of it, so can we.
243 // Also this keeps the cached data smaller. 274 // Also this keeps the cached data smaller.
244 continue; 275 if !is_private || is_original_def {
276 locations.push((module, name.clone()));
277 }
278 }
279 }
280
281 // Descend into all modules visible from `from`.
282 for (_, per_ns) in data.scope.entries() {
283 if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() {
284 if vis.is_visible_from(db, from) {
285 worklist.push(module);
286 }
245 } 287 }
246 result.push((ModuleId { krate, local_id }, name.clone(), vis));
247 } 288 }
248 } 289 }
249 290
250 Arc::from(result) 291 locations
251} 292}
252 293
253#[cfg(test)] 294#[cfg(test)]
@@ -264,8 +305,8 @@ mod tests {
264 /// `code` needs to contain a cursor marker; checks that `find_path` for the 305 /// `code` needs to contain a cursor marker; checks that `find_path` for the
265 /// item the `path` refers to returns that same path when called from the 306 /// item the `path` refers to returns that same path when called from the
266 /// module the cursor is in. 307 /// module the cursor is in.
267 fn check_found_path(code: &str, path: &str) { 308 fn check_found_path(ra_fixture: &str, path: &str) {
268 let (db, pos) = TestDB::with_position(code); 309 let (db, pos) = TestDB::with_position(ra_fixture);
269 let module = db.module_for_file(pos.file_id); 310 let module = db.module_for_file(pos.file_id);
270 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); 311 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
271 let ast_path = parsed_path_file 312 let ast_path = parsed_path_file
@@ -396,6 +437,44 @@ mod tests {
396 } 437 }
397 438
398 #[test] 439 #[test]
440 fn partially_imported() {
441 // Tests that short paths are used even for external items, when parts of the path are
442 // already in scope.
443 check_found_path(
444 r#"
445 //- /main.rs crate:main deps:ra_syntax
446
447 use ra_syntax::ast;
448 <|>
449
450 //- /lib.rs crate:ra_syntax
451 pub mod ast {
452 pub enum ModuleItem {
453 A, B, C,
454 }
455 }
456 "#,
457 "ast::ModuleItem",
458 );
459
460 check_found_path(
461 r#"
462 //- /main.rs crate:main deps:ra_syntax
463
464 <|>
465
466 //- /lib.rs crate:ra_syntax
467 pub mod ast {
468 pub enum ModuleItem {
469 A, B, C,
470 }
471 }
472 "#,
473 "ra_syntax::ast::ModuleItem",
474 );
475 }
476
477 #[test]
399 fn same_crate_reexport() { 478 fn same_crate_reexport() {
400 let code = r#" 479 let code = r#"
401 //- /main.rs 480 //- /main.rs
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
new file mode 100644
index 000000000..68e20d06b
--- /dev/null
+++ b/crates/ra_hir_def/src/import_map.rs
@@ -0,0 +1,679 @@
1//! A map of all publicly exported items in a crate.
2
3use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
4
5use fst::{self, Streamer};
6use indexmap::{map::Entry, IndexMap};
7use ra_db::CrateId;
8use rustc_hash::FxHasher;
9
10use crate::{
11 db::DefDatabase,
12 item_scope::ItemInNs,
13 path::{ModPath, PathKind},
14 visibility::Visibility,
15 ModuleDefId, ModuleId,
16};
17
18type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
19
20/// Item import details stored in the `ImportMap`.
21#[derive(Debug, Clone, Eq, PartialEq)]
22pub struct ImportInfo {
23 /// A path that can be used to import the item, relative to the crate's root.
24 pub path: ModPath,
25 /// The module containing this item.
26 pub container: ModuleId,
27}
28
29/// A map from publicly exported items to the path needed to import/name them from a downstream
30/// crate.
31///
32/// Reexports of items are taken into account, ie. if something is exported under multiple
33/// names, the one with the shortest import path will be used.
34///
35/// Note that all paths are relative to the containing crate's root, so the crate name still needs
36/// to be prepended to the `ModPath` before the path is valid.
37pub struct ImportMap {
38 map: FxIndexMap<ItemInNs, ImportInfo>,
39
40 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
41 /// values returned by running `fst`.
42 ///
43 /// Since a path can refer to multiple items due to namespacing, we store all items with the
44 /// same path right after each other. This allows us to find all items after the FST gives us
45 /// the index of the first one.
46 importables: Vec<ItemInNs>,
47 fst: fst::Map<Vec<u8>>,
48}
49
50impl ImportMap {
51 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
52 let _p = ra_prof::profile("import_map_query");
53 let def_map = db.crate_def_map(krate);
54 let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default());
55
56 // We look only into modules that are public(ly reexported), starting with the crate root.
57 let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
58 let root = ModuleId { krate, local_id: def_map.root };
59 let mut worklist = vec![(root, empty)];
60 while let Some((module, mod_path)) = worklist.pop() {
61 let ext_def_map;
62 let mod_data = if module.krate == krate {
63 &def_map[module.local_id]
64 } else {
65 // The crate might reexport a module defined in another crate.
66 ext_def_map = db.crate_def_map(module.krate);
67 &ext_def_map[module.local_id]
68 };
69
70 let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
71 let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
72 if per_ns.is_none() {
73 None
74 } else {
75 Some((name, per_ns))
76 }
77 });
78
79 for (name, per_ns) in visible_items {
80 let mk_path = || {
81 let mut path = mod_path.clone();
82 path.segments.push(name.clone());
83 path
84 };
85
86 for item in per_ns.iter_items() {
87 let path = mk_path();
88 match import_map.entry(item) {
89 Entry::Vacant(entry) => {
90 entry.insert(ImportInfo { path, container: module });
91 }
92 Entry::Occupied(mut entry) => {
93 // If the new path is shorter, prefer that one.
94 if path.len() < entry.get().path.len() {
95 *entry.get_mut() = ImportInfo { path, container: module };
96 } else {
97 continue;
98 }
99 }
100 }
101
102 // If we've just added a path to a module, descend into it. We might traverse
103 // modules multiple times, but only if the new path to it is shorter than the
104 // first (else we `continue` above).
105 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
106 worklist.push((mod_id, mk_path()));
107 }
108 }
109 }
110 }
111
112 let mut importables = import_map.iter().collect::<Vec<_>>();
113
114 importables.sort_by(cmp);
115
116 // Build the FST, taking care not to insert duplicate values.
117
118 let mut builder = fst::MapBuilder::memory();
119 let mut last_batch_start = 0;
120
121 for idx in 0..importables.len() {
122 if let Some(next_item) = importables.get(idx + 1) {
123 if cmp(&importables[last_batch_start], next_item) == Ordering::Equal {
124 continue;
125 }
126 }
127
128 let start = last_batch_start;
129 last_batch_start = idx + 1;
130
131 let key = fst_path(&importables[start].1.path);
132
133 builder.insert(key, start as u64).unwrap();
134 }
135
136 let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
137 let importables = importables.iter().map(|(item, _)| **item).collect();
138
139 Arc::new(Self { map: import_map, fst, importables })
140 }
141
142 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
143 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
144 Some(&self.map.get(&item)?.path)
145 }
146
147 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
148 self.map.get(&item)
149 }
150}
151
152impl PartialEq for ImportMap {
153 fn eq(&self, other: &Self) -> bool {
154 // `fst` and `importables` are built from `map`, so we don't need to compare them.
155 self.map == other.map
156 }
157}
158
159impl Eq for ImportMap {}
160
161impl fmt::Debug for ImportMap {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 let mut importable_paths: Vec<_> = self
164 .map
165 .iter()
166 .map(|(item, info)| {
167 let ns = match item {
168 ItemInNs::Types(_) => "t",
169 ItemInNs::Values(_) => "v",
170 ItemInNs::Macros(_) => "m",
171 };
172 format!("- {} ({})", info.path, ns)
173 })
174 .collect();
175
176 importable_paths.sort();
177 f.write_str(&importable_paths.join("\n"))
178 }
179}
180
181fn fst_path(path: &ModPath) -> String {
182 let mut s = path.to_string();
183 s.make_ascii_lowercase();
184 s
185}
186
187fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
188 let lhs_str = fst_path(&lhs.path);
189 let rhs_str = fst_path(&rhs.path);
190 lhs_str.cmp(&rhs_str)
191}
192
193#[derive(Debug)]
194pub struct Query {
195 query: String,
196 lowercased: String,
197 anchor_end: bool,
198 case_sensitive: bool,
199 limit: usize,
200}
201
202impl Query {
203 pub fn new(query: &str) -> Self {
204 Self {
205 lowercased: query.to_lowercase(),
206 query: query.to_string(),
207 anchor_end: false,
208 case_sensitive: false,
209 limit: usize::max_value(),
210 }
211 }
212
213 /// Only returns items whose paths end with the (case-insensitive) query string as their last
214 /// segment.
215 pub fn anchor_end(self) -> Self {
216 Self { anchor_end: true, ..self }
217 }
218
219 /// Limits the returned number of items to `limit`.
220 pub fn limit(self, limit: usize) -> Self {
221 Self { limit, ..self }
222 }
223
224 /// Respect casing of the query string when matching.
225 pub fn case_sensitive(self) -> Self {
226 Self { case_sensitive: true, ..self }
227 }
228}
229
230/// Searches dependencies of `krate` for an importable path matching `query`.
231///
232/// This returns a list of items that could be imported from dependencies of `krate`.
233pub fn search_dependencies<'a>(
234 db: &'a dyn DefDatabase,
235 krate: CrateId,
236 query: Query,
237) -> Vec<ItemInNs> {
238 let _p = ra_prof::profile("search_dependencies").detail(|| format!("{:?}", query));
239
240 let graph = db.crate_graph();
241 let import_maps: Vec<_> =
242 graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
243
244 let automaton = fst::automaton::Subsequence::new(&query.lowercased);
245
246 let mut op = fst::map::OpBuilder::new();
247 for map in &import_maps {
248 op = op.add(map.fst.search(&automaton));
249 }
250
251 let mut stream = op.union();
252 let mut res = Vec::new();
253 while let Some((_, indexed_values)) = stream.next() {
254 for indexed_value in indexed_values {
255 let import_map = &import_maps[indexed_value.index];
256 let importables = &import_map.importables[indexed_value.value as usize..];
257
258 // Path shared by the importable items in this group.
259 let path = &import_map.map[&importables[0]].path;
260
261 if query.anchor_end {
262 // Last segment must match query.
263 let last = path.segments.last().unwrap().to_string();
264 if last.to_lowercase() != query.lowercased {
265 continue;
266 }
267 }
268
269 // Add the items from this `ModPath` group. Those are all subsequent items in
270 // `importables` whose paths match `path`.
271 let iter = importables.iter().copied().take_while(|item| {
272 let item_path = &import_map.map[item].path;
273 fst_path(item_path) == fst_path(path)
274 });
275
276 if query.case_sensitive {
277 // FIXME: This does not do a subsequence match.
278 res.extend(iter.filter(|item| {
279 let item_path = &import_map.map[item].path;
280 item_path.to_string().contains(&query.query)
281 }));
282 } else {
283 res.extend(iter);
284 }
285
286 if res.len() >= query.limit {
287 res.truncate(query.limit);
288 return res;
289 }
290 }
291 }
292
293 res
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299 use crate::test_db::TestDB;
300 use insta::assert_snapshot;
301 use itertools::Itertools;
302 use ra_db::fixture::WithFixture;
303 use ra_db::{SourceDatabase, Upcast};
304
305 fn import_map(ra_fixture: &str) -> String {
306 let db = TestDB::with_files(ra_fixture);
307 let crate_graph = db.crate_graph();
308
309 let s = crate_graph
310 .iter()
311 .filter_map(|krate| {
312 let cdata = &crate_graph[krate];
313 let name = cdata.display_name.as_ref()?;
314
315 let map = db.import_map(krate);
316
317 Some(format!("{}:\n{:?}", name, map))
318 })
319 .join("\n");
320 s
321 }
322
323 fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String {
324 let db = TestDB::with_files(ra_fixture);
325 let crate_graph = db.crate_graph();
326 let krate = crate_graph
327 .iter()
328 .find(|krate| {
329 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
330 == Some(krate_name.to_string())
331 })
332 .unwrap();
333
334 search_dependencies(db.upcast(), krate, query)
335 .into_iter()
336 .filter_map(|item| {
337 let mark = match item {
338 ItemInNs::Types(_) => "t",
339 ItemInNs::Values(_) => "v",
340 ItemInNs::Macros(_) => "m",
341 };
342 item.krate(db.upcast()).map(|krate| {
343 let map = db.import_map(krate);
344 let path = map.path_of(item).unwrap();
345 format!(
346 "{}::{} ({})",
347 crate_graph[krate].display_name.as_ref().unwrap(),
348 path,
349 mark
350 )
351 })
352 })
353 .join("\n")
354 }
355
356 #[test]
357 fn smoke() {
358 let map = import_map(
359 r"
360 //- /main.rs crate:main deps:lib
361
362 mod private {
363 pub use lib::Pub;
364 pub struct InPrivateModule;
365 }
366
367 pub mod publ1 {
368 use lib::Pub;
369 }
370
371 pub mod real_pub {
372 pub use lib::Pub;
373 }
374 pub mod real_pu2 { // same path length as above
375 pub use lib::Pub;
376 }
377
378 //- /lib.rs crate:lib
379 pub struct Pub {}
380 pub struct Pub2; // t + v
381 struct Priv;
382 ",
383 );
384
385 assert_snapshot!(map, @r###"
386 main:
387 - publ1 (t)
388 - real_pu2 (t)
389 - real_pub (t)
390 - real_pub::Pub (t)
391 lib:
392 - Pub (t)
393 - Pub2 (t)
394 - Pub2 (v)
395 "###);
396 }
397
398 #[test]
399 fn prefers_shortest_path() {
400 let map = import_map(
401 r"
402 //- /main.rs crate:main
403
404 pub mod sub {
405 pub mod subsub {
406 pub struct Def {}
407 }
408
409 pub use super::sub::subsub::Def;
410 }
411 ",
412 );
413
414 assert_snapshot!(map, @r###"
415 main:
416 - sub (t)
417 - sub::Def (t)
418 - sub::subsub (t)
419 "###);
420 }
421
422 #[test]
423 fn type_reexport_cross_crate() {
424 // Reexports need to be visible from a crate, even if the original crate exports the item
425 // at a shorter path.
426 let map = import_map(
427 r"
428 //- /main.rs crate:main deps:lib
429 pub mod m {
430 pub use lib::S;
431 }
432 //- /lib.rs crate:lib
433 pub struct S;
434 ",
435 );
436
437 assert_snapshot!(map, @r###"
438 main:
439 - m (t)
440 - m::S (t)
441 - m::S (v)
442 lib:
443 - S (t)
444 - S (v)
445 "###);
446 }
447
448 #[test]
449 fn macro_reexport() {
450 let map = import_map(
451 r"
452 //- /main.rs crate:main deps:lib
453 pub mod m {
454 pub use lib::pub_macro;
455 }
456 //- /lib.rs crate:lib
457 #[macro_export]
458 macro_rules! pub_macro {
459 () => {};
460 }
461 ",
462 );
463
464 assert_snapshot!(map, @r###"
465 main:
466 - m (t)
467 - m::pub_macro (m)
468 lib:
469 - pub_macro (m)
470 "###);
471 }
472
473 #[test]
474 fn module_reexport() {
475 // Reexporting modules from a dependency adds all contents to the import map.
476 let map = import_map(
477 r"
478 //- /main.rs crate:main deps:lib
479 pub use lib::module as reexported_module;
480 //- /lib.rs crate:lib
481 pub mod module {
482 pub struct S;
483 }
484 ",
485 );
486
487 assert_snapshot!(map, @r###"
488 main:
489 - reexported_module (t)
490 - reexported_module::S (t)
491 - reexported_module::S (v)
492 lib:
493 - module (t)
494 - module::S (t)
495 - module::S (v)
496 "###);
497 }
498
499 #[test]
500 fn cyclic_module_reexport() {
501 // A cyclic reexport does not hang.
502 let map = import_map(
503 r"
504 //- /lib.rs crate:lib
505 pub mod module {
506 pub struct S;
507 pub use super::sub::*;
508 }
509
510 pub mod sub {
511 pub use super::module;
512 }
513 ",
514 );
515
516 assert_snapshot!(map, @r###"
517 lib:
518 - module (t)
519 - module::S (t)
520 - module::S (v)
521 - sub (t)
522 "###);
523 }
524
525 #[test]
526 fn private_macro() {
527 let map = import_map(
528 r"
529 //- /lib.rs crate:lib
530 macro_rules! private_macro {
531 () => {};
532 }
533 ",
534 );
535
536 assert_snapshot!(map, @r###"
537 lib:
538 "###);
539 }
540
541 #[test]
542 fn namespacing() {
543 let map = import_map(
544 r"
545 //- /lib.rs crate:lib
546 pub struct Thing; // t + v
547 #[macro_export]
548 macro_rules! Thing { // m
549 () => {};
550 }
551 ",
552 );
553
554 assert_snapshot!(map, @r###"
555 lib:
556 - Thing (m)
557 - Thing (t)
558 - Thing (v)
559 "###);
560
561 let map = import_map(
562 r"
563 //- /lib.rs crate:lib
564 pub mod Thing {} // t
565 #[macro_export]
566 macro_rules! Thing { // m
567 () => {};
568 }
569 ",
570 );
571
572 assert_snapshot!(map, @r###"
573 lib:
574 - Thing (m)
575 - Thing (t)
576 "###);
577 }
578
579 #[test]
580 fn search() {
581 let ra_fixture = r#"
582 //- /main.rs crate:main deps:dep
583 //- /dep.rs crate:dep deps:tdep
584 use tdep::fmt as fmt_dep;
585 pub mod fmt {
586 pub trait Display {
587 fn fmt();
588 }
589 }
590 #[macro_export]
591 macro_rules! Fmt {
592 () => {};
593 }
594 pub struct Fmt;
595
596 pub fn format() {}
597 pub fn no() {}
598
599 //- /tdep.rs crate:tdep
600 pub mod fmt {
601 pub struct NotImportableFromMain;
602 }
603 "#;
604
605 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt"));
606 assert_snapshot!(res, @r###"
607 dep::fmt (t)
608 dep::Fmt (t)
609 dep::Fmt (v)
610 dep::Fmt (m)
611 dep::fmt::Display (t)
612 dep::format (v)
613 "###);
614
615 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end());
616 assert_snapshot!(res, @r###"
617 dep::fmt (t)
618 dep::Fmt (t)
619 dep::Fmt (v)
620 dep::Fmt (m)
621 "###);
622 }
623
624 #[test]
625 fn search_casing() {
626 let ra_fixture = r#"
627 //- /main.rs crate:main deps:dep
628 //- /dep.rs crate:dep
629
630 pub struct fmt;
631 pub struct FMT;
632 "#;
633
634 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT"));
635
636 assert_snapshot!(res, @r###"
637 dep::fmt (t)
638 dep::fmt (v)
639 dep::FMT (t)
640 dep::FMT (v)
641 "###);
642
643 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive());
644
645 assert_snapshot!(res, @r###"
646 dep::FMT (t)
647 dep::FMT (v)
648 "###);
649 }
650
651 #[test]
652 fn search_limit() {
653 let res = search_dependencies_of(
654 r#"
655 //- /main.rs crate:main deps:dep
656 //- /dep.rs crate:dep
657 pub mod fmt {
658 pub trait Display {
659 fn fmt();
660 }
661 }
662 #[macro_export]
663 macro_rules! Fmt {
664 () => {};
665 }
666 pub struct Fmt;
667
668 pub fn format() {}
669 pub fn no() {}
670 "#,
671 "main",
672 Query::new("").limit(2),
673 );
674 assert_snapshot!(res, @r###"
675 dep::fmt (t)
676 dep::Fmt (t)
677 "###);
678 }
679}
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs
index fc15948ad..b03ba939a 100644
--- a/crates/ra_hir_def/src/item_scope.rs
+++ b/crates/ra_hir_def/src/item_scope.rs
@@ -3,11 +3,12 @@
3 3
4use hir_expand::name::Name; 4use hir_expand::name::Name;
5use once_cell::sync::Lazy; 5use once_cell::sync::Lazy;
6use ra_db::CrateId;
6use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
7 8
8use crate::{ 9use crate::{
9 per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, 10 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
10 TraitId, 11 Lookup, MacroDefId, ModuleDefId, TraitId,
11}; 12};
12 13
13#[derive(Debug, Default, PartialEq, Eq)] 14#[derive(Debug, Default, PartialEq, Eq)]
@@ -203,4 +204,22 @@ impl ItemInNs {
203 ItemInNs::Macros(_) => None, 204 ItemInNs::Macros(_) => None,
204 } 205 }
205 } 206 }
207
208 /// Returns the crate defining this item (or `None` if `self` is built-in).
209 pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
210 Some(match self {
211 ItemInNs::Types(did) | ItemInNs::Values(did) => match did {
212 ModuleDefId::ModuleId(id) => id.krate,
213 ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate,
214 ModuleDefId::AdtId(id) => id.module(db).krate,
215 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate,
216 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate,
217 ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate,
218 ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate,
219 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate,
220 ModuleDefId::BuiltinType(_) => return None,
221 },
222 ItemInNs::Macros(id) => return id.krate,
223 })
224 }
206} 225}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index 5325a2760..edc59e5a8 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -43,6 +43,7 @@ pub mod child_by_source;
43 43
44pub mod visibility; 44pub mod visibility;
45pub mod find_path; 45pub mod find_path;
46pub mod import_map;
46 47
47#[cfg(test)] 48#[cfg(test)]
48mod test_db; 49mod test_db;
@@ -416,6 +417,7 @@ pub trait AsMacroCall {
416 fn as_call_id( 417 fn as_call_id(
417 &self, 418 &self,
418 db: &dyn db::DefDatabase, 419 db: &dyn db::DefDatabase,
420 krate: CrateId,
419 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 421 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
420 ) -> Option<MacroCallId>; 422 ) -> Option<MacroCallId>;
421} 423}
@@ -424,13 +426,14 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
424 fn as_call_id( 426 fn as_call_id(
425 &self, 427 &self,
426 db: &dyn db::DefDatabase, 428 db: &dyn db::DefDatabase,
429 krate: CrateId,
427 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 430 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
428 ) -> Option<MacroCallId> { 431 ) -> Option<MacroCallId> {
429 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); 432 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
430 let h = Hygiene::new(db.upcast(), self.file_id); 433 let h = Hygiene::new(db.upcast(), self.file_id);
431 let path = path::ModPath::from_src(self.value.path()?, &h)?; 434 let path = path::ModPath::from_src(self.value.path()?, &h)?;
432 435
433 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) 436 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver)
434 } 437 }
435} 438}
436 439
@@ -451,6 +454,7 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
451 fn as_call_id( 454 fn as_call_id(
452 &self, 455 &self,
453 db: &dyn db::DefDatabase, 456 db: &dyn db::DefDatabase,
457 krate: CrateId,
454 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 458 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
455 ) -> Option<MacroCallId> { 459 ) -> Option<MacroCallId> {
456 let def: MacroDefId = resolver(self.path.clone())?; 460 let def: MacroDefId = resolver(self.path.clone())?;
@@ -460,13 +464,13 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
460 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); 464 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
461 465
462 Some( 466 Some(
463 expand_eager_macro(db.upcast(), macro_call, def, &|path: ast::Path| { 467 expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| {
464 resolver(path::ModPath::from_src(path, &hygiene)?) 468 resolver(path::ModPath::from_src(path, &hygiene)?)
465 })? 469 })?
466 .into(), 470 .into(),
467 ) 471 )
468 } else { 472 } else {
469 Some(def.as_lazy_macro(db.upcast(), MacroCallKind::FnLike(self.ast_id)).into()) 473 Some(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(self.ast_id)).into())
470 } 474 }
471 } 475 }
472} 476}
@@ -475,12 +479,14 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
475 fn as_call_id( 479 fn as_call_id(
476 &self, 480 &self,
477 db: &dyn db::DefDatabase, 481 db: &dyn db::DefDatabase,
482 krate: CrateId,
478 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 483 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
479 ) -> Option<MacroCallId> { 484 ) -> Option<MacroCallId> {
480 let def = resolver(self.path.clone())?; 485 let def = resolver(self.path.clone())?;
481 Some( 486 Some(
482 def.as_lazy_macro( 487 def.as_lazy_macro(
483 db.upcast(), 488 db.upcast(),
489 krate,
484 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), 490 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()),
485 ) 491 )
486 .into(), 492 .into(),
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 353a31ad4..976e5e585 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -571,16 +571,18 @@ impl DefCollector<'_> {
571 return false; 571 return false;
572 } 572 }
573 573
574 if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { 574 if let Some(call_id) =
575 let resolved_res = self.def_map.resolve_path_fp_with_macro( 575 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
576 self.db, 576 let resolved_res = self.def_map.resolve_path_fp_with_macro(
577 ResolveMode::Other, 577 self.db,
578 directive.module_id, 578 ResolveMode::Other,
579 &path, 579 directive.module_id,
580 BuiltinShadowMode::Module, 580 &path,
581 ); 581 BuiltinShadowMode::Module,
582 resolved_res.resolved_def.take_macros() 582 );
583 }) { 583 resolved_res.resolved_def.take_macros()
584 })
585 {
584 resolved.push((directive.module_id, call_id, directive.depth)); 586 resolved.push((directive.module_id, call_id, directive.depth));
585 res = ReachedFixedPoint::No; 587 res = ReachedFixedPoint::No;
586 return false; 588 return false;
@@ -589,9 +591,10 @@ impl DefCollector<'_> {
589 true 591 true
590 }); 592 });
591 attribute_macros.retain(|directive| { 593 attribute_macros.retain(|directive| {
592 if let Some(call_id) = directive 594 if let Some(call_id) =
593 .ast_id 595 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
594 .as_call_id(self.db, |path| self.resolve_attribute_macro(&directive, &path)) 596 self.resolve_attribute_macro(&directive, &path)
597 })
595 { 598 {
596 resolved.push((directive.module_id, call_id, 0)); 599 resolved.push((directive.module_id, call_id, 0));
597 res = ReachedFixedPoint::No; 600 res = ReachedFixedPoint::No;
@@ -957,11 +960,13 @@ impl ModCollector<'_, '_> {
957 } 960 }
958 961
959 // Case 2: try to resolve in legacy scope and expand macro_rules 962 // Case 2: try to resolve in legacy scope and expand macro_rules
960 if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| { 963 if let Some(macro_call_id) =
961 path.as_ident().and_then(|name| { 964 ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| {
962 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) 965 path.as_ident().and_then(|name| {
966 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
967 })
963 }) 968 })
964 }) { 969 {
965 self.def_collector.unexpanded_macros.push(MacroDirective { 970 self.def_collector.unexpanded_macros.push(MacroDirective {
966 module_id: self.module_id, 971 module_id: self.module_id,
967 ast_id, 972 ast_id,
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs
index 386c5cade..cede4a6fc 100644
--- a/crates/ra_hir_def/src/nameres/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs
@@ -61,7 +61,7 @@ impl ModDir {
61 }; 61 };
62 62
63 for candidate in candidate_files.iter() { 63 for candidate in candidate_files.iter() {
64 if let Some(file_id) = db.resolve_relative_path(file_id, candidate) { 64 if let Some(file_id) = db.resolve_path(file_id, candidate.as_str()) {
65 let mut root_non_dir_owner = false; 65 let mut root_non_dir_owner = false;
66 let mut mod_path = RelativePathBuf::new(); 66 let mut mod_path = RelativePathBuf::new();
67 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) { 67 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) {
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index e84efe2ab..ba16442bd 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -76,6 +76,19 @@ impl ModPath {
76 } 76 }
77 } 77 }
78 78
79 /// Returns the number of segments in the path (counting special segments like `$crate` and
80 /// `super`).
81 pub fn len(&self) -> usize {
82 self.segments.len()
83 + match self.kind {
84 PathKind::Plain => 0,
85 PathKind::Super(i) => i as usize,
86 PathKind::Crate => 1,
87 PathKind::Abs => 0,
88 PathKind::DollarCrate(_) => 1,
89 }
90 }
91
79 pub fn is_ident(&self) -> bool { 92 pub fn is_ident(&self) -> bool {
80 self.kind == PathKind::Plain && self.segments.len() == 1 93 self.kind == PathKind::Plain && self.segments.len() == 1
81 } 94 }
@@ -273,7 +286,7 @@ impl From<Name> for ModPath {
273impl Display for ModPath { 286impl Display for ModPath {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 let mut first_segment = true; 288 let mut first_segment = true;
276 let mut add_segment = |s| { 289 let mut add_segment = |s| -> fmt::Result {
277 if !first_segment { 290 if !first_segment {
278 f.write_str("::")?; 291 f.write_str("::")?;
279 } 292 }
@@ -310,16 +323,16 @@ pub use hir_expand::name as __name;
310 323
311#[macro_export] 324#[macro_export]
312macro_rules! __known_path { 325macro_rules! __known_path {
313 (std::iter::IntoIterator) => {}; 326 (core::iter::IntoIterator) => {};
314 (std::result::Result) => {}; 327 (core::result::Result) => {};
315 (std::ops::Range) => {}; 328 (core::ops::Range) => {};
316 (std::ops::RangeFrom) => {}; 329 (core::ops::RangeFrom) => {};
317 (std::ops::RangeFull) => {}; 330 (core::ops::RangeFull) => {};
318 (std::ops::RangeTo) => {}; 331 (core::ops::RangeTo) => {};
319 (std::ops::RangeToInclusive) => {}; 332 (core::ops::RangeToInclusive) => {};
320 (std::ops::RangeInclusive) => {}; 333 (core::ops::RangeInclusive) => {};
321 (std::future::Future) => {}; 334 (core::future::Future) => {};
322 (std::ops::Try) => {}; 335 (core::ops::Try) => {};
323 ($path:path) => { 336 ($path:path) => {
324 compile_error!("Please register your known path in the path module") 337 compile_error!("Please register your known path in the path module")
325 }; 338 };
diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs
index 6e435c8c1..74665c588 100644
--- a/crates/ra_hir_def/src/per_ns.rs
+++ b/crates/ra_hir_def/src/per_ns.rs
@@ -5,7 +5,7 @@
5 5
6use hir_expand::MacroDefId; 6use hir_expand::MacroDefId;
7 7
8use crate::{visibility::Visibility, ModuleDefId}; 8use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId};
9 9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)] 10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11pub struct PerNs { 11pub struct PerNs {
@@ -84,4 +84,12 @@ impl PerNs {
84 macros: self.macros.or(other.macros), 84 macros: self.macros.or(other.macros),
85 } 85 }
86 } 86 }
87
88 pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
89 self.types
90 .map(|it| ItemInNs::Types(it.0))
91 .into_iter()
92 .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter())
93 .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter())
94 }
87} 95}
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs
index eb83dee79..4581d8745 100644
--- a/crates/ra_hir_def/src/test_db.rs
+++ b/crates/ra_hir_def/src/test_db.rs
@@ -6,9 +6,8 @@ use std::{
6}; 6};
7 7
8use hir_expand::db::AstDatabase; 8use hir_expand::db::AstDatabase;
9use ra_db::{ 9use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
10 salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath, Upcast, 10use rustc_hash::FxHashSet;
11};
12 11
13use crate::db::DefDatabase; 12use crate::db::DefDatabase;
14 13
@@ -58,24 +57,12 @@ impl FileLoader for TestDB {
58 fn file_text(&self, file_id: FileId) -> Arc<String> { 57 fn file_text(&self, file_id: FileId) -> Arc<String> {
59 FileLoaderDelegate(self).file_text(file_id) 58 FileLoaderDelegate(self).file_text(file_id)
60 } 59 }
61 fn resolve_relative_path( 60 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
62 &self, 61 FileLoaderDelegate(self).resolve_path(anchor, path)
63 anchor: FileId,
64 relative_path: &RelativePath,
65 ) -> Option<FileId> {
66 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
67 } 62 }
68 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 63 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
69 FileLoaderDelegate(self).relevant_crates(file_id) 64 FileLoaderDelegate(self).relevant_crates(file_id)
70 } 65 }
71
72 fn resolve_extern_path(
73 &self,
74 extern_id: ExternSourceId,
75 relative_path: &RelativePath,
76 ) -> Option<FileId> {
77 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
78 }
79} 66}
80 67
81impl TestDB { 68impl TestDB {
diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml
index 2cd522766..e5c9f3e99 100644
--- a/crates/ra_hir_expand/Cargo.toml
+++ b/crates/ra_hir_expand/Cargo.toml
@@ -10,6 +10,7 @@ doctest = false
10[dependencies] 10[dependencies]
11log = "0.4.8" 11log = "0.4.8"
12either = "1.5.3" 12either = "1.5.3"
13rustc-hash = "1.0.0"
13 14
14ra_arena = { path = "../ra_arena" } 15ra_arena = { path = "../ra_arena" }
15ra_db = { path = "../ra_db" } 16ra_db = { path = "../ra_db" }
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index 1dc9cac66..26b667b55 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -8,8 +8,7 @@ use ra_syntax::{
8 match_ast, 8 match_ast,
9}; 9};
10 10
11use crate::db::AstDatabase; 11use crate::{db::AstDatabase, name, quote, LazyMacroId, MacroDefId, MacroDefKind};
12use crate::{name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
15 ( $($trait:ident => $expand:ident),* ) => { 14 ( $($trait:ident => $expand:ident),* ) => {
@@ -156,23 +155,13 @@ fn expand_simple_derive(
156fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree { 155fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree {
157 // FIXME: make hygiene works for builtin derive macro 156 // FIXME: make hygiene works for builtin derive macro
158 // such that $crate can be used here. 157 // such that $crate can be used here.
159
160 let m: MacroCallId = id.into();
161 let file_id = m.as_file().original_file(db);
162 let cg = db.crate_graph(); 158 let cg = db.crate_graph();
163 let krates = db.relevant_crates(file_id); 159 let krate = db.lookup_intern_macro(id).krate;
164 let krate = match krates.get(0) {
165 Some(krate) => krate,
166 None => {
167 let tt = quote! { core };
168 return tt.token_trees[0].clone();
169 }
170 };
171 160
172 // XXX 161 // XXX
173 // All crates except core itself should have a dependency on core, 162 // All crates except core itself should have a dependency on core,
174 // We detect `core` by seeing whether it doesn't have such a dependency. 163 // We detect `core` by seeing whether it doesn't have such a dependency.
175 let tt = if cg[*krate].dependencies.iter().any(|dep| dep.name == "core") { 164 let tt = if cg[krate].dependencies.iter().any(|dep| dep.name == "core") {
176 quote! { core } 165 quote! { core }
177 } else { 166 } else {
178 quote! { crate } 167 quote! { crate }
@@ -264,10 +253,12 @@ fn partial_ord_expand(
264 253
265#[cfg(test)] 254#[cfg(test)]
266mod tests { 255mod tests {
267 use super::*;
268 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
269 use name::{known, Name}; 256 use name::{known, Name};
270 use ra_db::{fixture::WithFixture, SourceDatabase}; 257 use ra_db::{fixture::WithFixture, CrateId, SourceDatabase};
258
259 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
260
261 use super::*;
271 262
272 fn expand_builtin_derive(s: &str, name: Name) -> String { 263 fn expand_builtin_derive(s: &str, name: Name) -> String {
273 let def = find_builtin_derive(&name).unwrap(); 264 let def = find_builtin_derive(&name).unwrap();
@@ -291,7 +282,11 @@ mod tests {
291 282
292 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); 283 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
293 284
294 let loc = MacroCallLoc { def, kind: MacroCallKind::Attr(attr_id, name.to_string()) }; 285 let loc = MacroCallLoc {
286 def,
287 krate: CrateId(0),
288 kind: MacroCallKind::Attr(attr_id, name.to_string()),
289 };
295 290
296 let id: MacroCallId = db.intern_macro(loc).into(); 291 let id: MacroCallId = db.intern_macro(loc).into();
297 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 292 let parsed = db.parse_or_expand(id.as_file()).unwrap();
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index 3bce8f673..b50eb347c 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -1,15 +1,14 @@
1//! Builtin macro 1//! Builtin macro
2use crate::db::AstDatabase;
3use crate::{ 2use crate::{
4 ast::{self, AstToken, HasStringValue}, 3 db::AstDatabase, name, quote, AstId, CrateId, EagerMacroId, LazyMacroId, MacroCallId,
5 name, AstId, CrateId, MacroDefId, MacroDefKind, TextSize, 4 MacroDefId, MacroDefKind, TextSize,
6}; 5};
7 6
8use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId};
9use either::Either; 7use either::Either;
10use mbe::parse_to_token_tree; 8use mbe::parse_to_token_tree;
11use ra_db::{FileId, RelativePath}; 9use ra_db::FileId;
12use ra_parser::FragmentKind; 10use ra_parser::FragmentKind;
11use ra_syntax::ast::{self, AstToken, HasStringValue};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
15 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { 14 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
@@ -295,19 +294,13 @@ fn concat_expand(
295 294
296fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> { 295fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> {
297 let call_site = call_id.as_file().original_file(db); 296 let call_site = call_id.as_file().original_file(db);
298 297 let res = db.resolve_path(call_site, path)?;
299 // Handle trivial case 298 // Prevent include itself
300 if let Some(res) = db.resolve_relative_path(call_site, &RelativePath::new(&path)) { 299 if res == call_site {
301 // Prevent include itself 300 None
302 return if res == call_site { None } else { Some(res) }; 301 } else {
302 Some(res)
303 } 303 }
304
305 // Extern paths ?
306 let krate = *db.relevant_crates(call_site).get(0)?;
307 let (extern_source_id, relative_file) =
308 db.crate_graph()[krate].extern_source.extern_path(path)?;
309
310 db.resolve_extern_path(extern_source_id, &relative_file)
311} 304}
312 305
313fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> { 306fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
@@ -339,10 +332,7 @@ fn include_expand(
339} 332}
340 333
341fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> { 334fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
342 let call_id: MacroCallId = arg_id.into(); 335 let krate = db.lookup_intern_eager_expansion(arg_id).krate;
343 let original_file = call_id.as_file().original_file(db);
344
345 let krate = *db.relevant_crates(original_file).get(0)?;
346 db.crate_graph()[krate].env.get(key) 336 db.crate_graph()[krate].env.get(key)
347} 337}
348 338
@@ -401,6 +391,7 @@ mod tests {
401 391
402 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap(); 392 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap();
403 393
394 let krate = CrateId(0);
404 let file_id = match expander { 395 let file_id = match expander {
405 Either::Left(expander) => { 396 Either::Left(expander) => {
406 // the first one should be a macro_rules 397 // the first one should be a macro_rules
@@ -413,6 +404,7 @@ mod tests {
413 404
414 let loc = MacroCallLoc { 405 let loc = MacroCallLoc {
415 def, 406 def,
407 krate,
416 kind: MacroCallKind::FnLike(AstId::new( 408 kind: MacroCallKind::FnLike(AstId::new(
417 file_id.into(), 409 file_id.into(),
418 ast_id_map.ast_id(&macro_calls[1]), 410 ast_id_map.ast_id(&macro_calls[1]),
@@ -425,7 +417,7 @@ mod tests {
425 Either::Right(expander) => { 417 Either::Right(expander) => {
426 // the first one should be a macro_rules 418 // the first one should be a macro_rules
427 let def = MacroDefId { 419 let def = MacroDefId {
428 krate: Some(CrateId(0)), 420 krate: Some(krate),
429 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))), 421 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
430 kind: MacroDefKind::BuiltInEager(expander), 422 kind: MacroDefKind::BuiltInEager(expander),
431 local_inner: false, 423 local_inner: false,
@@ -439,6 +431,7 @@ mod tests {
439 def, 431 def,
440 fragment: FragmentKind::Expr, 432 fragment: FragmentKind::Expr,
441 subtree: Arc::new(parsed_args.clone()), 433 subtree: Arc::new(parsed_args.clone()),
434 krate,
442 file_id: file_id.into(), 435 file_id: file_id.into(),
443 } 436 }
444 }); 437 });
@@ -448,6 +441,7 @@ mod tests {
448 def, 441 def,
449 fragment, 442 fragment,