aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorMikhail Rakhmanov <[email protected]>2020-06-13 07:42:15 +0100
committerMikhail Rakhmanov <[email protected]>2020-06-13 07:42:15 +0100
commit16bbf4ab7f132e6e5e5318dccdef9a5d71afdd7f (patch)
tree4b79fa8c046be56b02427ba843e70cdf3ac05767 /crates
parenteeb8b9e236796da8734ba81a49164864497f7226 (diff)
parentb56ad148db0c69eb279c225f45d324b4e80e7367 (diff)
Merge branch 'master' into keyword_completion
# Conflicts: # docs/user/generated_features.adoc
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,
450 subtree: Arc::new(subtree), 443 subtree: Arc::new(subtree),
444 krate,
451 file_id: file_id.into(), 445 file_id: file_id.into(),
452 }; 446 };
453 447
diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs
index 932f47c30..302d2b3e0 100644
--- a/crates/ra_hir_expand/src/eager.rs
+++ b/crates/ra_hir_expand/src/eager.rs
@@ -25,12 +25,14 @@ use crate::{
25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
26}; 26};
27 27
28use ra_db::CrateId;
28use ra_parser::FragmentKind; 29use ra_parser::FragmentKind;
29use ra_syntax::{algo::SyntaxRewriter, SyntaxNode}; 30use ra_syntax::{algo::SyntaxRewriter, SyntaxNode};
30use std::sync::Arc; 31use std::sync::Arc;
31 32
32pub fn expand_eager_macro( 33pub fn expand_eager_macro(
33 db: &dyn AstDatabase, 34 db: &dyn AstDatabase,
35 krate: CrateId,
34 macro_call: InFile<ast::MacroCall>, 36 macro_call: InFile<ast::MacroCall>,
35 def: MacroDefId, 37 def: MacroDefId,
36 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 38 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
@@ -47,6 +49,7 @@ pub fn expand_eager_macro(
47 def, 49 def,
48 fragment: FragmentKind::Expr, 50 fragment: FragmentKind::Expr,
49 subtree: Arc::new(parsed_args.clone()), 51 subtree: Arc::new(parsed_args.clone()),
52 krate,
50 file_id: macro_call.file_id, 53 file_id: macro_call.file_id,
51 } 54 }
52 }); 55 });
@@ -56,14 +59,20 @@ pub fn expand_eager_macro(
56 let result = eager_macro_recur( 59 let result = eager_macro_recur(
57 db, 60 db,
58 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()), 61 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
62 krate,
59 resolver, 63 resolver,
60 )?; 64 )?;
61 let subtree = to_subtree(&result)?; 65 let subtree = to_subtree(&result)?;
62 66
63 if let MacroDefKind::BuiltInEager(eager) = def.kind { 67 if let MacroDefKind::BuiltInEager(eager) = def.kind {
64 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?; 68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?;
65 let eager = 69 let eager = EagerCallLoc {
66 EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; 70 def,
71 fragment,
72 subtree: Arc::new(subtree),
73 krate,
74 file_id: macro_call.file_id,
75 };
67 76
68 Some(db.intern_eager_expansion(eager)) 77 Some(db.intern_eager_expansion(eager))
69 } else { 78 } else {
@@ -81,11 +90,12 @@ fn lazy_expand(
81 db: &dyn AstDatabase, 90 db: &dyn AstDatabase,
82 def: &MacroDefId, 91 def: &MacroDefId,
83 macro_call: InFile<ast::MacroCall>, 92 macro_call: InFile<ast::MacroCall>,
93 krate: CrateId,
84) -> Option<InFile<SyntaxNode>> { 94) -> Option<InFile<SyntaxNode>> {
85 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 95 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
86 96
87 let id: MacroCallId = 97 let id: MacroCallId =
88 def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); 98 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
89 99
90 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) 100 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
91} 101}
@@ -93,6 +103,7 @@ fn lazy_expand(
93fn eager_macro_recur( 103fn eager_macro_recur(
94 db: &dyn AstDatabase, 104 db: &dyn AstDatabase,
95 curr: InFile<SyntaxNode>, 105 curr: InFile<SyntaxNode>,
106 krate: CrateId,
96 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 107 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
97) -> Option<SyntaxNode> { 108) -> Option<SyntaxNode> {
98 let original = curr.value.clone(); 109 let original = curr.value.clone();
@@ -105,18 +116,23 @@ fn eager_macro_recur(
105 let def: MacroDefId = macro_resolver(child.path()?)?; 116 let def: MacroDefId = macro_resolver(child.path()?)?;
106 let insert = match def.kind { 117 let insert = match def.kind {
107 MacroDefKind::BuiltInEager(_) => { 118 MacroDefKind::BuiltInEager(_) => {
108 let id: MacroCallId = 119 let id: MacroCallId = expand_eager_macro(
109 expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)? 120 db,
110 .into(); 121 krate,
122 curr.with_value(child.clone()),
123 def,
124 macro_resolver,
125 )?
126 .into();
111 db.parse_or_expand(id.as_file())? 127 db.parse_or_expand(id.as_file())?
112 } 128 }
113 MacroDefKind::Declarative 129 MacroDefKind::Declarative
114 | MacroDefKind::BuiltIn(_) 130 | MacroDefKind::BuiltIn(_)
115 | MacroDefKind::BuiltInDerive(_) 131 | MacroDefKind::BuiltInDerive(_)
116 | MacroDefKind::CustomDerive(_) => { 132 | MacroDefKind::CustomDerive(_) => {
117 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; 133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?;
118 // replace macro inside 134 // replace macro inside
119 eager_macro_recur(db, expanded, macro_resolver)? 135 eager_macro_recur(db, expanded, krate, macro_resolver)?
120 } 136 }
121 }; 137 };
122 138
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index f440c073b..5eac2605b 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -209,8 +209,13 @@ pub struct MacroDefId {
209} 209}
210 210
211impl MacroDefId { 211impl MacroDefId {
212 pub fn as_lazy_macro(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> LazyMacroId { 212 pub fn as_lazy_macro(
213 db.intern_macro(MacroCallLoc { def: self, kind }) 213 self,
214 db: &dyn db::AstDatabase,
215 krate: CrateId,
216 kind: MacroCallKind,
217 ) -> LazyMacroId {
218 db.intern_macro(MacroCallLoc { def: self, krate, kind })
214 } 219 }
215} 220}
216 221
@@ -227,6 +232,7 @@ pub enum MacroDefKind {
227#[derive(Debug, Clone, PartialEq, Eq, Hash)] 232#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228pub struct MacroCallLoc { 233pub struct MacroCallLoc {
229 pub(crate) def: MacroDefId, 234 pub(crate) def: MacroDefId,
235 pub(crate) krate: CrateId,
230 pub(crate) kind: MacroCallKind, 236 pub(crate) kind: MacroCallKind,
231} 237}
232 238
@@ -274,6 +280,7 @@ pub struct EagerCallLoc {
274 pub(crate) def: MacroDefId, 280 pub(crate) def: MacroDefId,
275 pub(crate) fragment: FragmentKind, 281 pub(crate) fragment: FragmentKind,
276 pub(crate) subtree: Arc<tt::Subtree>, 282 pub(crate) subtree: Arc<tt::Subtree>,
283 pub(crate) krate: CrateId,
277 pub(crate) file_id: HirFileId, 284 pub(crate) file_id: HirFileId,
278} 285}
279 286
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index ea495cb11..660bdfe33 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -153,6 +153,7 @@ pub mod known {
153 str, 153 str,
154 // Special names 154 // Special names
155 macro_rules, 155 macro_rules,
156 doc,
156 // Components of known path (value or mod name) 157 // Components of known path (value or mod name)
157 std, 158 std,
158 core, 159 core,
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs
index c1fb762de..09fc18c36 100644
--- a/crates/ra_hir_expand/src/test_db.rs
+++ b/crates/ra_hir_expand/src/test_db.rs
@@ -5,7 +5,8 @@ use std::{
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6}; 6};
7 7
8use ra_db::{salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; 8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate};
9use rustc_hash::FxHashSet;
9 10
10#[salsa::database( 11#[salsa::database(
11 ra_db::SourceDatabaseExtStorage, 12 ra_db::SourceDatabaseExtStorage,
@@ -41,21 +42,10 @@ impl FileLoader for TestDB {
41 fn file_text(&self, file_id: FileId) -> Arc<String> { 42 fn file_text(&self, file_id: FileId) -> Arc<String> {
42 FileLoaderDelegate(self).file_text(file_id) 43 FileLoaderDelegate(self).file_text(file_id)
43 } 44 }
44 fn resolve_relative_path( 45 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
45 &self, 46 FileLoaderDelegate(self).resolve_path(anchor, path)
46 anchor: FileId,
47 relative_path: &RelativePath,
48 ) -> Option<FileId> {
49 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
50 } 47 }
51 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 48 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
52 FileLoaderDelegate(self).relevant_crates(file_id) 49 FileLoaderDelegate(self).relevant_crates(file_id)
53 } 50 }
54 fn resolve_extern_path(
55 &self,
56 anchor: ExternSourceId,
57 relative_path: &RelativePath,
58 ) -> Option<FileId> {
59 FileLoaderDelegate(self).resolve_extern_path(anchor, relative_path)
60 }
61} 51}
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 4b8dcdc07..112fcd07e 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -27,8 +27,8 @@ test_utils = { path = "../test_utils" }
27 27
28scoped-tls = "1" 28scoped-tls = "1"
29 29
30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } 30chalk-solve = "0.11"
31chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } 31chalk-ir = "0.11"
32 32
33[dev-dependencies] 33[dev-dependencies]
34insta = "0.16.0" 34insta = "0.16.0"
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index 0a8bb24ac..bf71d38d6 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -3,8 +3,8 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{ 5use hir_def::{
6 db::DefDatabase, DefWithBodyId, GenericDefId, ImplId, LocalFieldId, TraitId, TypeParamId, 6 db::DefDatabase, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, TraitId,
7 VariantId, 7 TypeParamId, VariantId,
8}; 8};
9use ra_arena::map::ArenaMap; 9use ra_arena::map::ArenaMap;
10use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; 10use ra_db::{impl_intern_key, salsa, CrateId, Upcast};
@@ -13,8 +13,8 @@ use ra_prof::profile;
13use crate::{ 13use crate::{
14 method_resolution::{CrateImplDefs, TyFingerprint}, 14 method_resolution::{CrateImplDefs, TyFingerprint},
15 traits::{chalk, AssocTyValue, Impl}, 15 traits::{chalk, AssocTyValue, Impl},
16 Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty, 16 Binders, CallableDef, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig,
17 TyDefId, TypeCtor, ValueTyDefId, 17 ReturnTypeImplTraits, Substs, TraitRef, Ty, TyDefId, TypeCtor, ValueTyDefId,
18}; 18};
19use hir_expand::name::Name; 19use hir_expand::name::Name;
20 20
@@ -48,6 +48,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
48 #[salsa::invoke(crate::callable_item_sig)] 48 #[salsa::invoke(crate::callable_item_sig)]
49 fn callable_item_signature(&self, def: CallableDef) -> PolyFnSig; 49 fn callable_item_signature(&self, def: CallableDef) -> PolyFnSig;
50 50
51 #[salsa::invoke(crate::lower::return_type_impl_traits)]
52 fn return_type_impl_traits(
53 &self,
54 def: FunctionId,
55 ) -> Option<Arc<Binders<ReturnTypeImplTraits>>>;
56
51 #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] 57 #[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
52 #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] 58 #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]
53 fn generic_predicates_for_param( 59 fn generic_predicates_for_param(
@@ -80,6 +86,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
80 #[salsa::interned] 86 #[salsa::interned]
81 fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; 87 fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId;
82 #[salsa::interned] 88 #[salsa::interned]
89 fn intern_impl_trait_id(&self, id: OpaqueTyId) -> InternedOpaqueTyId;
90 #[salsa::interned]
83 fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; 91 fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId;
84 #[salsa::interned] 92 #[salsa::interned]
85 fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId; 93 fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId;
@@ -142,3 +150,7 @@ fn hir_database_is_object_safe() {
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 150#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
143pub struct GlobalTypeParamId(salsa::InternId); 151pub struct GlobalTypeParamId(salsa::InternId);
144impl_intern_key!(GlobalTypeParamId); 152impl_intern_key!(GlobalTypeParamId);
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
155pub struct InternedOpaqueTyId(salsa::InternId);
156impl_intern_key!(InternedOpaqueTyId);
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs
index b9c4d2e89..3c97e1354 100644
--- a/crates/ra_hir_ty/src/display.rs
+++ b/crates/ra_hir_ty/src/display.rs
@@ -4,7 +4,7 @@ use std::fmt;
4 4
5use crate::{ 5use crate::{
6 db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, 6 db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate,
7 Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 7 Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
8}; 8};
9use hir_def::{ 9use hir_def::{
10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, 10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId,
@@ -359,6 +359,21 @@ impl HirDisplay for ApplicationTy {
359 write!(f, ">")?; 359 write!(f, ">")?;
360 } 360 }
361 } 361 }
362 TypeCtor::OpaqueType(opaque_ty_id) => {
363 let bounds = match opaque_ty_id {
364 OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
365 let datas =
366 f.db.return_type_impl_traits(func).expect("impl trait id without data");
367 let data = (*datas)
368 .as_ref()
369 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
370 data.clone().subst(&self.parameters)
371 }
372 };
373 write!(f, "impl ")?;
374 write_bounds_like_dyn_trait(&bounds.value, f)?;
375 // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
376 }
362 TypeCtor::Closure { .. } => { 377 TypeCtor::Closure { .. } => {
363 let sig = self.parameters[0].callable_sig(f.db); 378 let sig = self.parameters[0].callable_sig(f.db);
364 if let Some(sig) = sig { 379 if let Some(sig) = sig {
@@ -427,14 +442,24 @@ impl HirDisplay for Ty {
427 } 442 }
428 } 443 }
429 Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?, 444 Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?,
430 Ty::Dyn(predicates) | Ty::Opaque(predicates) => { 445 Ty::Dyn(predicates) => {
431 match self { 446 write!(f, "dyn ")?;
432 Ty::Dyn(_) => write!(f, "dyn ")?,
433 Ty::Opaque(_) => write!(f, "impl ")?,
434 _ => unreachable!(),
435 };
436 write_bounds_like_dyn_trait(predicates, f)?; 447 write_bounds_like_dyn_trait(predicates, f)?;
437 } 448 }
449 Ty::Opaque(opaque_ty) => {
450 let bounds = match opaque_ty.opaque_ty_id {
451 OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
452 let datas =
453 f.db.return_type_impl_traits(func).expect("impl trait id without data");
454 let data = (*datas)
455 .as_ref()
456 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
457 data.clone().subst(&opaque_ty.parameters)
458 }
459 };
460 write!(f, "impl ")?;
461 write_bounds_like_dyn_trait(&bounds.value, f)?;
462 }
438 Ty::Unknown => write!(f, "{{unknown}}")?, 463 Ty::Unknown => write!(f, "{{unknown}}")?,
439 Ty::Infer(..) => write!(f, "_")?, 464 Ty::Infer(..) => write!(f, "_")?,
440 } 465 }
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index f04968e14..7db928dde 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -226,17 +226,19 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
226 None => return, 226 None => return,
227 }; 227 };
228 228
229 let std_result_path = path![std::result::Result]; 229 let core_result_path = path![core::result::Result];
230 230
231 let resolver = self.func.resolver(db.upcast()); 231 let resolver = self.func.resolver(db.upcast());
232 let std_result_enum = match resolver.resolve_known_enum(db.upcast(), &std_result_path) { 232 let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) {
233 Some(it) => it, 233 Some(it) => it,
234 _ => return, 234 _ => return,
235 }; 235 };
236 236
237 let std_result_ctor = TypeCtor::Adt(AdtId::EnumId(std_result_enum)); 237 let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum));
238 let params = match &mismatch.expected { 238 let params = match &mismatch.expected {
239 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters, 239 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => {
240 parameters
241 }
240 _ => return, 242 _ => return,
241 }; 243 };
242 244
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index dc77e88e5..3719f76a6 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -39,8 +39,7 @@ use ra_syntax::SmolStr;
39use super::{ 39use super::{
40 primitive::{FloatTy, IntTy}, 40 primitive::{FloatTy, IntTy},
41 traits::{Guidance, Obligation, ProjectionPredicate, Solution}, 41 traits::{Guidance, Obligation, ProjectionPredicate, Solution},
42 ApplicationTy, GenericPredicate, InEnvironment, ProjectionTy, Substs, TraitEnvironment, 42 InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
43 TraitRef, Ty, TypeCtor, TypeWalk, Uncertain,
44}; 43};
45use crate::{ 44use crate::{
46 db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, 45 db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode,
@@ -312,12 +311,6 @@ impl<'a> InferenceContext<'a> {
312 fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { 311 fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
313 match ty { 312 match ty {
314 Ty::Unknown => self.table.new_type_var(), 313 Ty::Unknown => self.table.new_type_var(),
315 Ty::Apply(ApplicationTy { ctor: TypeCtor::Int(Uncertain::Unknown), .. }) => {
316 self.table.new_integer_var()
317 }
318 Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(Uncertain::Unknown), .. }) => {
319 self.table.new_float_var()
320 }
321 _ => ty, 314 _ => ty,
322 } 315 }
323 } 316 }
@@ -383,25 +376,6 @@ impl<'a> InferenceContext<'a> {
383 ) -> Ty { 376 ) -> Ty {
384 match assoc_ty { 377 match assoc_ty {
385 Some(res_assoc_ty) => { 378 Some(res_assoc_ty) => {
386 // FIXME:
387 // Check if inner_ty is is `impl Trait` and contained input TypeAlias id
388 // this is a workaround while Chalk assoc type projection doesn't always work yet,
389 // but once that is fixed I don't think we should keep this
390 // (we'll probably change how associated types are resolved anyway)
391 if let Ty::Opaque(ref predicates) = inner_ty {
392 for p in predicates.iter() {
393 if let GenericPredicate::Projection(projection) = p {
394 if projection.projection_ty.associated_ty == res_assoc_ty {
395 if let ty_app!(_, params) = &projection.ty {
396 if params.len() == 0 {
397 return projection.ty.clone();
398 }
399 }
400 }
401 }
402 }
403 }
404
405 let ty = self.table.new_type_var(); 379 let ty = self.table.new_type_var();
406 let builder = Substs::build_for_def(self.db, res_assoc_ty) 380 let builder = Substs::build_for_def(self.db, res_assoc_ty)
407 .push(inner_ty) 381 .push(inner_ty)
@@ -458,13 +432,13 @@ impl<'a> InferenceContext<'a> {
458 }; 432 };
459 return match resolution { 433 return match resolution {
460 TypeNs::AdtId(AdtId::StructId(strukt)) => { 434 TypeNs::AdtId(AdtId::StructId(strukt)) => {
461 let substs = Ty::substs_from_path(&ctx, path, strukt.into()); 435 let substs = Ty::substs_from_path(&ctx, path, strukt.into(), true);
462 let ty = self.db.ty(strukt.into()); 436 let ty = self.db.ty(strukt.into());
463 let ty = self.insert_type_vars(ty.subst(&substs)); 437 let ty = self.insert_type_vars(ty.subst(&substs));
464 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) 438 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
465 } 439 }
466 TypeNs::EnumVariantId(var) => { 440 TypeNs::EnumVariantId(var) => {
467 let substs = Ty::substs_from_path(&ctx, path, var.into()); 441 let substs = Ty::substs_from_path(&ctx, path, var.into(), true);
468 let ty = self.db.ty(var.parent.into()); 442 let ty = self.db.ty(var.parent.into());
469 let ty = self.insert_type_vars(ty.subst(&substs)); 443 let ty = self.insert_type_vars(ty.subst(&substs));
470 forbid_unresolved_segments((ty, Some(var.into())), unresolved) 444 forbid_unresolved_segments((ty, Some(var.into())), unresolved)
@@ -581,13 +555,13 @@ impl<'a> InferenceContext<'a> {
581 } 555 }
582 556
583 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> { 557 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
584 let path = path![std::iter::IntoIterator]; 558 let path = path![core::iter::IntoIterator];
585 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 559 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
586 self.db.trait_data(trait_).associated_type_by_name(&name![Item]) 560 self.db.trait_data(trait_).associated_type_by_name(&name![Item])
587 } 561 }
588 562
589 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> { 563 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
590 let path = path![std::ops::Try]; 564 let path = path![core::ops::Try];
591 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 565 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
592 self.db.trait_data(trait_).associated_type_by_name(&name![Ok]) 566 self.db.trait_data(trait_).associated_type_by_name(&name![Ok])
593 } 567 }
@@ -613,37 +587,37 @@ impl<'a> InferenceContext<'a> {
613 } 587 }
614 588
615 fn resolve_range_full(&self) -> Option<AdtId> { 589 fn resolve_range_full(&self) -> Option<AdtId> {
616 let path = path![std::ops::RangeFull]; 590 let path = path![core::ops::RangeFull];
617 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 591 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
618 Some(struct_.into()) 592 Some(struct_.into())
619 } 593 }
620 594
621 fn resolve_range(&self) -> Option<AdtId> { 595 fn resolve_range(&self) -> Option<AdtId> {
622 let path = path![std::ops::Range]; 596 let path = path![core::ops::Range];
623 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 597 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
624 Some(struct_.into()) 598 Some(struct_.into())
625 } 599 }
626 600
627 fn resolve_range_inclusive(&self) -> Option<AdtId> { 601 fn resolve_range_inclusive(&self) -> Option<AdtId> {
628 let path = path![std::ops::RangeInclusive]; 602 let path = path![core::ops::RangeInclusive];
629 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 603 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
630 Some(struct_.into()) 604 Some(struct_.into())
631 } 605 }
632 606
633 fn resolve_range_from(&self) -> Option<AdtId> { 607 fn resolve_range_from(&self) -> Option<AdtId> {
634 let path = path![std::ops::RangeFrom]; 608 let path = path![core::ops::RangeFrom];
635 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 609 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
636 Some(struct_.into()) 610 Some(struct_.into())
637 } 611 }
638 612
639 fn resolve_range_to(&self) -> Option<AdtId> { 613 fn resolve_range_to(&self) -> Option<AdtId> {
640 let path = path![std::ops::RangeTo]; 614 let path = path![core::ops::RangeTo];
641 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 615 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
642 Some(struct_.into()) 616 Some(struct_.into())
643 } 617 }
644 618
645 fn resolve_range_to_inclusive(&self) -> Option<AdtId> { 619 fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
646 let path = path![std::ops::RangeToInclusive]; 620 let path = path![core::ops::RangeToInclusive];
647 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 621 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
648 Some(struct_.into()) 622 Some(struct_.into())
649 } 623 }
@@ -683,8 +657,8 @@ impl InferTy {
683 fn fallback_value(self) -> Ty { 657 fn fallback_value(self) -> Ty {
684 match self { 658 match self {
685 InferTy::TypeVar(..) => Ty::Unknown, 659 InferTy::TypeVar(..) => Ty::Unknown,
686 InferTy::IntVar(..) => Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::i32()))), 660 InferTy::IntVar(..) => Ty::simple(TypeCtor::Int(IntTy::i32())),
687 InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(Uncertain::Known(FloatTy::f64()))), 661 InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(FloatTy::f64())),
688 InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never), 662 InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never),
689 } 663 }
690 } 664 }
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 4a98e2deb..9fd310f69 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -18,7 +18,7 @@ use crate::{
18 traits::InEnvironment, 18 traits::InEnvironment,
19 utils::{generics, variant_data, Generics}, 19 utils::{generics, variant_data, Generics},
20 ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, 20 ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs,
21 TraitRef, Ty, TypeCtor, Uncertain, 21 TraitRef, Ty, TypeCtor,
22}; 22};
23 23
24use super::{ 24use super::{
@@ -426,15 +426,7 @@ impl<'a> InferenceContext<'a> {
426 match &inner_ty { 426 match &inner_ty {
427 // Fast path for builtins 427 // Fast path for builtins
428 Ty::Apply(ApplicationTy { 428 Ty::Apply(ApplicationTy {
429 ctor: 429 ctor: TypeCtor::Int(IntTy { signedness: Signedness::Signed, .. }),
430 TypeCtor::Int(Uncertain::Known(IntTy {
431 signedness: Signedness::Signed,
432 ..
433 })),
434 ..
435 })
436 | Ty::Apply(ApplicationTy {
437 ctor: TypeCtor::Int(Uncertain::Unknown),
438 .. 430 ..
439 }) 431 })
440 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(_), .. }) 432 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(_), .. })
@@ -577,9 +569,7 @@ impl<'a> InferenceContext<'a> {
577 ); 569 );
578 self.infer_expr( 570 self.infer_expr(
579 *repeat, 571 *repeat,
580 &Expectation::has_type(Ty::simple(TypeCtor::Int(Uncertain::Known( 572 &Expectation::has_type(Ty::simple(TypeCtor::Int(IntTy::usize()))),
581 IntTy::usize(),
582 )))),
583 ); 573 );
584 } 574 }
585 } 575 }
@@ -592,13 +582,19 @@ impl<'a> InferenceContext<'a> {
592 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), Ty::simple(TypeCtor::Str)) 582 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), Ty::simple(TypeCtor::Str))
593 } 583 }
594 Literal::ByteString(..) => { 584 Literal::ByteString(..) => {
595 let byte_type = Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::u8()))); 585 let byte_type = Ty::simple(TypeCtor::Int(IntTy::u8()));
596 let array_type = Ty::apply_one(TypeCtor::Array, byte_type); 586 let array_type = Ty::apply_one(TypeCtor::Array, byte_type);
597 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type) 587 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type)
598 } 588 }
599 Literal::Char(..) => Ty::simple(TypeCtor::Char), 589 Literal::Char(..) => Ty::simple(TypeCtor::Char),
600 Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int((*ty).into())), 590 Literal::Int(_v, ty) => match ty {
601 Literal::Float(_v, ty) => Ty::simple(TypeCtor::Float((*ty).into())), 591 Some(int_ty) => Ty::simple(TypeCtor::Int((*int_ty).into())),
592 None => self.table.new_integer_var(),
593 },
594 Literal::Float(_v, ty) => match ty {
595 Some(float_ty) => Ty::simple(TypeCtor::Float((*float_ty).into())),
596 None => self.table.new_float_var(),
597 },
602 }, 598 },
603 }; 599 };
604 // use a new type variable if we got Ty::Unknown here 600 // use a new type variable if we got Ty::Unknown here
diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs
index 1c2e56fb0..1ad0d8397 100644
--- a/crates/ra_hir_ty/src/infer/path.rs
+++ b/crates/ra_hir_ty/src/infer/path.rs
@@ -95,7 +95,7 @@ impl<'a> InferenceContext<'a> {
95 // self_subst is just for the parent 95 // self_subst is just for the parent
96 let parent_substs = self_subst.unwrap_or_else(Substs::empty); 96 let parent_substs = self_subst.unwrap_or_else(Substs::empty);
97 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); 97 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
98 let substs = Ty::substs_from_path(&ctx, path, typable); 98 let substs = Ty::substs_from_path(&ctx, path, typable, true);
99 let full_substs = Substs::builder(substs.len()) 99 let full_substs = Substs::builder(substs.len())
100 .use_parent_substs(&parent_substs) 100 .use_parent_substs(&parent_substs)
101 .fill(substs.0[parent_substs.len()..].iter().cloned()) 101 .fill(substs.0[parent_substs.len()..].iter().cloned())
@@ -141,6 +141,7 @@ impl<'a> InferenceContext<'a> {
141 def, 141 def,
142 resolved_segment, 142 resolved_segment,
143 remaining_segments_for_ty, 143 remaining_segments_for_ty,
144 true,
144 ); 145 );
145 if let Ty::Unknown = ty { 146 if let Ty::Unknown = ty {
146 return None; 147 return None;
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 9fa8d3bdc..2b9372b4b 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -58,7 +58,7 @@ use ra_db::{impl_intern_key, salsa, CrateId};
58 58
59use crate::{ 59use crate::{
60 db::HirDatabase, 60 db::HirDatabase,
61 primitive::{FloatTy, IntTy, Uncertain}, 61 primitive::{FloatTy, IntTy},
62 utils::{generics, make_mut_slice, Generics}, 62 utils::{generics, make_mut_slice, Generics},
63}; 63};
64use display::HirDisplay; 64use display::HirDisplay;
@@ -87,10 +87,10 @@ pub enum TypeCtor {
87 Char, 87 Char,
88 88
89 /// A primitive integer type. For example, `i32`. 89 /// A primitive integer type. For example, `i32`.
90 Int(Uncertain<IntTy>), 90 Int(IntTy),
91 91
92 /// A primitive floating-point type. For example, `f64`. 92 /// A primitive floating-point type. For example, `f64`.
93 Float(Uncertain<FloatTy>), 93 Float(FloatTy),
94 94
95 /// Structures, enumerations and unions. 95 /// Structures, enumerations and unions.
96 Adt(AdtId), 96 Adt(AdtId),
@@ -147,6 +147,12 @@ pub enum TypeCtor {
147 /// an **application type** like `(Iterator::Item)<T>`. 147 /// an **application type** like `(Iterator::Item)<T>`.
148 AssociatedType(TypeAliasId), 148 AssociatedType(TypeAliasId),
149 149
150 /// This represents a placeholder for an opaque type in situations where we
151 /// don't know the hidden type (i.e. currently almost always). This is
152 /// analogous to the `AssociatedType` type constructor. As with that one,
153 /// these are only produced by Chalk.
154 OpaqueType(OpaqueTyId),
155
150 /// The type of a specific closure. 156 /// The type of a specific closure.
151 /// 157 ///
152 /// The closure signature is stored in a `FnPtr` type in the first type 158 /// The closure signature is stored in a `FnPtr` type in the first type
@@ -194,6 +200,14 @@ impl TypeCtor {
194 let generic_params = generics(db.upcast(), type_alias.into()); 200 let generic_params = generics(db.upcast(), type_alias.into());
195 generic_params.len() 201 generic_params.len()
196 } 202 }
203 TypeCtor::OpaqueType(opaque_ty_id) => {
204 match opaque_ty_id {
205 OpaqueTyId::ReturnTypeImplTrait(func, _) => {
206 let generic_params = generics(db.upcast(), func.into());
207 generic_params.len()
208 }
209 }
210 }
197 TypeCtor::FnPtr { num_args } => num_args as usize + 1, 211 TypeCtor::FnPtr { num_args } => num_args as usize + 1,
198 TypeCtor::Tuple { cardinality } => cardinality as usize, 212 TypeCtor::Tuple { cardinality } => cardinality as usize,
199 } 213 }
@@ -220,6 +234,11 @@ impl TypeCtor {
220 TypeCtor::AssociatedType(type_alias) => { 234 TypeCtor::AssociatedType(type_alias) => {
221 Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) 235 Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate)
222 } 236 }
237 TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id {
238 OpaqueTyId::ReturnTypeImplTrait(func, _) => {
239 Some(func.lookup(db.upcast()).module(db.upcast()).krate)
240 }
241 },
223 } 242 }
224 } 243 }
225 244
@@ -241,6 +260,7 @@ impl TypeCtor {
241 TypeCtor::Adt(adt) => Some(adt.into()), 260 TypeCtor::Adt(adt) => Some(adt.into()),
242 TypeCtor::FnDef(callable) => Some(callable.into()), 261 TypeCtor::FnDef(callable) => Some(callable.into()),
243 TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()), 262 TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()),
263 TypeCtor::OpaqueType(_impl_trait_id) => None,
244 } 264 }
245 } 265 }
246} 266}
@@ -254,6 +274,12 @@ pub struct ApplicationTy {
254 pub parameters: Substs, 274 pub parameters: Substs,
255} 275}
256 276
277#[derive(Clone, PartialEq, Eq, Debug, Hash)]
278pub struct OpaqueTy {
279 pub opaque_ty_id: OpaqueTyId,
280 pub parameters: Substs,
281}
282
257/// A "projection" type corresponds to an (unnormalized) 283/// A "projection" type corresponds to an (unnormalized)
258/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the 284/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
259/// trait and all its parameters are fully known. 285/// trait and all its parameters are fully known.
@@ -308,6 +334,12 @@ pub enum Ty {
308 /// trait and all its parameters are fully known. 334 /// trait and all its parameters are fully known.
309 Projection(ProjectionTy), 335 Projection(ProjectionTy),
310 336
337 /// An opaque type (`impl Trait`).
338 ///
339 /// This is currently only used for return type impl trait; each instance of
340 /// `impl Trait` in a return type gets its own ID.
341 Opaque(OpaqueTy),
342
311 /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T) 343 /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T)
312 /// {}` when we're type-checking the body of that function. In this 344 /// {}` when we're type-checking the body of that function. In this
313 /// situation, we know this stands for *some* type, but don't know the exact 345 /// situation, we know this stands for *some* type, but don't know the exact
@@ -332,12 +364,6 @@ pub enum Ty {
332 /// didn't seem worth the overhead yet. 364 /// didn't seem worth the overhead yet.
333 Dyn(Arc<[GenericPredicate]>), 365 Dyn(Arc<[GenericPredicate]>),
334 366
335 /// An opaque type (`impl Trait`).
336 ///
337 /// The predicates are quantified over the `Self` type; see `Ty::Dyn` for
338 /// more.
339 Opaque(Arc<[GenericPredicate]>),
340
341 /// A placeholder for a type which could not be computed; this is propagated 367 /// A placeholder for a type which could not be computed; this is propagated
342 /// to avoid useless error messages. Doubles as a placeholder where type 368 /// to avoid useless error messages. Doubles as a placeholder where type
343 /// variables are inserted before type checking, since we want to try to 369 /// variables are inserted before type checking, since we want to try to
@@ -490,7 +516,7 @@ impl Deref for Substs {
490 } 516 }
491} 517}
492 518
493#[derive(Copy, Clone, PartialEq, Eq, Debug)] 519#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
494pub struct Binders<T> { 520pub struct Binders<T> {
495 pub num_binders: usize, 521 pub num_binders: usize,
496 pub value: T, 522 pub value: T,
@@ -534,6 +560,20 @@ impl<T: TypeWalk> Binders<T> {
534 } 560 }
535} 561}
536 562
563impl<T: TypeWalk> TypeWalk for Binders<T> {
564 fn walk(&self, f: &mut impl FnMut(&Ty)) {
565 self.value.walk(f);
566 }
567
568 fn walk_mut_binders(
569 &mut self,
570 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
571 binders: DebruijnIndex,
572 ) {
573 self.value.walk_mut_binders(f, binders.shifted_in())
574 }
575}
576
537/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. 577/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait.
538/// Name to be bikeshedded: TraitBound? TraitImplements? 578/// Name to be bikeshedded: TraitBound? TraitImplements?
539#[derive(Clone, PartialEq, Eq, Debug, Hash)] 579#[derive(Clone, PartialEq, Eq, Debug, Hash)]
@@ -947,11 +987,16 @@ impl TypeWalk for Ty {
947 t.walk(f); 987 t.walk(f);
948 } 988 }
949 } 989 }
950 Ty::Dyn(predicates) | Ty::Opaque(predicates) => { 990 Ty::Dyn(predicates) => {
951 for p in predicates.iter() { 991 for p in predicates.iter() {
952 p.walk(f); 992 p.walk(f);
953 } 993 }
954 } 994 }
995 Ty::Opaque(o_ty) => {
996 for t in o_ty.parameters.iter() {
997 t.walk(f);
998 }
999 }
955 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} 1000 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
956 } 1001 }
957 f(self); 1002 f(self);
@@ -969,13 +1014,48 @@ impl TypeWalk for Ty {
969 Ty::Projection(p_ty) => { 1014 Ty::Projection(p_ty) => {
970 p_ty.parameters.walk_mut_binders(f, binders); 1015 p_ty.parameters.walk_mut_binders(f, binders);
971 } 1016 }
972 Ty::Dyn(predicates) | Ty::Opaque(predicates) => { 1017 Ty::Dyn(predicates) => {
973 for p in make_mut_slice(predicates) { 1018 for p in make_mut_slice(predicates) {
974 p.walk_mut_binders(f, binders.shifted_in()); 1019 p.walk_mut_binders(f, binders.shifted_in());
975 } 1020 }
976 } 1021 }
1022 Ty::Opaque(o_ty) => {
1023 o_ty.parameters.walk_mut_binders(f, binders);
1024 }
977 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} 1025 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
978 } 1026 }
979 f(self, binders); 1027 f(self, binders);
980 } 1028 }
981} 1029}
1030
1031impl<T: TypeWalk> TypeWalk for Vec<T> {
1032 fn walk(&self, f: &mut impl FnMut(&Ty)) {
1033 for t in self {
1034 t.walk(f);
1035 }
1036 }
1037 fn walk_mut_binders(
1038 &mut self,
1039 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
1040 binders: DebruijnIndex,
1041 ) {
1042 for t in self {
1043 t.walk_mut_binders(f, binders);
1044 }
1045 }
1046}
1047
1048#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
1049pub enum OpaqueTyId {
1050 ReturnTypeImplTrait(hir_def::FunctionId, u16),
1051}
1052
1053#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1054pub struct ReturnTypeImplTraits {
1055 pub(crate) impl_traits: Vec<ReturnTypeImplTrait>,
1056}
1057
1058#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1059pub(crate) struct ReturnTypeImplTrait {
1060 pub(crate) bounds: Binders<Vec<GenericPredicate>>,
1061}
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 35ac86a46..42713928f 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -21,8 +21,10 @@ use hir_def::{
21 HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 21 HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
22 UnionId, VariantId, 22 UnionId, VariantId,
23}; 23};
24use hir_expand::name::Name;
24use ra_arena::map::ArenaMap; 25use ra_arena::map::ArenaMap;
25use ra_db::CrateId; 26use ra_db::CrateId;
27use test_utils::mark;
26 28
27use crate::{ 29use crate::{
28 db::HirDatabase, 30 db::HirDatabase,
@@ -31,10 +33,10 @@ use crate::{
31 all_super_trait_refs, associated_type_by_name_including_super_traits, generics, 33 all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
32 make_mut_slice, variant_data, 34 make_mut_slice, variant_data,
33 }, 35 },
34 Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, 36 Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, OpaqueTy, OpaqueTyId, PolyFnSig,
35 ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, 37 ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait, ReturnTypeImplTraits, Substs,
38 TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
36}; 39};
37use hir_expand::name::Name;
38 40
39#[derive(Debug)] 41#[derive(Debug)]
40pub struct TyLoweringContext<'a> { 42pub struct TyLoweringContext<'a> {
@@ -47,7 +49,16 @@ pub struct TyLoweringContext<'a> {
47 /// possible currently, so this should be fine for now. 49 /// possible currently, so this should be fine for now.
48 pub type_param_mode: TypeParamLoweringMode, 50 pub type_param_mode: TypeParamLoweringMode,
49 pub impl_trait_mode: ImplTraitLoweringMode, 51 pub impl_trait_mode: ImplTraitLoweringMode,
50 pub impl_trait_counter: std::cell::Cell<u16>, 52 impl_trait_counter: std::cell::Cell<u16>,
53 /// When turning `impl Trait` into opaque types, we have to collect the
54 /// bounds at the same time to get the IDs correct (without becoming too
55 /// complicated). I don't like using interior mutability (as for the
56 /// counter), but I've tried and failed to make the lifetimes work for
57 /// passing around a `&mut TyLoweringContext`. The core problem is that
58 /// we're grouping the mutable data (the counter and this field) together
59 /// with the immutable context (the references to the DB and resolver).
60 /// Splitting this up would be a possible fix.
61 opaque_type_data: std::cell::RefCell<Vec<ReturnTypeImplTrait>>,
51} 62}
52 63
53impl<'a> TyLoweringContext<'a> { 64impl<'a> TyLoweringContext<'a> {
@@ -56,26 +67,42 @@ impl<'a> TyLoweringContext<'a> {
56 let impl_trait_mode = ImplTraitLoweringMode::Disallowed; 67 let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
57 let type_param_mode = TypeParamLoweringMode::Placeholder; 68 let type_param_mode = TypeParamLoweringMode::Placeholder;
58 let in_binders = DebruijnIndex::INNERMOST; 69 let in_binders = DebruijnIndex::INNERMOST;
59 Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode } 70 let opaque_type_data = std::cell::RefCell::new(Vec::new());
71 Self {
72 db,
73 resolver,
74 in_binders,
75 impl_trait_mode,
76 impl_trait_counter,
77 type_param_mode,
78 opaque_type_data,
79 }
60 } 80 }
61 81
62 pub fn with_shifted_in<T>( 82 pub fn with_debruijn<T>(
63 &self, 83 &self,
64 debruijn: DebruijnIndex, 84 debruijn: DebruijnIndex,
65 f: impl FnOnce(&TyLoweringContext) -> T, 85 f: impl FnOnce(&TyLoweringContext) -> T,
66 ) -> T { 86 ) -> T {
87 let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new());
67 let new_ctx = Self { 88 let new_ctx = Self {
68 in_binders: self.in_binders.shifted_in_from(debruijn), 89 in_binders: debruijn,
69 impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), 90 impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()),
91 opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec),
70 ..*self 92 ..*self
71 }; 93 };
72 let result = f(&new_ctx); 94 let result = f(&new_ctx);
73 self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); 95 self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
96 self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
74 result 97 result
75 } 98 }
76 99
77 pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self { 100 pub fn with_shifted_in<T>(
78 Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self } 101 &self,
102 debruijn: DebruijnIndex,
103 f: impl FnOnce(&TyLoweringContext) -> T,
104 ) -> T {
105 self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f)
79 } 106 }
80 107
81 pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { 108 pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
@@ -167,20 +194,44 @@ impl Ty {
167 TypeRef::ImplTrait(bounds) => { 194 TypeRef::ImplTrait(bounds) => {
168 match ctx.impl_trait_mode { 195 match ctx.impl_trait_mode {
169 ImplTraitLoweringMode::Opaque => { 196 ImplTraitLoweringMode::Opaque => {
170 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); 197 let idx = ctx.impl_trait_counter.get();
171 let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { 198 ctx.impl_trait_counter.set(idx + 1);
172 bounds 199
173 .iter() 200 assert!(idx as usize == ctx.opaque_type_data.borrow().len());
174 .flat_map(|b| { 201 // this dance is to make sure the data is in the right
175 GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) 202 // place even if we encounter more opaque types while
176 }) 203 // lowering the bounds
177 .collect() 204 ctx.opaque_type_data
178 }); 205 .borrow_mut()
179 Ty::Opaque(predicates) 206 .push(ReturnTypeImplTrait { bounds: Binders::new(1, Vec::new()) });
207 // We don't want to lower the bounds inside the binders
208 // we're currently in, because they don't end up inside
209 // those binders. E.g. when we have `impl Trait<impl
210 // OtherTrait<T>>`, the `impl OtherTrait<T>` can't refer
211 // to the self parameter from `impl Trait`, and the
212 // bounds aren't actually stored nested within each
213 // other, but separately. So if the `T` refers to a type
214 // parameter of the outer function, it's just one binder
215 // away instead of two.
216 let actual_opaque_type_data = ctx
217 .with_debruijn(DebruijnIndex::INNERMOST, |ctx| {
218 ReturnTypeImplTrait::from_hir(ctx, &bounds)
219 });
220 ctx.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data;
221
222 let func = match ctx.resolver.generic_def() {
223 Some(GenericDefId::FunctionId(f)) => f,
224 _ => panic!("opaque impl trait lowering in non-function"),
225 };
226 let impl_trait_id = OpaqueTyId::ReturnTypeImplTrait(func, idx);
227 let generics = generics(ctx.db.upcast(), func.into());
228 let parameters = Substs::bound_vars(&generics, ctx.in_binders);
229 Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters })
180 } 230 }
181 ImplTraitLoweringMode::Param => { 231 ImplTraitLoweringMode::Param => {
182 let idx = ctx.impl_trait_counter.get(); 232 let idx = ctx.impl_trait_counter.get();
183 ctx.impl_trait_counter.set(idx + 1); 233 // FIXME we're probably doing something wrong here
234 ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
184 if let Some(def) = ctx.resolver.generic_def() { 235 if let Some(def) = ctx.resolver.generic_def() {
185 let generics = generics(ctx.db.upcast(), def); 236 let generics = generics(ctx.db.upcast(), def);
186 let param = generics 237 let param = generics
@@ -197,7 +248,8 @@ impl Ty {
197 } 248 }
198 ImplTraitLoweringMode::Variable => { 249 ImplTraitLoweringMode::Variable => {
199 let idx = ctx.impl_trait_counter.get(); 250 let idx = ctx.impl_trait_counter.get();
200 ctx.impl_trait_counter.set(idx + 1); 251 // FIXME we're probably doing something wrong here
252 ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
201 let (parent_params, self_params, list_params, _impl_trait_params) = 253 let (parent_params, self_params, list_params, _impl_trait_params) =
202 if let Some(def) = ctx.resolver.generic_def() { 254 if let Some(def) = ctx.resolver.generic_def() {
203 let generics = generics(ctx.db.upcast(), def); 255 let generics = generics(ctx.db.upcast(), def);
@@ -271,6 +323,7 @@ impl Ty {
271 resolution: TypeNs, 323 resolution: TypeNs,
272 resolved_segment: PathSegment<'_>, 324 resolved_segment: PathSegment<'_>,
273 remaining_segments: PathSegments<'_>, 325 remaining_segments: PathSegments<'_>,
326 infer_args: bool,
274 ) -> (Ty, Option<TypeNs>) { 327 ) -> (Ty, Option<TypeNs>) {
275 let ty = match resolution { 328 let ty = match resolution {
276 TypeNs::TraitId(trait_) => { 329 TypeNs::TraitId(trait_) => {
@@ -348,9 +401,15 @@ impl Ty {
348 ctx.db.ty(adt.into()).subst(&substs) 401 ctx.db.ty(adt.into()).subst(&substs)
349 } 402 }
350 403
351 TypeNs::AdtId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 404 TypeNs::AdtId(it) => {
352 TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 405 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
353 TypeNs::TypeAliasId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 406 }
407 TypeNs::BuiltinType(it) => {
408 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
409 }
410 TypeNs::TypeAliasId(it) => {
411 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
412 }
354 // FIXME: report error 413 // FIXME: report error
355 TypeNs::EnumVariantId(_) => return (Ty::Unknown, None), 414 TypeNs::EnumVariantId(_) => return (Ty::Unknown, None),
356 }; 415 };
@@ -376,7 +435,13 @@ impl Ty {
376 ), 435 ),
377 Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)), 436 Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
378 }; 437 };
379 Ty::from_partly_resolved_hir_path(ctx, resolution, resolved_segment, remaining_segments) 438 Ty::from_partly_resolved_hir_path(
439 ctx,
440 resolution,
441 resolved_segment,
442 remaining_segments,
443 false,
444 )
380 } 445 }
381 446
382 fn select_associated_type( 447 fn select_associated_type(
@@ -422,13 +487,14 @@ impl Ty {
422 ctx: &TyLoweringContext<'_>, 487 ctx: &TyLoweringContext<'_>,
423 segment: PathSegment<'_>, 488 segment: PathSegment<'_>,
424 typable: TyDefId, 489 typable: TyDefId,
490 infer_args: bool,
425 ) -> Ty { 491 ) -> Ty {
426 let generic_def = match typable { 492 let generic_def = match typable {
427 TyDefId::BuiltinType(_) => None, 493 TyDefId::BuiltinType(_) => None,
428 TyDefId::AdtId(it) => Some(it.into()), 494 TyDefId::AdtId(it) => Some(it.into()),
429 TyDefId::TypeAliasId(it) => Some(it.into()), 495 TyDefId::TypeAliasId(it) => Some(it.into()),
430 }; 496 };
431 let substs = substs_from_path_segment(ctx, segment, generic_def, false); 497 let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args);
432 ctx.db.ty(typable).subst(&substs) 498 ctx.db.ty(typable).subst(&substs)
433 } 499 }
434 500
@@ -441,6 +507,7 @@ impl Ty {
441 // `ValueTyDefId` is just a convenient way to pass generics and 507 // `ValueTyDefId` is just a convenient way to pass generics and
442 // special-case enum variants 508 // special-case enum variants
443 resolved: ValueTyDefId, 509 resolved: ValueTyDefId,
510 infer_args: bool,
444 ) -> Substs { 511 ) -> Substs {
445 let last = path.segments().last().expect("path should have at least one segment"); 512 let last = path.segments().last().expect("path should have at least one segment");
446 let (segment, generic_def) = match resolved { 513 let (segment, generic_def) = match resolved {
@@ -463,22 +530,27 @@ impl Ty {
463 (segment, Some(var.parent.into())) 530 (segment, Some(var.parent.into()))
464 } 531 }
465 }; 532 };
466 substs_from_path_segment(ctx, segment, generic_def, false) 533 substs_from_path_segment(ctx, segment, generic_def, infer_args)
467 } 534 }
468} 535}
469 536
470pub(super) fn substs_from_path_segment( 537fn substs_from_path_segment(
471 ctx: &TyLoweringContext<'_>, 538 ctx: &TyLoweringContext<'_>,
472 segment: PathSegment<'_>, 539 segment: PathSegment<'_>,
473 def_generic: Option<GenericDefId>, 540 def_generic: Option<GenericDefId>,
474 _add_self_param: bool, 541 infer_args: bool,
475) -> Substs { 542) -> Substs {
476 let mut substs = Vec::new(); 543 let mut substs = Vec::new();
477 let def_generics = def_generic.map(|def| generics(ctx.db.upcast(), def)); 544 let def_generics = def_generic.map(|def| generics(ctx.db.upcast(), def));
478 545
479 let (parent_params, self_params, type_params, impl_trait_params) = 546 let (parent_params, self_params, type_params, impl_trait_params) =
480 def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split()); 547 def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split());
548 let total_len = parent_params + self_params + type_params + impl_trait_params;
549
481 substs.extend(iter::repeat(Ty::Unknown).take(parent_params)); 550 substs.extend(iter::repeat(Ty::Unknown).take(parent_params));
551
552 let mut had_explicit_args = false;
553
482 if let Some(generic_args) = &segment.args_and_bindings { 554 if let Some(generic_args) = &segment.args_and_bindings {
483 if !generic_args.has_self_type { 555 if !generic_args.has_self_type {
484 substs.extend(iter::repeat(Ty::Unknown).take(self_params)); 556 substs.extend(iter::repeat(Ty::Unknown).take(self_params));
@@ -490,31 +562,35 @@ pub(super) fn substs_from_path_segment(
490 for arg in generic_args.args.iter().skip(skip).take(expected_num) { 562 for arg in generic_args.args.iter().skip(skip).take(expected_num) {
491 match arg { 563 match arg {
492 GenericArg::Type(type_ref) => { 564 GenericArg::Type(type_ref) => {
565 had_explicit_args = true;
493 let ty = Ty::from_hir(ctx, type_ref); 566 let ty = Ty::from_hir(ctx, type_ref);
494 substs.push(ty); 567 substs.push(ty);
495 } 568 }
496 } 569 }
497 } 570 }
498 } 571 }
499 let total_len = parent_params + self_params + type_params + impl_trait_params;
500 // add placeholders for args that were not provided
501 for _ in substs.len()..total_len {
502 substs.push(Ty::Unknown);
503 }
504 assert_eq!(substs.len(), total_len);
505 572
506 // handle defaults 573 // handle defaults. In expression or pattern path segments without
507 if let Some(def_generic) = def_generic { 574 // explicitly specified type arguments, missing type arguments are inferred
508 let default_substs = ctx.db.generic_defaults(def_generic); 575 // (i.e. defaults aren't used).
509 assert_eq!(substs.len(), default_substs.len()); 576 if !infer_args || had_explicit_args {
577 if let Some(def_generic) = def_generic {
578 let default_substs = ctx.db.generic_defaults(def_generic);
579 assert_eq!(total_len, default_substs.len());
510 580
511 for (i, default_ty) in default_substs.iter().enumerate() { 581 for default_ty in default_substs.iter().skip(substs.len()) {
512 if substs[i] == Ty::Unknown { 582 substs.push(default_ty.clone());
513 substs[i] = default_ty.clone();
514 } 583 }
515 } 584 }
516 } 585 }
517 586
587 // add placeholders for args that were not provided
588 // FIXME: emit diagnostics in contexts where this is not allowed
589 for _ in substs.len()..total_len {
590 substs.push(Ty::Unknown);
591 }
592 assert_eq!(substs.len(), total_len);
593
518 Substs(substs.into()) 594 Substs(substs.into())
519} 595}
520 596
@@ -563,9 +639,7 @@ impl TraitRef {
563 segment: PathSegment<'_>, 639 segment: PathSegment<'_>,
564 resolved: TraitId, 640 resolved: TraitId,
565 ) -> Substs { 641 ) -> Substs {
566 let has_self_param = 642 substs_from_path_segment(ctx, segment, Some(resolved.into()), false)
567 segment.args_and_bindings.as_ref().map(|a| a.has_self_type).unwrap_or(false);
568 substs_from_path_segment(ctx, segment, Some(resolved.into()), !has_self_param)
569 } 643 }
570 644
571 pub(crate) fn from_type_bound( 645 pub(crate) fn from_type_bound(
@@ -663,6 +737,30 @@ fn assoc_type_bindings_from_type_bound<'a>(
663 }) 737 })
664} 738}
665 739
740impl ReturnTypeImplTrait {
741 fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self {
742 mark::hit!(lower_rpit);
743 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
744 let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
745 bounds
746 .iter()
747 .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone()))
748 .collect()
749 });
750 ReturnTypeImplTrait { bounds: Binders::new(1, predicates) }
751 }
752}
753
754fn count_impl_traits(type_ref: &TypeRef) -> usize {
755 let mut count = 0;
756 type_ref.walk(&mut |type_ref| {
757 if matches!(type_ref, TypeRef::ImplTrait(_)) {
758 count += 1;
759 }
760 });
761 count
762}
763
666/// Build the signature of a callable item (function, struct or enum variant). 764/// Build the signature of a callable item (function, struct or enum variant).
667pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig { 765pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig {
668 match def { 766 match def {
@@ -864,7 +962,9 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
864 .with_impl_trait_mode(ImplTraitLoweringMode::Variable) 962 .with_impl_trait_mode(ImplTraitLoweringMode::Variable)
865 .with_type_param_mode(TypeParamLoweringMode::Variable); 963 .with_type_param_mode(TypeParamLoweringMode::Variable);
866 let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::<Vec<_>>(); 964 let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::<Vec<_>>();
867 let ctx_ret = ctx_params.with_impl_trait_mode(ImplTraitLoweringMode::Opaque); 965 let ctx_ret = TyLoweringContext::new(db, &resolver)
966 .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
967 .with_type_param_mode(TypeParamLoweringMode::Variable);
868 let ret = Ty::from_hir(&ctx_ret, &data.ret_type); 968 let ret = Ty::from_hir(&ctx_ret, &data.ret_type);
869 let generics = generics(db.upcast(), def.into()); 969 let generics = generics(db.upcast(), def.into());
870 let num_binders = generics.len(); 970 let num_binders = generics.len();
@@ -1084,3 +1184,25 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
1084 TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?, 1184 TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?,
1085 )) 1185 ))
1086} 1186}
1187
1188pub(crate) fn return_type_impl_traits(
1189 db: &impl HirDatabase,
1190 def: hir_def::FunctionId,
1191) -> Option<Arc<Binders<ReturnTypeImplTraits>>> {
1192 // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
1193 let data = db.function_data(def);
1194 let resolver = def.resolver(db.upcast());
1195 let ctx_ret = TyLoweringContext::new(db, &resolver)
1196 .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
1197 .with_type_param_mode(TypeParamLoweringMode::Variable);
1198 let _ret = Ty::from_hir(&ctx_ret, &data.ret_type);
1199 let generics = generics(db.upcast(), def.into());
1200 let num_binders = generics.len();
1201 let return_type_impl_traits =
1202 ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() };
1203 if return_type_impl_traits.impl_traits.is_empty() {
1204 None
1205 } else {
1206 Some(Arc::new(Binders::new(num_binders, return_type_impl_traits)))
1207 }
1208}
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index e19628fdf..e83b39456 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -16,12 +16,8 @@ use rustc_hash::{FxHashMap, FxHashSet};
16 16
17use super::Substs; 17use super::Substs;
18use crate::{ 18use crate::{
19 autoderef, 19 autoderef, db::HirDatabase, primitive::FloatBitness, utils::all_super_traits, ApplicationTy,
20 db::HirDatabase, 20 Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
21 primitive::{FloatBitness, Uncertain},
22 utils::all_super_traits,
23 ApplicationTy, Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty,
24 TypeCtor, TypeWalk,
25}; 21};
26 22
27/// This is used as a key for indexing impls. 23/// This is used as a key for indexing impls.
@@ -147,12 +143,12 @@ impl Ty {
147 } 143 }
148 TypeCtor::Bool => lang_item_crate!("bool"), 144 TypeCtor::Bool => lang_item_crate!("bool"),
149 TypeCtor::Char => lang_item_crate!("char"), 145 TypeCtor::Char => lang_item_crate!("char"),
150 TypeCtor::Float(Uncertain::Known(f)) => match f.bitness { 146 TypeCtor::Float(f) => match f.bitness {
151 // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime) 147 // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime)
152 FloatBitness::X32 => lang_item_crate!("f32", "f32_runtime"), 148 FloatBitness::X32 => lang_item_crate!("f32", "f32_runtime"),
153 FloatBitness::X64 => lang_item_crate!("f64", "f64_runtime"), 149 FloatBitness::X64 => lang_item_crate!("f64", "f64_runtime"),
154 }, 150 },
155 TypeCtor::Int(Uncertain::Known(i)) => lang_item_crate!(i.ty_to_string()), 151 TypeCtor::Int(i) => lang_item_crate!(i.ty_to_string()),
156 TypeCtor::Str => lang_item_crate!("str_alloc", "str"), 152 TypeCtor::Str => lang_item_crate!("str_alloc", "str"),
157 TypeCtor::Slice => lang_item_crate!("slice_alloc", "slice"), 153 TypeCtor::Slice => lang_item_crate!("slice_alloc", "slice"),
158 TypeCtor::RawPtr(Mutability::Shared) => lang_item_crate!("const_ptr"), 154 TypeCtor::RawPtr(Mutability::Shared) => lang_item_crate!("const_ptr"),
diff --git a/crates/ra_hir_ty/src/primitive.rs b/crates/ra_hir_ty/src/primitive.rs
index 02a8179d9..37966b709 100644
--- a/crates/ra_hir_ty/src/primitive.rs
+++ b/crates/ra_hir_ty/src/primitive.rs
@@ -7,42 +7,6 @@ use std::fmt;
7 7
8pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, FloatBitness, IntBitness, Signedness}; 8pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, FloatBitness, IntBitness, Signedness};
9 9
10#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
11pub enum Uncertain<T> {
12 Unknown,
13 Known(T),
14}
15
16impl From<IntTy> for Uncertain<IntTy> {
17 fn from(ty: IntTy) -> Self {
18 Uncertain::Known(ty)
19 }
20}
21
22impl fmt::Display for Uncertain<IntTy> {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 match *self {
25 Uncertain::Unknown => write!(f, "{{integer}}"),
26 Uncertain::Known(ty) => write!(f, "{}", ty),
27 }
28 }
29}
30
31impl From<FloatTy> for Uncertain<FloatTy> {
32 fn from(ty: FloatTy) -> Self {
33 Uncertain::Known(ty)
34 }
35}
36
37impl fmt::Display for Uncertain<FloatTy> {
38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 match *self {
40 Uncertain::Unknown => write!(f, "{{float}}"),
41 Uncertain::Known(ty) => write!(f, "{}", ty),
42 }
43 }
44}
45
46#[derive(Copy, Clone, Eq, PartialEq, Hash)] 10#[derive(Copy, Clone, Eq, PartialEq, Hash)]
47pub struct IntTy { 11pub struct IntTy {
48 pub signedness: Signedness, 12 pub signedness: Signedness,
@@ -173,21 +137,3 @@ impl From<BuiltinFloat> for FloatTy {
173 FloatTy { bitness: t.bitness } 137 FloatTy { bitness: t.bitness }
174 } 138 }
175} 139}
176
177impl From<Option<BuiltinInt>> for Uncertain<IntTy> {
178 fn from(t: Option<BuiltinInt>) -> Self {
179 match t {
180 None => Uncertain::Unknown,
181 Some(t) => Uncertain::Known(t.into()),
182 }
183 }
184}
185
186impl From<Option<BuiltinFloat>> for Uncertain<FloatTy> {
187 fn from(t: Option<BuiltinFloat>) -> Self {
188 match t {
189 None => Uncertain::Unknown,
190 Some(t) => Uncertain::Known(t.into()),
191 }
192 }
193}
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index 8498d3d96..ad04e3e0f 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -7,9 +7,8 @@ use std::{
7 7
8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; 8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId};
9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; 9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink};
10use ra_db::{ 10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast};
11 salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase, Upcast, 11use rustc_hash::FxHashSet;
12};
13use stdx::format_to; 12use stdx::format_to;
14 13
15use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; 14use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator};
@@ -72,23 +71,12 @@ impl FileLoader for TestDB {
72 fn file_text(&self, file_id: FileId) -> Arc<String> { 71 fn file_text(&self, file_id: FileId) -> Arc<String> {
73 FileLoaderDelegate(self).file_text(file_id) 72 FileLoaderDelegate(self).file_text(file_id)
74 } 73 }
75 fn resolve_relative_path( 74 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
76 &self, 75 FileLoaderDelegate(self).resolve_path(anchor, path)
77 anchor: FileId,
78 relative_path: &RelativePath,
79 ) -> Option<FileId> {
80 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
81 } 76 }
82 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 77 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
83 FileLoaderDelegate(self).relevant_crates(file_id) 78 FileLoaderDelegate(self).relevant_crates(file_id)
84 } 79 }
85 fn resolve_extern_path(
86 &self,
87 extern_id: ra_db::ExternSourceId,
88 relative_path: &RelativePath,
89 ) -> Option<FileId> {
90 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
91 }
92} 80}
93 81
94impl TestDB { 82impl TestDB {
diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs
index 4088b1d22..5dfa0a014 100644
--- a/crates/ra_hir_ty/src/tests/display_source_code.rs
+++ b/crates/ra_hir_ty/src/tests/display_source_code.rs
@@ -29,7 +29,7 @@ fn omit_default_type_parameters() {
29 //- /main.rs 29 //- /main.rs
30 struct Foo<T = u8> { t: T } 30 struct Foo<T = u8> { t: T }
31 fn main() { 31 fn main() {
32 let foo = Foo { t: 5 }; 32 let foo = Foo { t: 5u8 };
33 foo<|>; 33 foo<|>;
34 } 34 }
35 ", 35 ",
@@ -41,7 +41,7 @@ fn omit_default_type_parameters() {
41 //- /main.rs 41 //- /main.rs
42 struct Foo<K, T = u8> { k: K, t: T } 42 struct Foo<K, T = u8> { k: K, t: T }
43 fn main() { 43 fn main() {
44 let foo = Foo { k: 400, t: 5 }; 44 let foo = Foo { k: 400, t: 5u8 };
45 foo<|>; 45 foo<|>;
46 } 46 }
47 ", 47 ",
diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs
index 558a70022..804297315 100644
--- a/crates/ra_hir_ty/src/tests/method_resolution.rs
+++ b/crates/ra_hir_ty/src/tests/method_resolution.rs
@@ -184,60 +184,6 @@ fn test() {
184} 184}
185 185
186#[test] 186#[test]
187fn infer_associated_method_generics_with_default_param() {
188 assert_snapshot!(
189 infer(r#"
190struct Gen<T=u32> {
191 val: T
192}
193
194impl<T> Gen<T> {
195 pub fn make() -> Gen<T> {
196 loop { }
197 }
198}
199
200fn test() {
201 let a = Gen::make();
202}
203"#),
204 @r###"
205 80..104 '{ ... }': Gen<T>
206 90..98 'loop { }': !
207 95..98 '{ }': ()
208 118..146 '{ ...e(); }': ()
209 128..129 'a': Gen<u32>
210 132..141 'Gen::make': fn make<u32>() -> Gen<u32>
211 132..143 'Gen::make()': Gen<u32>
212 "###
213 );
214}
215
216#[test]
217fn infer_associated_method_generics_with_default_tuple_param() {
218 let t = type_at(
219 r#"
220//- /main.rs
221struct Gen<T=()> {
222 val: T
223}
224
225impl<T> Gen<T> {
226 pub fn make() -> Gen<T> {
227 loop { }
228 }
229}
230
231fn test() {
232 let a = Gen::make();
233 a.val<|>;
234}
235"#,
236 );
237 assert_eq!(t, "()");
238}
239
240#[test]
241fn infer_associated_method_generics_without_args() { 187fn infer_associated_method_generics_without_args() {
242 assert_snapshot!( 188 assert_snapshot!(
243 infer(r#" 189 infer(r#"
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 88309157b..37659cd02 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -95,7 +95,7 @@ fn foo() {
95fn infer_ranges() { 95fn infer_ranges() {
96 let (db, pos) = TestDB::with_position( 96 let (db, pos) = TestDB::with_position(
97 r#" 97 r#"
98//- /main.rs crate:main deps:std 98//- /main.rs crate:main deps:core
99fn test() { 99fn test() {
100 let a = ..; 100 let a = ..;
101 let b = 1..; 101 let b = 1..;
@@ -108,7 +108,7 @@ fn test() {
108 t<|>; 108 t<|>;
109} 109}
110 110
111//- /std.rs crate:std 111//- /core.rs crate:core
112#[prelude_import] use prelude::*; 112#[prelude_import] use prelude::*;
113mod prelude {} 113mod prelude {}
114 114
@@ -1997,3 +1997,111 @@ fn foo() {
1997 "### 1997 "###
1998 ); 1998 );
1999} 1999}
2000
2001#[test]
2002fn generic_default() {
2003 assert_snapshot!(
2004 infer(r#"
2005struct Thing<T = ()> { t: T }
2006enum OtherThing<T = ()> {
2007 One { t: T },
2008 Two(T),
2009}
2010
2011fn test(t1: Thing, t2: OtherThing, t3: Thing<i32>, t4: OtherThing<i32>) {
2012 t1.t;
2013 t3.t;
2014 match t2 {
2015 OtherThing::One { t } => { t; },
2016 OtherThing::Two(t) => { t; },
2017 }
2018 match t4 {
2019 OtherThing::One { t } => { t; },
2020 OtherThing::Two(t) => { t; },
2021 }
2022}
2023"#),
2024 @r###"
2025 98..100 't1': Thing<()>
2026 109..111 't2': OtherThing<()>
2027 125..127 't3': Thing<i32>
2028 141..143 't4': OtherThing<i32>
2029 162..385 '{ ... } }': ()
2030 168..170 't1': Thing<()>
2031 168..172 't1.t': ()
2032 178..180 't3': Thing<i32>
2033 178..182 't3.t': i32
2034 188..283 'match ... }': ()
2035 194..196 't2': OtherThing<()>
2036 207..228 'OtherT... { t }': OtherThing<()>
2037 225..226 't': ()
2038 232..238 '{ t; }': ()
2039 234..235 't': ()
2040 248..266 'OtherT...Two(t)': OtherThing<()>
2041 264..265 't': ()
2042 270..276 '{ t; }': ()
2043 272..273 't': ()
2044 288..383 'match ... }': ()
2045 294..296 't4': OtherThing<i32>
2046 307..328 'OtherT... { t }': OtherThing<i32>
2047 325..326 't': i32
2048 332..338 '{ t; }': ()
2049 334..335 't': i32
2050 348..366 'OtherT...Two(t)': OtherThing<i32>
2051 364..365 't': i32
2052 370..376 '{ t; }': ()
2053 372..373 't': i32
2054 "###
2055 );
2056}
2057
2058#[test]
2059fn generic_default_in_struct_literal() {
2060 assert_snapshot!(
2061 infer(r#"
2062struct Thing<T = ()> { t: T }
2063enum OtherThing<T = ()> {
2064 One { t: T },
2065 Two(T),
2066}
2067
2068fn test() {
2069 let x = Thing { t: loop {} };
2070 let y = Thing { t: () };
2071 let z = Thing { t: 1i32 };
2072 if let Thing { t } = z {
2073 t;
2074 }
2075
2076 let a = OtherThing::One { t: 1i32 };
2077 let b = OtherThing::Two(1i32);
2078}
2079"#),
2080 @r###"
2081 100..320 '{ ...32); }': ()
2082 110..111 'x': Thing<!>
2083 114..134 'Thing ...p {} }': Thing<!>
2084 125..132 'loop {}': !
2085 130..132 '{}': ()
2086 144..145 'y': Thing<()>
2087 148..163 'Thing { t: () }': Thing<()>
2088 159..161 '()': ()
2089 173..174 'z': Thing<i32>
2090 177..194 'Thing ...1i32 }': Thing<i32>
2091 188..192 '1i32': i32
2092 200..241 'if let... }': ()
2093 207..218 'Thing { t }': Thing<i32>
2094 215..216 't': i32
2095 221..222 'z': Thing<i32>
2096 223..241 '{ ... }': ()
2097 233..234 't': i32
2098 251..252 'a': OtherThing<i32>
2099 255..282 'OtherT...1i32 }': OtherThing<i32>
2100 276..280 '1i32': i32
2101 292..293 'b': OtherThing<i32>
2102 296..311 'OtherThing::Two': Two<i32>(i32) -> OtherThing<i32>
2103 296..317 'OtherT...(1i32)': OtherThing<i32>
2104 312..316 '1i32': i32
2105 "###
2106 );
2107}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index e8778d419..e81193a3c 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -10,7 +10,7 @@ use super::{infer, infer_with_mismatches, type_at, type_at_pos};
10fn infer_await() { 10fn infer_await() {
11 let (db, pos) = TestDB::with_position( 11 let (db, pos) = TestDB::with_position(
12 r#" 12 r#"
13//- /main.rs crate:main deps:std 13//- /main.rs crate:main deps:core
14 14
15struct IntFuture; 15struct IntFuture;
16 16
@@ -24,7 +24,7 @@ fn test() {
24 v<|>; 24 v<|>;
25} 25}
26 26
27//- /std.rs crate:std 27//- /core.rs crate:core
28#[prelude_import] use future::*; 28#[prelude_import] use future::*;
29mod future { 29mod future {
30 #[lang = "future_trait"] 30 #[lang = "future_trait"]
@@ -42,7 +42,7 @@ mod future {
42fn infer_async() { 42fn infer_async() {
43 let (db, pos) = TestDB::with_position( 43 let (db, pos) = TestDB::with_position(
44 r#" 44 r#"
45//- /main.rs crate:main deps:std 45//- /main.rs crate:main deps:core
46 46
47async fn foo() -> u64 { 47async fn foo() -> u64 {
48 128 48 128
@@ -54,7 +54,7 @@ fn test() {
54 v<|>; 54 v<|>;
55} 55}
56 56
57//- /std.rs crate:std 57//- /core.rs crate:core
58#[prelude_import] use future::*; 58#[prelude_import] use future::*;
59mod future { 59mod future {
60 #[lang = "future_trait"] 60 #[lang = "future_trait"]
@@ -72,7 +72,7 @@ mod future {
72fn infer_desugar_async() { 72fn infer_desugar_async() {
73 let (db, pos) = TestDB::with_position( 73 let (db, pos) = TestDB::with_position(
74 r#" 74 r#"
75//- /main.rs crate:main deps:std 75//- /main.rs crate:main deps:core
76 76
77async fn foo() -> u64 { 77async fn foo() -> u64 {
78 128 78 128
@@ -83,7 +83,7 @@ fn test() {
83 r<|>; 83 r<|>;
84} 84}
85 85
86//- /std.rs crate:std 86//- /core.rs crate:core
87#[prelude_import] use future::*; 87#[prelude_import] use future::*;
88mod future { 88mod future {
89 trait Future { 89 trait Future {
@@ -100,7 +100,7 @@ mod future {
100fn infer_try() { 100fn infer_try() {
101 let (db, pos) = TestDB::with_position( 101 let (db, pos) = TestDB::with_position(
102 r#" 102 r#"
103//- /main.rs crate:main deps:std 103//- /main.rs crate:main deps:core
104 104
105fn test() { 105fn test() {
106 let r: Result<i32, u64> = Result::Ok(1); 106 let r: Result<i32, u64> = Result::Ok(1);
@@ -108,7 +108,7 @@ fn test() {
108 v<|>; 108 v<|>;
109} 109}
110 110
111//- /std.rs crate:std 111//- /core.rs crate:core
112 112
113#[prelude_import] use ops::*; 113#[prelude_import] use ops::*;
114mod ops { 114mod ops {
@@ -140,9 +140,9 @@ mod result {
140fn infer_for_loop() { 140fn infer_for_loop() {
141 let (db, pos) = TestDB::with_position( 141 let (db, pos) = TestDB::with_position(
142 r#" 142 r#"
143//- /main.rs crate:main deps:std 143//- /main.rs crate:main deps:core,alloc
144 144
145use std::collections::Vec; 145use alloc::collections::Vec;
146 146
147fn test() { 147fn test() {
148 let v = Vec::new(); 148 let v = Vec::new();
@@ -152,7 +152,7 @@ fn test() {
152 } 152 }
153} 153}
154 154
155//- /std.rs crate:std 155//- /core.rs crate:core
156 156
157#[prelude_import] use iter::*; 157#[prelude_import] use iter::*;
158mod iter { 158mod iter {
@@ -161,6 +161,8 @@ mod iter {
161 } 161 }
162} 162}
163 163
164//- /alloc.rs crate:alloc deps:core
165
164mod collections { 166mod collections {
165 struct Vec<T> {} 167 struct Vec<T> {}
166 impl<T> Vec<T> { 168 impl<T> Vec<T> {
@@ -168,7 +170,7 @@ mod collections {
168 fn push(&mut self, t: T) { } 170 fn push(&mut self, t: T) { }
169 } 171 }
170 172
171 impl<T> crate::iter::IntoIterator for Vec<T> { 173 impl<T> IntoIterator for Vec<T> {
172 type Item=T; 174 type Item=T;
173 } 175 }
174} 176}
@@ -1110,7 +1112,6 @@ fn test() {
1110} 1112}
1111 1113
1112#[test] 1114#[test]
1113#[ignore]
1114fn impl_trait() { 1115fn impl_trait() {
1115 assert_snapshot!( 1116 assert_snapshot!(
1116 infer(r#" 1117 infer(r#"
@@ -1161,6 +1162,95 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u64>) {
1161} 1162}
1162 1163
1163#[test] 1164#[test]
1165fn simple_return_pos_impl_trait() {
1166 mark::check!(lower_rpit);
1167 assert_snapshot!(
1168 infer(r#"
1169trait Trait<T> {
1170 fn foo(&self) -> T;
1171}
1172fn bar() -> impl Trait<u64> { loop {} }
1173
1174fn test() {
1175 let a = bar();
1176 a.foo();
1177}
1178"#),
1179 @r###"
1180 30..34 'self': &Self
1181 72..83 '{ loop {} }': !
1182 74..81 'loop {}': !
1183 79..81 '{}': ()
1184 95..130 '{ ...o(); }': ()
1185 105..106 'a': impl Trait<u64>
1186 109..112 'bar': fn bar() -> impl Trait<u64>
1187 109..114 'bar()': impl Trait<u64>
1188 120..121 'a': impl Trait<u64>
1189 120..127 'a.foo()': u64
1190 "###
1191 );
1192}
1193
1194#[test]
1195fn more_return_pos_impl_trait() {
1196 assert_snapshot!(
1197 infer(r#"
1198trait Iterator {
1199 type Item;
1200 fn next(&mut self) -> Self::Item;
1201}
1202trait Trait<T> {
1203 fn foo(&self) -> T;
1204}
1205fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) { loop {} }
1206fn baz<T>(t: T) -> (impl Iterator<Item = impl Trait<T>>, impl Trait<T>) { loop {} }
1207
1208fn test() {
1209 let (a, b) = bar();
1210 a.next().foo();
1211 b.foo();
1212 let (c, d) = baz(1u128);
1213 c.next().foo();
1214 d.foo();
1215}
1216"#),
1217 @r###"
1218 50..54 'self': &mut Self
1219 102..106 'self': &Self
1220 185..196 '{ loop {} }': ({unknown}, {unknown})
1221 187..194 'loop {}': !
1222 192..194 '{}': ()
1223 207..208 't': T
1224 269..280 '{ loop {} }': ({unknown}, {unknown})
1225 271..278 'loop {}': !
1226 276..278 '{}': ()
1227 292..414 '{ ...o(); }': ()
1228 302..308 '(a, b)': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
1229 303..304 'a': impl Iterator<Item = impl Trait<u32>>
1230 306..307 'b': impl Trait<u64>
1231 311..314 'bar': fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
1232 311..316 'bar()': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
1233 322..323 'a': impl Iterator<Item = impl Trait<u32>>
1234 322..330 'a.next()': impl Trait<u32>
1235 322..336 'a.next().foo()': u32
1236 342..343 'b': impl Trait<u64>
1237 342..349 'b.foo()': u64
1238 359..365 '(c, d)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
1239 360..361 'c': impl Iterator<Item = impl Trait<u128>>
1240 363..364 'd': impl Trait<u128>
1241 368..371 'baz': fn baz<u128>(u128) -> (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
1242 368..378 'baz(1u128)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
1243 372..377 '1u128': u128
1244 384..385 'c': impl Iterator<Item = impl Trait<u128>>
1245 384..392 'c.next()': impl Trait<u128>
1246 384..398 'c.next().foo()': u128
1247 404..405 'd': impl Trait<u128>
1248 404..411 'd.foo()': u128
1249 "###
1250 );
1251}
1252
1253#[test]
1164fn dyn_trait() { 1254fn dyn_trait() {
1165 assert_snapshot!( 1255 assert_snapshot!(
1166 infer(r#" 1256 infer(r#"
@@ -1718,33 +1808,33 @@ fn test() {
1718} 1808}
1719"#), 1809"#),
1720 @r###" 1810 @r###"
172165..69 'self': &Self 1811 65..69 'self': &Self
1722166..170 'self': Self 1812 166..170 'self': Self
1723172..176 'args': Args 1813 172..176 'args': Args
1724240..244 'self': &Foo 1814 240..244 'self': &Foo
1725255..257 '{}': () 1815 255..257 '{}': ()
1726335..336 'f': F 1816 335..336 'f': F
1727355..357 '{}': () 1817 355..357 '{}': ()
1728444..690 '{ ...o(); }': () 1818 444..690 '{ ...o(); }': ()
1729454..459 'lazy1': Lazy<Foo, fn() -> T> 1819 454..459 'lazy1': Lazy<Foo, || -> Foo>
1730476..485 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> 1820 476..485 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
1731476..493 'Lazy::...| Foo)': Lazy<Foo, fn() -> T> 1821 476..493 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
1732486..492 '|| Foo': || -> T 1822 486..492 '|| Foo': || -> Foo
1733489..492 'Foo': Foo 1823 489..492 'Foo': Foo
1734503..505 'r1': {unknown} 1824 503..505 'r1': usize
1735508..513 'lazy1': Lazy<Foo, fn() -> T> 1825 508..513 'lazy1': Lazy<Foo, || -> Foo>
1736508..519 'lazy1.foo()': {unknown} 1826 508..519 'lazy1.foo()': usize
1737561..576 'make_foo_fn_ptr': fn() -> Foo 1827 561..576 'make_foo_fn_ptr': fn() -> Foo
1738592..603 'make_foo_fn': fn make_foo_fn() -> Foo 1828 592..603 'make_foo_fn': fn make_foo_fn() -> Foo
1739613..618 'lazy2': Lazy<Foo, fn() -> T> 1829 613..618 'lazy2': Lazy<Foo, fn() -> Foo>
1740635..644 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> 1830 635..644 'Lazy::new': fn new<Foo, fn() -> Foo>(fn() -> Foo) -> Lazy<Foo, fn() -> Foo>
1741635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> T> 1831 635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> Foo>
1742645..660 'make_foo_fn_ptr': fn() -> Foo 1832 645..660 'make_foo_fn_ptr': fn() -> Foo
1743671..673 'r2': {unknown} 1833 671..673 'r2': {unknown}
1744676..681 'lazy2': Lazy<Foo, fn() -> T> 1834 676..681 'lazy2': Lazy<Foo, fn() -> Foo>
1745676..687 'lazy2.foo()': {unknown} 1835 676..687 'lazy2.foo()': {unknown}
1746550..552 '{}': () 1836 550..552 '{}': ()
1747"### 1837 "###
1748 ); 1838 );
1749} 1839}
1750 1840
@@ -2758,12 +2848,12 @@ fn test() {
2758fn integer_range_iterate() { 2848fn integer_range_iterate() {
2759 let t = type_at( 2849 let t = type_at(
2760 r#" 2850 r#"
2761//- /main.rs crate:main deps:std 2851//- /main.rs crate:main deps:core
2762fn test() { 2852fn test() {
2763 for x in 0..100 { x<|>; } 2853 for x in 0..100 { x<|>; }
2764} 2854}
2765 2855
2766//- /std.rs crate:std 2856//- /core.rs crate:core
2767pub mod ops { 2857pub mod ops {
2768 pub struct Range<Idx> { 2858 pub struct Range<Idx> {
2769 pub start: Idx, 2859 pub start: Idx,
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index 61de3cc30..a72a82f5a 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -4,7 +4,7 @@ use std::sync::Arc;
4use log::debug; 4use log::debug;
5 5
6use chalk_ir::{fold::shift::Shift, GenericArg, TypeName}; 6use chalk_ir::{fold::shift::Shift, GenericArg, TypeName};
7use chalk_solve::rust_ir::{self, WellKnownTrait}; 7use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
8 8
9use hir_def::{ 9use hir_def::{
10 lang_item::{lang_attr, LangItemTarget}, 10 lang_item::{lang_attr, LangItemTarget},
@@ -100,6 +100,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
100 fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { 100 fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> {
101 self.db.associated_ty_value(self.krate, id) 101 self.db.associated_ty_value(self.krate, id)
102 } 102 }
103
103 fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> { 104 fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> {
104 vec![] 105 vec![]
105 } 106 }
@@ -130,11 +131,34 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
130 self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) 131 self.db.program_clauses_for_chalk_env(self.krate, environment.clone())
131 } 132 }
132 133
133 fn opaque_ty_data( 134 fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId<Interner>) -> Arc<OpaqueTyDatum> {
134 &self, 135 let interned_id = crate::db::InternedOpaqueTyId::from(id);
135 _id: chalk_ir::OpaqueTyId<Interner>, 136 let full_id = self.db.lookup_intern_impl_trait_id(interned_id);
136 ) -> Arc<rust_ir::OpaqueTyDatum<Interner>> { 137 let (func, idx) = match full_id {
137 unimplemented!() 138 crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => (func, idx),
139 };
140 let datas =
141 self.db.return_type_impl_traits(func).expect("impl trait id without impl traits");
142 let data = &datas.value.impl_traits[idx as usize];
143 let bound = OpaqueTyDatumBound {
144 bounds: make_binders(
145 data.bounds
146 .value
147 .iter()
148 .cloned()
149 .filter(|b| !b.is_error())
150 .map(|b| b.to_chalk(self.db))
151 .collect(),
152 1,
153 ),
154 };
155 let num_vars = datas.num_binders;
156 Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound: make_binders(bound, num_vars) })
157 }
158
159 fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> {
160 // FIXME: actually provide the hidden type; it is relevant for auto traits
161 Ty::Unknown.to_chalk(self.db)
138 } 162 }
139 163
140 fn force_impl_for( 164 fn force_impl_for(
@@ -150,10 +174,6 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
150 // FIXME: implement actual object safety 174 // FIXME: implement actual object safety
151 true 175 true
152 } 176 }
153
154 fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> {
155 Ty::Unknown.to_chalk(self.db)
156 }
157} 177}
158 178
159pub(crate) fn program_clauses_for_chalk_env_query( 179pub(crate) fn program_clauses_for_chalk_env_query(
@@ -460,6 +480,18 @@ impl From<crate::traits::GlobalImplId> for ImplId {
460 } 480 }
461} 481}
462 482
483impl From<OpaqueTyId> for crate::db::InternedOpaqueTyId {
484 fn from(id: OpaqueTyId) -> Self {
485 InternKey::from_intern_id(id.0)
486 }
487}
488
489impl From<crate::db::InternedOpaqueTyId> for OpaqueTyId {
490 fn from(id: crate::db::InternedOpaqueTyId) -> Self {
491 chalk_ir::OpaqueTyId(id.as_intern_id())
492 }
493}
494
463impl From<rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId { 495impl From<rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId {
464 fn from(id: rust_ir::AssociatedTyValueId<Interner>) -> Self { 496 fn from(id: rust_ir::AssociatedTyValueId<Interner>) -> Self {
465 Self::from_intern_id(id.0) 497 Self::from_intern_id(id.0)
diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs
index e27074ba6..56aab640c 100644
--- a/crates/ra_hir_ty/src/traits/chalk/interner.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs
@@ -22,6 +22,8 @@ pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interne
22pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>; 22pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>;
23pub type FnDefId = chalk_ir::FnDefId<Interner>; 23pub type FnDefId = chalk_ir::FnDefId<Interner>;
24pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; 24pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>;
25pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>;
26pub type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>;
25 27
26impl chalk_ir::interner::Interner for Interner { 28impl chalk_ir::interner::Interner for Interner {
27 type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc? 29 type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc?
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
index 5f6daf842..18e5c9c16 100644
--- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -14,10 +14,10 @@ use ra_db::salsa::InternKey;
14 14
15use crate::{ 15use crate::{
16 db::HirDatabase, 16 db::HirDatabase,
17 primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain}, 17 primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness},
18 traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, 18 traits::{builtin, AssocTyValue, Canonical, Impl, Obligation},
19 ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy, 19 ApplicationTy, CallableDef, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId,
20 Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, 20 ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
21}; 21};
22 22
23use super::interner::*; 23use super::interner::*;
@@ -68,7 +68,16 @@ impl ToChalk for Ty {
68 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) }; 68 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) };
69 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) 69 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner)
70 } 70 }
71 Ty::Opaque(_) | Ty::Unknown => { 71 Ty::Opaque(opaque_ty) => {
72 let opaque_ty_id = opaque_ty.opaque_ty_id.to_chalk(db);
73 let substitution = opaque_ty.parameters.to_chalk(db);
74 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy {
75 opaque_ty_id,
76 substitution,
77 }))
78 .intern(&Interner)
79 }
80 Ty::Unknown => {
72 let substitution = chalk_ir::Substitution::empty(&Interner); 81 let substitution = chalk_ir::Substitution::empty(&Interner);
73 let name = TypeName::Error; 82 let name = TypeName::Error;
74 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) 83 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
@@ -98,7 +107,11 @@ impl ToChalk for Ty {
98 let parameters = from_chalk(db, proj.substitution); 107 let parameters = from_chalk(db, proj.substitution);
99 Ty::Projection(ProjectionTy { associated_ty, parameters }) 108 Ty::Projection(ProjectionTy { associated_ty, parameters })
100 } 109 }
101 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(), 110 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => {
111 let impl_trait_id = from_chalk(db, opaque_ty.opaque_ty_id);
112 let parameters = from_chalk(db, opaque_ty.substitution);
113 Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters })
114 }
102 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => { 115 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => {
103 let parameters: Substs = from_chalk(db, substitution); 116 let parameters: Substs = from_chalk(db, substitution);
104 Ty::Apply(ApplicationTy { 117 Ty::Apply(ApplicationTy {
@@ -204,6 +217,21 @@ impl ToChalk for hir_def::TraitId {
204 } 217 }
205} 218}
206 219
220impl ToChalk for OpaqueTyId {
221 type Chalk = chalk_ir::OpaqueTyId<Interner>;
222
223 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::OpaqueTyId<Interner> {
224 db.intern_impl_trait_id(self).into()
225 }
226
227 fn from_chalk(
228 db: &dyn HirDatabase,
229 opaque_ty_id: chalk_ir::OpaqueTyId<Interner>,
230 ) -> OpaqueTyId {
231 db.lookup_intern_impl_trait_id(opaque_ty_id.into())
232 }
233}
234
207impl ToChalk for TypeCtor { 235impl ToChalk for TypeCtor {
208 type Chalk = TypeName<Interner>; 236 type Chalk = TypeName<Interner>;
209 237
@@ -214,13 +242,18 @@ impl ToChalk for TypeCtor {
214 TypeName::AssociatedType(type_id) 242 TypeName::AssociatedType(type_id)
215 } 243 }
216 244
245 TypeCtor::OpaqueType(impl_trait_id) => {
246 let id = impl_trait_id.to_chalk(db);
247 TypeName::OpaqueType(id)
248 }
249
217 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), 250 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool),
218 TypeCtor::Char => TypeName::Scalar(Scalar::Char), 251 TypeCtor::Char => TypeName::Scalar(Scalar::Char),
219 TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)), 252 TypeCtor::Int(int_ty) => TypeName::Scalar(int_ty_to_chalk(int_ty)),
220 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) => { 253 TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 }) => {
221 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) 254 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32))
222 } 255 }
223 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) => { 256 TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 }) => {
224 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) 257 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64))
225 } 258 }
226 259
@@ -235,9 +268,7 @@ impl ToChalk for TypeCtor {
235 } 268 }
236 TypeCtor::Never => TypeName::Never, 269 TypeCtor::Never => TypeName::Never,
237 270
238 TypeCtor::Int(Uncertain::Unknown) 271 TypeCtor::Adt(_)
239 | TypeCtor::Float(Uncertain::Unknown)
240 | TypeCtor::Adt(_)
241 | TypeCtor::Array 272 | TypeCtor::Array
242 | TypeCtor::FnPtr { .. } 273 | TypeCtor::FnPtr { .. }
243 | TypeCtor::Closure { .. } => { 274 | TypeCtor::Closure { .. } => {
@@ -252,23 +283,25 @@ impl ToChalk for TypeCtor {
252 match type_name { 283 match type_name {
253 TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), 284 TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
254 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), 285 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
255 TypeName::OpaqueType(_) => unreachable!(), 286 TypeName::OpaqueType(opaque_type_id) => {
287 TypeCtor::OpaqueType(from_chalk(db, opaque_type_id))
288 }
256 289
257 TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool, 290 TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool,
258 TypeName::Scalar(Scalar::Char) => TypeCtor::Char, 291 TypeName::Scalar(Scalar::Char) => TypeCtor::Char,
259 TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(Uncertain::Known(IntTy { 292 TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(IntTy {
260 signedness: Signedness::Signed, 293 signedness: Signedness::Signed,
261 bitness: bitness_from_chalk_int(int_ty), 294 bitness: bitness_from_chalk_int(int_ty),
262 })), 295 }),
263 TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(Uncertain::Known(IntTy { 296 TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(IntTy {
264 signedness: Signedness::Unsigned, 297 signedness: Signedness::Unsigned,
265 bitness: bitness_from_chalk_uint(uint_ty), 298 bitness: bitness_from_chalk_uint(uint_ty),
266 })), 299 }),
267 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => { 300 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => {
268 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) 301 TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 })
269 } 302 }
270 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => { 303 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => {
271 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) 304 TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 })
272 } 305 }
273 TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 }, 306 TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 },
274 TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)), 307 TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)),
@@ -447,6 +480,11 @@ impl ToChalk for GenericPredicate {
447 let ty = from_chalk(db, projection_eq.ty); 480 let ty = from_chalk(db, projection_eq.ty);
448 GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty }) 481 GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty })
449 } 482 }
483
484 chalk_ir::WhereClause::LifetimeOutlives(_) => {
485 // we shouldn't get these from Chalk
486 panic!("encountered LifetimeOutlives from Chalk")
487 }
450 } 488 }
451 } 489 }
452} 490}
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
index d88828c7c..556af7098 100644
--- a/crates/ra_hir_ty/src/traits/chalk/tls.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs
@@ -69,6 +69,11 @@ impl DebugContext<'_> {
69 let name = self.0.type_alias_data(type_alias).name.clone(); 69 let name = self.0.type_alias_data(type_alias).name.clone();
70 write!(f, "{}::{}", trait_name, name)?; 70 write!(f, "{}::{}", trait_name, name)?;
71 } 71 }
72 TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id {
73 crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
74 write!(f, "{{impl trait {} of {:?}}}", idx, func)?;
75 }
76 },
72 TypeCtor::Closure { def, expr } => { 77 TypeCtor::Closure { def, expr } => {
73 write!(f, "{{closure {:?} in ", expr.into_raw())?; 78 write!(f, "{{closure {:?} in ", expr.into_raw())?;
74 match def { 79 match def {
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index fa37b6955..e1fcf379d 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -126,3 +126,81 @@ pub(crate) fn completions(
126 126
127 Some(acc) 127 Some(acc)
128} 128}
129
130#[cfg(test)]
131mod tests {
132 use crate::completion::completion_config::CompletionConfig;
133 use crate::mock_analysis::analysis_and_position;
134
135 struct DetailAndDocumentation<'a> {
136 detail: &'a str,
137 documentation: &'a str,
138 }
139
140 fn check_detail_and_documentation(fixture: &str, expected: DetailAndDocumentation) {
141 let (analysis, position) = analysis_and_position(fixture);
142 let config = CompletionConfig::default();
143 let completions = analysis.completions(&config, position).unwrap().unwrap();
144 for item in completions {
145 if item.detail() == Some(expected.detail) {
146 let opt = item.documentation();
147 let doc = opt.as_ref().map(|it| it.as_str());
148 assert_eq!(doc, Some(expected.documentation));
149 return;
150 }
151 }
152 panic!("completion detail not found: {}", expected.detail)
153 }
154
155 #[test]
156 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
157 check_detail_and_documentation(
158 r#"
159 //- /lib.rs
160 macro_rules! bar {
161 () => {
162 struct Bar;
163 impl Bar {
164 #[doc = "Do the foo"]
165 fn foo(&self) {}
166 }
167 }
168 }
169
170 bar!();
171
172 fn foo() {
173 let bar = Bar;
174 bar.fo<|>;
175 }
176 "#,
177 DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
178 );
179 }
180
181 #[test]
182 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
183 check_detail_and_documentation(
184 r#"
185 //- /lib.rs
186 macro_rules! bar {
187 () => {
188 struct Bar;
189 impl Bar {
190 /// Do the foo
191 fn foo(&self) {}
192 }
193 }
194 }
195
196 bar!();
197
198 fn foo() {
199 let bar = Bar;
200 bar.fo<|>;
201 }
202 "#,
203 DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
204 );
205 }
206}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 15dc50cf1..e1bfd72f9 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -21,7 +21,7 @@ use ra_syntax::{
21}; 21};
22use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::{TextEdit, TextEditBuilder};
23 23
24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceChange, SourceFileEdit}; 24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceFileEdit};
25 25
26#[derive(Debug, Copy, Clone)] 26#[derive(Debug, Copy, Clone)]
27pub enum Severity { 27pub enum Severity {
@@ -115,7 +115,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
115 let node = d.ast(db); 115 let node = d.ast(db);
116 let replacement = format!("Ok({})", node.syntax()); 116 let replacement = format!("Ok({})", node.syntax());
117 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 117 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
118 let source_change = SourceChange::source_file_edit_from(file_id, edit); 118 let source_change = SourceFileEdit { file_id, edit }.into();
119 let fix = Fix::new("Wrap with ok", source_change); 119 let fix = Fix::new("Wrap with ok", source_change);
120 res.borrow_mut().push(Diagnostic { 120 res.borrow_mut().push(Diagnostic {
121 range: sema.diagnostics_range(d).range, 121 range: sema.diagnostics_range(d).range,
@@ -187,7 +187,8 @@ fn check_struct_shorthand_initialization(
187 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { 187 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) {
188 let field_name = name_ref.syntax().text().to_string(); 188 let field_name = name_ref.syntax().text().to_string();
189 let field_expr = expr.syntax().text().to_string(); 189 let field_expr = expr.syntax().text().to_string();
190 if field_name == field_expr { 190 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
191 if field_name == field_expr && !field_name_is_tup_index {
191 let mut edit_builder = TextEditBuilder::default(); 192 let mut edit_builder = TextEditBuilder::default();
192 edit_builder.delete(record_field.syntax().text_range()); 193 edit_builder.delete(record_field.syntax().text_range());
193 edit_builder.insert(record_field.syntax().text_range().start(), field_name); 194 edit_builder.insert(record_field.syntax().text_range().start(), field_name);
@@ -321,29 +322,26 @@ mod tests {
321 fn test_wrap_return_type() { 322 fn test_wrap_return_type() {
322 let before = r#" 323 let before = r#"
323 //- /main.rs 324 //- /main.rs
324 use std::{string::String, result::Result::{self, Ok, Err}}; 325 use core::result::Result::{self, Ok, Err};
325 326
326 fn div(x: i32, y: i32) -> Result<i32, String> { 327 fn div(x: i32, y: i32) -> Result<i32, ()> {
327 if y == 0 { 328 if y == 0 {
328 return Err("div by zero".into()); 329 return Err(());
329 } 330 }
330 x / y<|> 331 x / y<|>
331 } 332 }
332 333
333 //- /std/lib.rs 334 //- /core/lib.rs
334 pub mod string {
335 pub struct String { }
336 }
337 pub mod result { 335 pub mod result {
338 pub enum Result<T, E> { Ok(T), Err(E) } 336 pub enum Result<T, E> { Ok(T), Err(E) }
339 } 337 }
340 "#; 338 "#;
341 let after = r#" 339 let after = r#"
342 use std::{string::String, result::Result::{self, Ok, Err}}; 340 use core::result::Result::{self, Ok, Err};
343 341
344 fn div(x: i32, y: i32) -> Result<i32, String> { 342 fn div(x: i32, y: i32) -> Result<i32, ()> {
345 if y == 0 { 343 if y == 0 {
346 return Err("div by zero".into()); 344 return Err(());
347 } 345 }
348 Ok(x / y) 346 Ok(x / y)
349 } 347 }
@@ -355,7 +353,7 @@ mod tests {
355 fn test_wrap_return_type_handles_generic_functions() { 353 fn test_wrap_return_type_handles_generic_functions() {
356 let before = r#" 354 let before = r#"
357 //- /main.rs 355 //- /main.rs
358 use std::result::Result::{self, Ok, Err}; 356 use core::result::Result::{self, Ok, Err};
359 357
360 fn div<T>(x: T) -> Result<T, i32> { 358 fn div<T>(x: T) -> Result<T, i32> {
361 if x == 0 { 359 if x == 0 {
@@ -364,13 +362,13 @@ mod tests {
364 <|>x 362 <|>x
365 } 363 }
366 364
367 //- /std/lib.rs 365 //- /core/lib.rs
368 pub mod result { 366 pub mod result {
369 pub enum Result<T, E> { Ok(T), Err(E) } 367 pub enum Result<T, E> { Ok(T), Err(E) }
370 } 368 }
371 "#; 369 "#;
372 let after = r#" 370 let after = r#"
373 use std::result::Result::{self, Ok, Err}; 371 use core::result::Result::{self, Ok, Err};
374 372
375 fn div<T>(x: T) -> Result<T, i32> { 373 fn div<T>(x: T) -> Result<T, i32> {
376 if x == 0 { 374 if x == 0 {
@@ -386,32 +384,29 @@ mod tests {
386 fn test_wrap_return_type_handles_type_aliases() { 384 fn test_wrap_return_type_handles_type_aliases() {
387 let before = r#" 385 let before = r#"
388 //- /main.rs 386 //- /main.rs
389 use std::{string::String, result::Result::{self, Ok, Err}}; 387 use core::result::Result::{self, Ok, Err};
390 388
391 type MyResult<T> = Result<T, String>; 389 type MyResult<T> = Result<T, ()>;
392 390
393 fn div(x: i32, y: i32) -> MyResult<i32> { 391 fn div(x: i32, y: i32) -> MyResult<i32> {
394 if y == 0 { 392 if y == 0 {
395 return Err("div by zero".into()); 393 return Err(());
396 } 394 }
397 x <|>/ y 395 x <|>/ y
398 } 396 }
399 397
400 //- /std/lib.rs 398 //- /core/lib.rs
401 pub mod string {
402 pub struct String { }
403 }
404 pub mod result { 399 pub mod result {
405 pub enum Result<T, E> { Ok(T), Err(E) } 400 pub enum Result<T, E> { Ok(T), Err(E) }
406 } 401 }
407 "#; 402 "#;
408 let after = r#" 403 let after = r#"
409 use std::{string::String, result::Result::{self, Ok, Err}}; 404 use core::result::Result::{self, Ok, Err};
410 405
411 type MyResult<T> = Result<T, String>; 406 type MyResult<T> = Result<T, ()>;
412 fn div(x: i32, y: i32) -> MyResult<i32> { 407 fn div(x: i32, y: i32) -> MyResult<i32> {
413 if y == 0 { 408 if y == 0 {
414 return Err("div by zero".into()); 409 return Err(());
415 } 410 }
416 Ok(x / y) 411 Ok(x / y)
417 } 412 }
@@ -423,16 +418,13 @@ mod tests {
423 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 418 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
424 let content = r#" 419 let content = r#"
425 //- /main.rs 420 //- /main.rs
426 use std::{string::String, result::Result::{self, Ok, Err}}; 421 use core::result::Result::{self, Ok, Err};
427 422
428 fn foo() -> Result<String, i32> { 423 fn foo() -> Result<(), i32> {
429 0<|> 424 0<|>
430 } 425 }
431 426
432 //- /std/lib.rs 427 //- /core/lib.rs
433 pub mod string {
434 pub struct String { }
435 }
436 pub mod result { 428 pub mod result {
437 pub enum Result<T, E> { Ok(T), Err(E) } 429 pub enum Result<T, E> { Ok(T), Err(E) }
438 } 430 }
@@ -444,7 +436,7 @@ mod tests {
444 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 436 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
445 let content = r#" 437 let content = r#"
446 //- /main.rs 438 //- /main.rs
447 use std::{string::String, result::Result::{self, Ok, Err}}; 439 use core::result::Result::{self, Ok, Err};
448 440
449 enum SomeOtherEnum { 441 enum SomeOtherEnum {
450 Ok(i32), 442 Ok(i32),
@@ -455,10 +447,7 @@ mod tests {
455 0<|> 447 0<|>
456 } 448 }
457 449
458 //- /std/lib.rs 450 //- /core/lib.rs
459 pub mod string {
460 pub struct String { }
461 }
462 pub mod result { 451 pub mod result {
463 pub enum Result<T, E> { Ok(T), Err(E) } 452 pub enum Result<T, E> { Ok(T), Err(E) }
464 } 453 }
@@ -731,6 +720,18 @@ mod tests {
731 "#, 720 "#,
732 check_struct_shorthand_initialization, 721 check_struct_shorthand_initialization,
733 ); 722 );
723 check_not_applicable(
724 r#"
725 struct A(usize);
726
727 fn main() {
728 A {
729 0: 0
730 }
731 }
732 "#,
733 check_struct_shorthand_initialization,
734 );
734 735
735 check_apply( 736 check_apply(
736 r#" 737 r#"
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 9572debd8..ca8a6a650 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -10,7 +10,7 @@ use std::{
10use hir::{Docs, Documentation, HasSource, HirDisplay}; 10use hir::{Docs, Documentation, HasSource, HirDisplay};
11use ra_ide_db::RootDatabase; 11use ra_ide_db::RootDatabase;
12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; 12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
13use stdx::SepBy; 13use stdx::{split1, SepBy};
14 14
15use crate::display::{generic_parameters, where_predicates}; 15use crate::display::{generic_parameters, where_predicates};
16 16
@@ -207,7 +207,16 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
207 res.push(raw_param); 207 res.push(raw_param);
208 } 208 }
209 209
210 res.extend(param_list.params().map(|param| param.syntax().text().to_string())); 210 // macro-generated functions are missing whitespace
211 fn fmt_param(param: ast::Param) -> String {
212 let text = param.syntax().text().to_string();
213 match split1(&text, ':') {
214 Some((left, right)) => format!("{}: {}", left.trim(), right.trim()),
215 _ => text,
216 }
217 }
218
219 res.extend(param_list.params().map(fmt_param));
211 res_types.extend(param_list.params().map(|param| { 220 res_types.extend(param_list.params().map(|param| {
212 let param_text = param.syntax().text().to_string(); 221 let param_text = param.syntax().text().to_string();
213 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { 222 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) {
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 5da28edd2..c7bb1e69f 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -92,15 +92,16 @@ impl NavigationTarget {
92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
93 if let Some(src) = module.declaration_source(db) { 93 if let Some(src) = module.declaration_source(db) {
94 let frange = original_range(db, src.as_ref().map(|it| it.syntax())); 94 let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
95 return NavigationTarget::from_syntax( 95 let mut res = NavigationTarget::from_syntax(
96 frange.file_id, 96 frange.file_id,
97 name, 97 name,
98 None, 98 None,
99 frange.range, 99 frange.range,
100 src.value.syntax().kind(), 100 src.value.syntax().kind(),
101 src.value.doc_comment_text(),
102 src.value.short_label(),
103 ); 101 );
102 res.docs = src.value.doc_comment_text();
103 res.description = src.value.short_label();
104 return res;
104 } 105 }
105 module.to_nav(db) 106 module.to_nav(db)
106 } 107 }
@@ -130,11 +131,9 @@ impl NavigationTarget {
130 } 131 }
131 132
132 /// Allows `NavigationTarget` to be created from a `NameOwner` 133 /// Allows `NavigationTarget` to be created from a `NameOwner`
133 fn from_named( 134 pub(crate) fn from_named(
134 db: &RootDatabase, 135 db: &RootDatabase,
135 node: InFile<&dyn ast::NameOwner>, 136 node: InFile<&dyn ast::NameOwner>,
136 docs: Option<String>,
137 description: Option<String>,
138 ) -> NavigationTarget { 137 ) -> NavigationTarget {
139 //FIXME: use `_` instead of empty string 138 //FIXME: use `_` instead of empty string
140 let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default(); 139 let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default();
@@ -148,8 +147,6 @@ impl NavigationTarget {
148 focus_range, 147 focus_range,
149 frange.range, 148 frange.range,
150 node.value.syntax().kind(), 149 node.value.syntax().kind(),
151 docs,
152 description,
153 ) 150 )
154 } 151 }
155 152
@@ -159,8 +156,6 @@ impl NavigationTarget {
159 focus_range: Option<TextRange>, 156 focus_range: Option<TextRange>,
160 full_range: TextRange, 157 full_range: TextRange,
161 kind: SyntaxKind, 158 kind: SyntaxKind,
162 docs: Option<String>,
163 description: Option<String>,
164 ) -> NavigationTarget { 159 ) -> NavigationTarget {
165 NavigationTarget { 160 NavigationTarget {
166 file_id, 161 file_id,
@@ -169,8 +164,8 @@ impl NavigationTarget {
169 full_range, 164 full_range,
170 focus_range, 165 focus_range,
171 container_name: None, 166 container_name: None,
172 description, 167 description: None,
173 docs, 168 docs: None,
174 } 169 }
175 } 170 }
176} 171}
@@ -238,12 +233,11 @@ where
238{ 233{
239 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 234 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
240 let src = self.source(db); 235 let src = self.source(db);
241 NavigationTarget::from_named( 236 let mut res =
242 db, 237 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
243 src.as_ref().map(|it| it as &dyn ast::NameOwner), 238 res.docs = src.value.doc_comment_text();
244 src.value.doc_comment_text(), 239 res.description = src.value.short_label();
245 src.value.short_label(), 240 res
246 )
247 } 241 }
248} 242}
249 243
@@ -258,15 +252,7 @@ impl ToNav for hir::Module {
258 } 252 }
259 }; 253 };
260 let frange = original_range(db, src.with_value(syntax)); 254 let frange = original_range(db, src.with_value(syntax));
261 NavigationTarget::from_syntax( 255 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind())
262 frange.file_id,
263 name,
264 focus,
265 frange.range,
266 syntax.kind(),
267 None,
268 None,
269 )
270 } 256 }
271} 257}
272 258
@@ -285,8 +271,6 @@ impl ToNav for hir::ImplDef {
285 None, 271 None,
286 frange.range, 272 frange.range,
287 src.value.syntax().kind(), 273 src.value.syntax().kind(),
288 None,
289 None,
290 ) 274 )
291 } 275 }
292} 276}
@@ -296,12 +280,12 @@ impl ToNav for hir::Field {
296 let src = self.source(db); 280 let src = self.source(db);
297 281
298 match &src.value { 282 match &src.value {
299 FieldSource::Named(it) => NavigationTarget::from_named( 283 FieldSource::Named(it) => {
300 db, 284 let mut res = NavigationTarget::from_named(db, src.with_value(it));
301 src.with_value(it), 285 res.docs = it.doc_comment_text();
302 it.doc_comment_text(), 286 res.description = it.short_label();
303 it.short_label(), 287 res
304 ), 288 }
305 FieldSource::Pos(it) => { 289 FieldSource::Pos(it) => {
306 let frange = original_range(db, src.with_value(it.syntax())); 290 let frange = original_range(db, src.with_value(it.syntax()));
307 NavigationTarget::from_syntax( 291 NavigationTarget::from_syntax(
@@ -310,8 +294,6 @@ impl ToNav for hir::Field {
310 None, 294 None,
311 frange.range, 295 frange.range,
312 it.syntax().kind(), 296 it.syntax().kind(),
313 None,
314 None,
315 ) 297 )
316 } 298 }
317 } 299 }
@@ -322,12 +304,10 @@ impl ToNav for hir::MacroDef {
322 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 304 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
323 let src = self.source(db); 305 let src = self.source(db);
324 log::debug!("nav target {:#?}", src.value.syntax()); 306 log::debug!("nav target {:#?}", src.value.syntax());
325 NavigationTarget::from_named( 307 let mut res =
326 db, 308 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
327 src.as_ref().map(|it| it as &dyn ast::NameOwner), 309 res.docs = src.value.doc_comment_text();
328 src.value.doc_comment_text(), 310 res
329 None,
330 )
331 } 311 }
332} 312}
333 313
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index a6c86e99c..0798d2c36 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,6 +1,6 @@
1use hir::Semantics; 1use hir::Semantics;
2use ra_ide_db::{ 2use ra_ide_db::{
3 defs::{classify_name, classify_name_ref}, 3 defs::{classify_name, classify_name_ref, NameClass},
4 symbol_index, RootDatabase, 4 symbol_index, RootDatabase,
5}; 5};
6use ra_syntax::{ 6use ra_syntax::{
@@ -39,7 +39,10 @@ pub(crate) fn goto_definition(
39 reference_definition(&sema, &name_ref).to_vec() 39 reference_definition(&sema, &name_ref).to_vec()
40 }, 40 },
41 ast::Name(name) => { 41 ast::Name(name) => {
42 let def = classify_name(&sema, &name)?.definition(); 42 let def = match classify_name(&sema, &name)? {
43 NameClass::Definition(def) | NameClass::ConstReference(def) => def,
44 NameClass::FieldShorthand { local: _, field } => field,
45 };
43 let nav = def.try_to_nav(sema.db)?; 46 let nav = def.try_to_nav(sema.db)?;
44 vec![nav] 47 vec![nav]
45 }, 48 },
@@ -886,4 +889,23 @@ mod tests {
886 "x", 889 "x",
887 ) 890 )
888 } 891 }
892
893 #[test]
894 fn goto_def_for_enum_variant_field() {
895 check_goto(
896 "
897 //- /lib.rs
898 enum Foo {
899 Bar { x: i32 }
900 }
901 fn baz(foo: Foo) {
902 match foo {
903 Foo::Bar { x<|> } => x
904 };
905 }
906 ",
907 "x RECORD_FIELD_DEF FileId(1) 21..27 21..22",
908 "x: i32|x",
909 );
910 }
889} 911}
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index d96cb5596..ad78b7671 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,8 +1,8 @@
1use std::iter::once; 1use std::iter::once;
2 2
3use hir::{ 3use hir::{
4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 4 Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay,
5 ModuleSource, Semantics, 5 ModuleDef, ModuleSource, Semantics,
6}; 6};
7use itertools::Itertools; 7use itertools::Itertools;
8use ra_db::SourceDatabase; 8use ra_db::SourceDatabase;
@@ -10,22 +10,55 @@ use ra_ide_db::{
10 defs::{classify_name, classify_name_ref, Definition}, 10 defs::{classify_name, classify_name_ref, Definition},
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use ra_syntax::{ 13use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
14 ast::{self, DocCommentsOwner},
15 match_ast, AstNode,
16 SyntaxKind::*,
17 SyntaxToken, TokenAtOffset,
18};
19 14
20use crate::{ 15use crate::{
21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 16 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav},
22 FilePosition, RangeInfo, 17 runnables::runnable,
18 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
23}; 19};
20use test_utils::mark;
21
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct HoverConfig {
24 pub implementations: bool,
25 pub run: bool,
26 pub debug: bool,
27}
28
29impl Default for HoverConfig {
30 fn default() -> Self {
31 Self { implementations: true, run: true, debug: true }
32 }
33}
34
35impl HoverConfig {
36 pub const NO_ACTIONS: Self = Self { implementations: false, run: false, debug: false };
37
38 pub fn any(&self) -> bool {
39 self.implementations || self.runnable()
40 }
41
42 pub fn none(&self) -> bool {
43 !self.any()
44 }
45
46 pub fn runnable(&self) -> bool {
47 self.run || self.debug
48 }
49}
50
51#[derive(Debug, Clone)]
52pub enum HoverAction {
53 Runnable(Runnable),
54 Implementaion(FilePosition),
55}
24 56
25/// Contains the results when hovering over an item 57/// Contains the results when hovering over an item
26#[derive(Debug, Default)] 58#[derive(Debug, Default)]
27pub struct HoverResult { 59pub struct HoverResult {
28 results: Vec<String>, 60 results: Vec<String>,
61 actions: Vec<HoverAction>,
29} 62}
30 63
31impl HoverResult { 64impl HoverResult {
@@ -53,10 +86,20 @@ impl HoverResult {
53 &self.results 86 &self.results
54 } 87 }
55 88
89 pub fn actions(&self) -> &[HoverAction] {
90 &self.actions
91 }
92
93 pub fn push_action(&mut self, action: HoverAction) {
94 self.actions.push(action);
95 }
96
56 /// Returns the results converted into markup 97 /// Returns the results converted into markup
57 /// for displaying in a UI 98 /// for displaying in a UI
99 ///
100 /// Does not process actions!
58 pub fn to_markup(&self) -> String { 101 pub fn to_markup(&self) -> String {
59 self.results.join("\n\n---\n") 102 self.results.join("\n\n___\n")
60 } 103 }
61} 104}
62 105
@@ -87,6 +130,14 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
87 res.extend(hover_text_from_name_kind(db, name_kind)); 130 res.extend(hover_text_from_name_kind(db, name_kind));
88 131
89 if !res.is_empty() { 132 if !res.is_empty() {
133 if let Some(action) = show_implementations_action(db, name_kind) {
134 res.push_action(action);
135 }
136
137 if let Some(action) = runnable_action(&sema, name_kind, position.file_id) {
138 res.push_action(action);
139 }
140
90 return Some(RangeInfo::new(range, res)); 141 return Some(RangeInfo::new(range, res));
91 } 142 }
92 } 143 }
@@ -117,6 +168,56 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
117 Some(RangeInfo::new(range, res)) 168 Some(RangeInfo::new(range, res))
118} 169}
119 170
171fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
172 fn to_action(nav_target: NavigationTarget) -> HoverAction {
173 HoverAction::Implementaion(FilePosition {
174 file_id: nav_target.file_id(),
175 offset: nav_target.range().start(),
176 })
177 }
178
179 match def {
180 Definition::ModuleDef(it) => match it {
181 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))),
182 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))),
183 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))),
184 ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))),
185 _ => None,
186 },
187 _ => None,
188 }
189}
190
191fn runnable_action(
192 sema: &Semantics<RootDatabase>,
193 def: Definition,
194 file_id: FileId,
195) -> Option<HoverAction> {
196 match def {
197 Definition::ModuleDef(it) => match it {
198 ModuleDef::Module(it) => match it.definition_source(sema.db).value {
199 ModuleSource::Module(it) => runnable(&sema, it.syntax().clone(), file_id)
200 .map(|it| HoverAction::Runnable(it)),
201 _ => None,
202 },
203 ModuleDef::Function(it) => {
204 let src = it.source(sema.db);
205 if src.file_id != file_id.into() {
206 mark::hit!(hover_macro_generated_struct_fn_doc_comment);
207 mark::hit!(hover_macro_generated_struct_fn_doc_attr);
208
209 return None;
210 }
211
212 runnable(&sema, src.value.syntax().clone(), file_id)
213 .map(|it| HoverAction::Runnable(it))
214 }
215 _ => None,
216 },
217 _ => None,
218 }
219}
220
120fn hover_text( 221fn hover_text(
121 docs: Option<String>, 222 docs: Option<String>,
122 desc: Option<String>, 223 desc: Option<String>,
@@ -169,13 +270,15 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
169 return match def { 270 return match def {
170 Definition::Macro(it) => { 271 Definition::Macro(it) => {
171 let src = it.source(db); 272 let src = it.source(db);
172 hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) 273 let docs = Documentation::from_ast(&src.value).map(Into::into);
274 hover_text(docs, Some(macro_label(&src.value)), mod_path)
173 } 275 }
174 Definition::Field(it) => { 276 Definition::Field(it) => {
175 let src = it.source(db); 277 let src = it.source(db);
176 match src.value { 278 match src.value {
177 FieldSource::Named(it) => { 279 FieldSource::Named(it) => {
178 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 280 let docs = Documentation::from_ast(&it).map(Into::into);
281 hover_text(docs, it.short_label(), mod_path)
179 } 282 }
180 _ => None, 283 _ => None,
181 } 284 }
@@ -183,7 +286,8 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
183 Definition::ModuleDef(it) => match it { 286 Definition::ModuleDef(it) => match it {
184 ModuleDef::Module(it) => match it.definition_source(db).value { 287 ModuleDef::Module(it) => match it.definition_source(db).value {
185 ModuleSource::Module(it) => { 288 ModuleSource::Module(it) => {
186 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 289 let docs = Documentation::from_ast(&it).map(Into::into);
290 hover_text(docs, it.short_label(), mod_path)
187 } 291 }
188 _ => None, 292 _ => None,
189 }, 293 },
@@ -208,10 +312,11 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
208 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> 312 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String>
209 where 313 where
210 D: HasSource<Ast = A>, 314 D: HasSource<Ast = A>,
211 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, 315 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner,
212 { 316 {
213 let src = def.source(db); 317 let src = def.source(db);
214 hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) 318 let docs = Documentation::from_ast(&src.value).map(Into::into);
319 hover_text(docs, src.value.short_label(), mod_path)
215 } 320 }
216} 321}
217 322
@@ -229,6 +334,9 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
229 334
230#[cfg(test)] 335#[cfg(test)]
231mod tests { 336mod tests {
337 use super::*;
338 use insta::assert_debug_snapshot;
339
232 use ra_db::FileLoader; 340 use ra_db::FileLoader;
233 use ra_syntax::TextRange; 341 use ra_syntax::TextRange;
234 342
@@ -242,7 +350,15 @@ mod tests {
242 s.map(trim_markup) 350 s.map(trim_markup)
243 } 351 }
244 352
245 fn check_hover_result(fixture: &str, expected: &[&str]) -> String { 353 fn assert_impl_action(action: &HoverAction, position: u32) {
354 let offset = match action {
355 HoverAction::Implementaion(pos) => pos.offset,
356 it => panic!("Unexpected hover action: {:#?}", it),
357 };
358 assert_eq!(offset, position.into());
359 }
360
361 fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) {
246 let (analysis, position) = analysis_and_position(fixture); 362 let (analysis, position) = analysis_and_position(fixture);
247 let hover = analysis.hover(position).unwrap().unwrap(); 363 let hover = analysis.hover(position).unwrap().unwrap();
248 let mut results = Vec::from(hover.info.results()); 364 let mut results = Vec::from(hover.info.results());
@@ -257,7 +373,7 @@ mod tests {
257 assert_eq!(hover.info.len(), expected.len()); 373 assert_eq!(hover.info.len(), expected.len());
258 374
259 let content = analysis.db.file_text(position.file_id); 375 let content = analysis.db.file_text(position.file_id);
260 content[hover.range].to_string() 376 (content[hover.range].to_string(), hover.info.actions().to_vec())
261 } 377 }
262 378
263 fn check_hover_no_result(fixture: &str) { 379 fn check_hover_no_result(fixture: &str) {
@@ -458,7 +574,7 @@ struct Test<K, T = u8> {
458} 574}
459 575
460fn main() { 576fn main() {
461 let zz<|> = Test { t: 23, k: 33 }; 577 let zz<|> = Test { t: 23u8, k: 33 };
462}"#, 578}"#,
463 &["Test<i32, u8>"], 579 &["Test<i32, u8>"],
464 ); 580 );
@@ -747,7 +863,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
747 863
748 #[test] 864 #[test]
749 fn test_hover_through_macro() { 865 fn test_hover_through_macro() {
750 let hover_on = check_hover_result( 866 let (hover_on, _) = check_hover_result(
751 " 867 "
752 //- /lib.rs 868 //- /lib.rs
753 macro_rules! id { 869 macro_rules! id {
@@ -768,7 +884,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
768 884
769 #[test] 885 #[test]
770 fn test_hover_through_expr_in_macro() { 886 fn test_hover_through_expr_in_macro() {
771 let hover_on = check_hover_result( 887 let (hover_on, _) = check_hover_result(
772 " 888 "
773 //- /lib.rs 889 //- /lib.rs
774 macro_rules! id { 890 macro_rules! id {
@@ -786,7 +902,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
786 902
787 #[test] 903 #[test]
788 fn test_hover_through_expr_in_macro_recursive() { 904 fn test_hover_through_expr_in_macro_recursive() {
789 let hover_on = check_hover_result( 905 let (hover_on, _) = check_hover_result(
790 " 906 "
791 //- /lib.rs 907 //- /lib.rs
792 macro_rules! id_deep { 908 macro_rules! id_deep {
@@ -807,7 +923,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
807 923
808 #[test] 924 #[test]
809 fn test_hover_through_func_in_macro_recursive() { 925 fn test_hover_through_func_in_macro_recursive() {
810 let hover_on = check_hover_result( 926 let (hover_on, _) = check_hover_result(
811 " 927 "
812 //- /lib.rs 928 //- /lib.rs
813 macro_rules! id_deep { 929 macro_rules! id_deep {
@@ -831,7 +947,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
831 947
832 #[test] 948 #[test]
833 fn test_hover_through_literal_string_in_macro() { 949 fn test_hover_through_literal_string_in_macro() {
834 let hover_on = check_hover_result( 950 let (hover_on, _) = check_hover_result(
835 r#" 951 r#"
836 //- /lib.rs 952 //- /lib.rs
837 macro_rules! arr { 953 macro_rules! arr {
@@ -850,7 +966,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
850 966
851 #[test] 967 #[test]
852 fn test_hover_through_assert_macro() { 968 fn test_hover_through_assert_macro() {
853 let hover_on = check_hover_result( 969 let (hover_on, _) = check_hover_result(
854 r#" 970 r#"
855 //- /lib.rs 971 //- /lib.rs
856 #[rustc_builtin_macro] 972 #[rustc_builtin_macro]
@@ -926,13 +1042,14 @@ fn func(foo: i32) { if true { <|>foo; }; }
926 1042
927 #[test] 1043 #[test]
928 fn test_hover_trait_show_qualifiers() { 1044 fn test_hover_trait_show_qualifiers() {
929 check_hover_result( 1045 let (_, actions) = check_hover_result(
930 " 1046 "
931 //- /lib.rs 1047 //- /lib.rs
932 unsafe trait foo<|>() {} 1048 unsafe trait foo<|>() {}
933 ", 1049 ",
934 &["unsafe trait foo"], 1050 &["unsafe trait foo"],
935 ); 1051 );
1052 assert_impl_action(&actions[0], 13);
936 } 1053 }
937 1054
938 #[test] 1055 #[test]
@@ -951,4 +1068,246 @@ fn func(foo: i32) { if true { <|>foo; }; }
951 &["mod my"], 1068 &["mod my"],
952 ); 1069 );
953 } 1070 }
1071
1072 #[test]
1073 fn test_hover_struct_doc_comment() {
1074 check_hover_result(
1075 r#"
1076 //- /lib.rs
1077 /// bar docs
1078 struct Bar;
1079
1080 fn foo() {
1081 let bar = Ba<|>r;
1082 }
1083 "#,
1084 &["struct Bar\n```\n___\n\nbar docs"],
1085 );
1086 }
1087
1088 #[test]
1089 fn test_hover_struct_doc_attr() {
1090 check_hover_result(
1091 r#"
1092 //- /lib.rs
1093 #[doc = "bar docs"]
1094 struct Bar;
1095
1096 fn foo() {
1097 let bar = Ba<|>r;
1098 }
1099 "#,
1100 &["struct Bar\n```\n___\n\nbar docs"],
1101 );
1102 }
1103
1104 #[test]
1105 fn test_hover_struct_doc_attr_multiple_and_mixed() {
1106 check_hover_result(
1107 r#"
1108 //- /lib.rs
1109 /// bar docs 0
1110 #[doc = "bar docs 1"]
1111 #[doc = "bar docs 2"]
1112 struct Bar;
1113
1114 fn foo() {
1115 let bar = Ba<|>r;
1116 }
1117 "#,
1118 &["struct Bar\n```\n___\n\nbar docs 0\n\nbar docs 1\n\nbar docs 2"],
1119 );
1120 }
1121
1122 #[test]
1123 fn test_hover_macro_generated_struct_fn_doc_comment() {
1124 mark::check!(hover_macro_generated_struct_fn_doc_comment);
1125
1126 check_hover_result(
1127 r#"
1128 //- /lib.rs
1129 macro_rules! bar {
1130 () => {
1131 struct Bar;
1132 impl Bar {
1133 /// Do the foo
1134 fn foo(&self) {}
1135 }
1136 }
1137 }
1138
1139 bar!();
1140
1141 fn foo() {
1142 let bar = Bar;
1143 bar.fo<|>o();
1144 }
1145 "#,
1146 &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\n Do the foo"],
1147 );
1148 }
1149
1150 #[test]
1151 fn test_hover_macro_generated_struct_fn_doc_attr() {
1152 mark::check!(hover_macro_generated_struct_fn_doc_attr);
1153
1154 check_hover_result(
1155 r#"
1156 //- /lib.rs
1157 macro_rules! bar {
1158 () => {
1159 struct Bar;
1160 impl Bar {
1161 #[doc = "Do the foo"]
1162 fn foo(&self) {}
1163 }
1164 }
1165 }
1166
1167 bar!();
1168
1169 fn foo() {
1170 let bar = Bar;
1171 bar.fo<|>o();
1172 }
1173 "#,
1174 &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"],
1175 );
1176 }
1177
1178 #[test]
1179 fn test_hover_trait_has_impl_action() {
1180 let (_, actions) = check_hover_result(
1181 "
1182 //- /lib.rs
1183 trait foo<|>() {}
1184 ",
1185 &["trait foo"],
1186 );
1187 assert_impl_action(&actions[0], 6);
1188 }
1189
1190 #[test]
1191 fn test_hover_struct_has_impl_action() {
1192 let (_, actions) = check_hover_result(
1193 "
1194 //- /lib.rs
1195 struct foo<|>() {}
1196 ",
1197 &["struct foo"],
1198 );
1199 assert_impl_action(&actions[0], 7);
1200 }
1201
1202 #[test]
1203 fn test_hover_union_has_impl_action() {
1204 let (_, actions) = check_hover_result(
1205 "
1206 //- /lib.rs
1207 union foo<|>() {}
1208 ",
1209 &["union foo"],
1210 );
1211 assert_impl_action(&actions[0], 6);
1212 }
1213
1214 #[test]
1215 fn test_hover_enum_has_impl_action() {
1216 let (_, actions) = check_hover_result(
1217 "
1218 //- /lib.rs
1219 enum foo<|>() {
1220 A,
1221 B
1222 }
1223 ",
1224 &["enum foo"],
1225 );
1226 assert_impl_action(&actions[0], 5);
1227 }
1228
1229 #[test]
1230 fn test_hover_test_has_action() {
1231 let (_, actions) = check_hover_result(
1232 "
1233 //- /lib.rs
1234 #[test]
1235 fn foo_<|>test() {}
1236 ",
1237 &["fn foo_test()"],
1238 );
1239 assert_debug_snapshot!(actions,
1240 @r###"
1241 [
1242 Runnable(
1243 Runnable {
1244 nav: NavigationTarget {
1245 file_id: FileId(
1246 1,
1247 ),
1248 full_range: 0..24,
1249 name: "foo_test",
1250 kind: FN_DEF,
1251 focus_range: Some(
1252 11..19,
1253 ),
1254 container_name: None,
1255 description: None,
1256 docs: None,
1257 },
1258 kind: Test {
1259 test_id: Path(
1260 "foo_test",
1261 ),
1262 attr: TestAttr {
1263 ignore: false,
1264 },
1265 },
1266 cfg_exprs: [],
1267 },
1268 ),
1269 ]
1270 "###);
1271 }
1272
1273 #[test]
1274 fn test_hover_test_mod_has_action() {
1275 let (_, actions) = check_hover_result(
1276 "
1277 //- /lib.rs
1278 mod tests<|> {
1279 #[test]
1280 fn foo_test() {}
1281 }
1282 ",
1283 &["mod tests"],
1284 );
1285 assert_debug_snapshot!(actions,
1286 @r###"
1287 [
1288 Runnable(
1289 Runnable {
1290 nav: NavigationTarget {
1291 file_id: FileId(
1292 1,
1293 ),
1294 full_range: 0..46,
1295 name: "tests",
1296 kind: MODULE,
1297 focus_range: Some(
1298 4..9,
1299 ),
1300 container_name: None,
1301 description: None,
1302 docs: None,
1303 },
1304 kind: TestMod {
1305 path: "tests",
1306 },
1307 cfg_exprs: [],
1308 },
1309 ),
1310 ]
1311 "###);
1312 }
954} 1313}
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 75bd3c96b..7eb2cef73 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -149,11 +149,10 @@ fn get_param_name_hints(
149 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), 149 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(),
150 _ => return None, 150 _ => return None,
151 }; 151 };
152 let args_count = args.clone().count();
153 152
154 let fn_signature = get_fn_signature(sema, &expr)?; 153 let fn_signature = get_fn_signature(sema, &expr)?;
155 let n_params_to_skip = 154 let n_params_to_skip =
156 if fn_signature.has_self_param && fn_signature.parameter_names.len() > args_count { 155 if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) {
157 1 156 1
158 } else { 157 } else {
159 0 158 0
@@ -416,7 +415,7 @@ struct Test<K, T = u8> {
416} 415}
417 416
418fn main() { 417fn main() {
419 let zz = Test { t: 23, k: 33 }; 418 let zz = Test { t: 23u8, k: 33 };
420 let zz_ref = &zz; 419 let zz_ref = &zz;
421}"#, 420}"#,
422 ); 421 );
@@ -429,7 +428,7 @@ fn main() {
429 label: "Test<i32>", 428 label: "Test<i32>",
430 }, 429 },
431 InlayHint { 430 InlayHint {
432 range: 105..111, 431 range: 107..113,
433 kind: TypeHint, 432 kind: TypeHint,
434 label: "&Test<i32>", 433 label: "&Test<i32>",
435 }, 434 },
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 12d5716e8..28f686767 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -66,7 +66,7 @@ pub use crate::{
66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
67 expand_macro::ExpandedMacro, 67 expand_macro::ExpandedMacro,
68 folding_ranges::{Fold, FoldKind}, 68 folding_ranges::{Fold, FoldKind},
69 hover::HoverResult, 69 hover::{HoverAction, HoverConfig, HoverResult},
70 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, 70 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
71 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, 71 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
72 runnables::{Runnable, RunnableKind, TestId}, 72 runnables::{Runnable, RunnableKind, TestId},
@@ -77,7 +77,7 @@ pub use crate::{
77}; 77};
78 78
79pub use hir::Documentation; 79pub use hir::Documentation;
80pub use ra_assists::{AssistConfig, AssistId}; 80pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist};
81pub use ra_db::{ 81pub use ra_db::{
82 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, 82 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId,
83}; 83};
@@ -142,14 +142,6 @@ pub struct AnalysisHost {
142 db: RootDatabase, 142 db: RootDatabase,
143} 143}
144 144
145#[derive(Debug)]
146pub struct Assist {
147 pub id: AssistId,
148 pub label: String,
149 pub group_label: Option<String>,
150 pub source_change: SourceChange,
151}
152
153impl AnalysisHost { 145impl AnalysisHost {
154 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost { 146 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
155 AnalysisHost { db: RootDatabase::new(lru_capacity) } 147 AnalysisHost { db: RootDatabase::new(lru_capacity) }
@@ -470,20 +462,23 @@ impl Analysis {
470 self.with_db(|db| completion::completions(db, config, position).map(Into::into)) 462 self.with_db(|db| completion::completions(db, config, position).map(Into::into))
471 } 463 }
472 464
473 /// Computes assists (aka code actions aka intentions) for the given 465 /// Computes resolved assists with source changes for the given position.
466 pub fn resolved_assists(
467 &self,
468 config: &AssistConfig,
469 frange: FileRange,
470 ) -> Cancelable<Vec<ResolvedAssist>> {
471 self.with_db(|db| ra_assists::Assist::resolved(db, config, frange))
472 }
473
474 /// Computes unresolved assists (aka code actions aka intentions) for the given
474 /// position. 475 /// position.
475 pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> { 476 pub fn unresolved_assists(
476 self.with_db(|db| { 477 &self,
477 ra_assists::Assist::resolved(db, config, frange) 478 config: &AssistConfig,
478 .into_iter() 479 frange: FileRange,
479 .map(|assist| Assist { 480 ) -> Cancelable<Vec<Assist>> {
480 id: assist.assist.id, 481 self.with_db(|db| Assist::unresolved(db, config, frange))
481 label: assist.assist.label,
482 group_label: assist.assist.group.map(|it| it.0),
483 source_change: assist.source_change,
484 })
485 .collect()
486 })
487 } 482 }
488 483
489 /// Computes the set of diagnostics for the given file. 484 /// Computes the set of diagnostics for the given file.
@@ -508,7 +503,7 @@ impl Analysis {
508 ) -> Cancelable<Result<SourceChange, SsrError>> { 503 ) -> Cancelable<Result<SourceChange, SsrError>> {
509 self.with_db(|db| { 504 self.with_db(|db| {
510 let edits = ssr::parse_search_replace(query, parse_only, db)?; 505 let edits = ssr::parse_search_replace(query, parse_only, db)?;
511 Ok(SourceChange::source_file_edits(edits)) 506 Ok(SourceChange::from(edits))
512 }) 507 })
513 } 508 }
514 509
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 28c6349b1..915d4f4d3 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -171,7 +171,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
171 ), 171 ),
172 }); 172 });
173 173
174 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) 174 Some(RangeInfo::new(range, SourceChange::from(edits)))
175} 175}
176 176
177fn text_edit_from_self_param( 177fn text_edit_from_self_param(
@@ -234,7 +234,7 @@ fn rename_self_to_param(
234 let range = ast::SelfParam::cast(self_token.parent()) 234 let range = ast::SelfParam::cast(self_token.parent())
235 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 235 .map_or(self_token.text_range(), |p| p.syntax().text_range());
236 236
237 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) 237 Some(RangeInfo::new(range, SourceChange::from(edits)))
238} 238}
239 239
240fn rename_reference( 240fn rename_reference(
@@ -253,7 +253,7 @@ fn rename_reference(
253 return None; 253 return None;
254 } 254 }
255 255
256 Some(RangeInfo::new(range, SourceChange::source_file_edits(edit))) 256 Some(RangeInfo::new(range, SourceChange::from(edit)))
257} 257}
258 258
259#[cfg(test)] 259#[cfg(test)]
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 286d45eee..fc57dc33d 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,31 +1,31 @@
1use std::fmt;
2
1use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; 3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
2use itertools::Itertools; 4use itertools::Itertools;
5use ra_cfg::CfgExpr;
3use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
4use ra_syntax::{ 7use ra_syntax::{
5 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, 8 ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner},
6 match_ast, SyntaxNode, TextRange, 9 match_ast, SyntaxNode,
7}; 10};
8 11
9use crate::FileId; 12use crate::{display::ToNav, FileId, NavigationTarget};
10use ast::DocCommentsOwner;
11use ra_cfg::CfgExpr;
12use std::fmt::Display;
13 13
14#[derive(Debug)] 14#[derive(Debug, Clone)]
15pub struct Runnable { 15pub struct Runnable {
16 pub range: TextRange, 16 pub nav: NavigationTarget,
17 pub kind: RunnableKind, 17 pub kind: RunnableKind,
18 pub cfg_exprs: Vec<CfgExpr>, 18 pub cfg_exprs: Vec<CfgExpr>,
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug, Clone)]
22pub enum TestId { 22pub enum TestId {
23 Name(String), 23 Name(String),
24 Path(String), 24 Path(String),
25} 25}
26 26
27impl Display for TestId { 27impl fmt::Display for TestId {
28 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 28 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29 match self { 29 match self {
30 TestId::Name(name) => write!(f, "{}", name), 30 TestId::Name(name) => write!(f, "{}", name),
31 TestId::Path(path) => write!(f, "{}", path), 31 TestId::Path(path) => write!(f, "{}", path),
@@ -33,7 +33,7 @@ impl Display for TestId {
33 } 33 }
34} 34}
35 35
36#[derive(Debug)] 36#[derive(Debug, Clone)]
37pub enum RunnableKind { 37pub enum RunnableKind {
38 Test { test_id: TestId, attr: TestAttr }, 38 Test { test_id: TestId, attr: TestAttr },
39 TestMod { path: String }, 39 TestMod { path: String },
@@ -42,6 +42,42 @@ pub enum RunnableKind {
42 Bin, 42 Bin,
43} 43}
44 44
45#[derive(Debug, Eq, PartialEq)]
46pub struct RunnableAction {
47 pub run_title: &'static str,
48 pub debugee: bool,
49}
50
51const TEST: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Test", debugee: true };
52const DOCTEST: RunnableAction =
53 RunnableAction { run_title: "▶\u{fe0e} Run Doctest", debugee: false };
54const BENCH: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Bench", debugee: true };
55const BIN: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run", debugee: true };
56
57impl Runnable {
58 // test package::module::testname
59 pub fn label(&self, target: Option<String>) -> String {
60 match &self.kind {
61 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
62 RunnableKind::TestMod { path } => format!("test-mod {}", path),
63 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
64 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
65 RunnableKind::Bin => {
66 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
67 }
68 }
69 }
70
71 pub fn action(&self) -> &'static RunnableAction {
72 match &self.kind {
73 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => &TEST,
74 RunnableKind::DocTest { .. } => &DOCTEST,
75 RunnableKind::Bench { .. } => &BENCH,
76 RunnableKind::Bin => &BIN,
77 }
78 }
79}
80
45// Feature: Run 81// Feature: Run
46// 82//
47// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor 83// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
@@ -59,7 +95,11 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
59 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() 95 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
60} 96}
61 97
62fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> { 98pub(crate) fn runnable(
99 sema: &Semantics<RootDatabase>,
100 item: SyntaxNode,
101 file_id: FileId,
102) -> Option<Runnable> {
63 match_ast! { 103 match_ast! {
64 match item { 104 match item {
65 ast::FnDef(it) => runnable_fn(sema, it, file_id), 105 ast::FnDef(it) => runnable_fn(sema, it, file_id),
@@ -131,10 +171,11 @@ fn runnable_fn(
131 let cfg_exprs = 171 let cfg_exprs =
132 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 172 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
133 173
134 Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) 174 let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def));
175 Some(Runnable { nav, kind, cfg_exprs })
135} 176}
136 177
137#[derive(Debug)] 178#[derive(Debug, Copy, Clone)]
138pub struct TestAttr { 179pub struct TestAttr {
139 pub ignore: bool, 180 pub ignore: bool,
140} 181}
@@ -183,7 +224,6 @@ fn runnable_mod(
183 if !has_test_function { 224 if !has_test_function {
184 return None; 225 return None;
185 } 226 }
186 let range = module.syntax().text_range();
187 let module_def = sema.to_def(&module)?; 227 let module_def = sema.to_def(&module)?;
188 228
189 let path = module_def 229 let path = module_def
@@ -197,7 +237,8 @@ fn runnable_mod(
197 let cfg_exprs = 237 let cfg_exprs =
198 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 238 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
199 239
200 Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) 240 let nav = module_def.to_nav(sema.db);
241 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
201} 242}
202 243
203#[cfg(test)] 244#[cfg(test)]
@@ -206,6 +247,15 @@ mod tests {
206 247
207 use crate::mock_analysis::analysis_and_position; 248 use crate::mock_analysis::analysis_and_position;
208 249
250 use super::{Runnable, RunnableAction, BENCH, BIN, DOCTEST, TEST};
251
252 fn assert_actions(runnables: &[Runnable], actions: &[&RunnableAction]) {
253 assert_eq!(
254 actions,
255 runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice()
256 );
257 }
258
209 #[test] 259 #[test]
210 fn test_runnables() { 260 fn test_runnables() {
211 let (analysis, pos) = analysis_and_position( 261 let (analysis, pos) = analysis_and_position(
@@ -220,6 +270,9 @@ mod tests {
220 #[test] 270 #[test]
221 #[ignore] 271 #[ignore]
222 fn test_foo() {} 272 fn test_foo() {}
273
274 #[bench]
275 fn bench() {}
223 "#, 276 "#,
224 ); 277 );
225 let runnables = analysis.runnables(pos.file_id).unwrap(); 278 let runnables = analysis.runnables(pos.file_id).unwrap();
@@ -227,12 +280,38 @@ mod tests {
227 @r###" 280 @r###"
228 [ 281 [
229 Runnable { 282 Runnable {
230 range: 1..21, 283 nav: NavigationTarget {
284 file_id: FileId(
285 1,
286 ),
287 full_range: 1..21,
288 name: "main",
289 kind: FN_DEF,
290 focus_range: Some(
291 12..16,
292 ),
293 container_name: None,
294 description: None,
295 docs: None,
296 },
231 kind: Bin, 297 kind: Bin,
232 cfg_exprs: [], 298 cfg_exprs: [],
233 }, 299 },
234 Runnable { 300 Runnable {
235 range: 22..46, 301 nav: NavigationTarget {
302 file_id: FileId(
303 1,
304 ),
305 full_range: 22..46,
306 name: "test_foo",
307 kind: FN_DEF,
308 focus_range: Some(
309 33..41,
310 ),
311 container_name: None,
312 description: None,
313 docs: None,
314 },
236 kind: Test { 315 kind: Test {
237 test_id: Path( 316 test_id: Path(
238 "test_foo", 317 "test_foo",
@@ -244,7 +323,20 @@ mod tests {
244 cfg_exprs: [], 323 cfg_exprs: [],
245 }, 324 },
246 Runnable { 325 Runnable {
247 range: 47..81, 326 nav: NavigationTarget {
327 file_id: FileId(
328 1,
329 ),
330 full_range: 47..81,
331 name: "test_foo",
332 kind: FN_DEF,
333 focus_range: Some(
334 68..76,
335 ),
336 container_name: None,
337 description: None,
338 docs: None,
339 },
248 kind: Test { 340 kind: Test {
249 test_id: Path( 341 test_id: Path(
250 "test_foo", 342 "test_foo",
@@ -255,9 +347,32 @@ mod tests {
255 }, 347 },
256 cfg_exprs: [], 348 cfg_exprs: [],
257 }, 349 },
350 Runnable {
351 nav: NavigationTarget {
352 file_id: FileId(
353 1,
354 ),
355 full_range: 82..104,
356 name: "bench",
357 kind: FN_DEF,
358 focus_range: Some(
359 94..99,
360 ),
361 container_name: None,
362 description: None,
363 docs: None,
364 },
365 kind: Bench {
366 test_id: Path(
367 "bench",
368 ),
369 },
370 cfg_exprs: [],
371 },
258 ] 372 ]
259 "### 373 "###
260 ); 374 );
375 assert_actions(&runnables, &[&BIN, &TEST, &TEST, &BENCH]);
261 } 376 }
262 377
263 #[test] 378 #[test]
@@ -279,12 +394,38 @@ mod tests {
279 @r###" 394 @r###"
280 [ 395 [
281 Runnable { 396 Runnable {
282 range: 1..21, 397 nav: NavigationTarget {
398 file_id: FileId(
399 1,
400 ),
401 full_range: 1..21,
402 name: "main",
403 kind: FN_DEF,
404 focus_range: Some(
405 12..16,
406 ),
407 container_name: None,
408 description: None,
409 docs: None,
410 },
283 kind: Bin, 411 kind: Bin,
284 cfg_exprs: [], 412 cfg_exprs: [],
285 }, 413 },
286 Runnable { 414 Runnable {
287 range: 22..64, 415 nav: NavigationTarget {
416 file_id: FileId(
417 1,
418 ),
419 full_range: 22..64,
420 name: "foo",
421 kind: FN_DEF,
422 focus_range: Some(
423 56..59,
424 ),
425 container_name: None,
426 description: None,
427 docs: None,
428 },
288 kind: DocTest { 429 kind: DocTest {
289 test_id: Path( 430 test_id: Path(
290 "foo", 431 "foo",
@@ -295,6 +436,7 @@ mod tests {
295 ] 436 ]
296 "### 437 "###
297 ); 438 );
439 assert_actions(&runnables, &[&BIN, &DOCTEST]);
298 } 440 }
299 441
300 #[test] 442 #[test]
@@ -319,12 +461,38 @@ mod tests {
319 @r###" 461 @r###"
320 [ 462 [
321 Runnable { 463 Runnable {
322 range: 1..21, 464 nav: NavigationTarget {
465 file_id: FileId(
466 1,
467 ),
468 full_range: 1..21,
469 name: "main",
470 kind: FN_DEF,
471 focus_range: Some(
472 12..16,
473 ),
474 container_name: None,
475 description: None,
476 docs: None,
477 },
323 kind: Bin, 478 kind: Bin,
324 cfg_exprs: [], 479 cfg_exprs: [],
325 }, 480 },
326 Runnable { 481 Runnable {
327 range: 51..105, 482 nav: NavigationTarget {
483 file_id: FileId(
484 1,
485 ),
486 full_range: 51..105,
487 name: "foo",
488 kind: FN_DEF,
489 focus_range: Some(
490 97..100,
491 ),
492 container_name: None,
493 description: None,
494 docs: None,
495 },
328 kind: DocTest { 496 kind: DocTest {
329 test_id: Path( 497 test_id: Path(
330 "Data::foo", 498 "Data::foo",
@@ -335,6 +503,7 @@ mod tests {
335 ] 503 ]
336 "### 504 "###
337 ); 505 );
506 assert_actions(&runnables, &[&BIN, &DOCTEST]);
338 } 507 }
339 508
340 #[test] 509 #[test]
@@ -354,14 +523,40 @@ mod tests {
354 @r###" 523 @r###"
355 [ 524 [
356 Runnable { 525 Runnable {
357 range: 1..59, 526 nav: NavigationTarget {
527 file_id: FileId(
528 1,
529 ),
530 full_range: 1..59,
531 name: "test_mod",
532 kind: MODULE,
533 focus_range: Some(
534 13..21,
535 ),
536 container_name: None,
537 description: None,
538 docs: None,
539 },
358 kind: TestMod { 540 kind: TestMod {
359 path: "test_mod", 541 path: "test_mod",
360 }, 542 },
361 cfg_exprs: [], 543 cfg_exprs: [],
362 }, 544 },
363 Runnable { 545 Runnable {
364 range: 28..57, 546 nav: NavigationTarget {
547 file_id: FileId(
548 1,
549 ),
550 full_range: 28..57,
551 name: "test_foo1",
552 kind: FN_DEF,
553 focus_range: Some(
554 43..52,
555 ),
556 container_name: None,
557 description: None,
558 docs: None,
559 },
365 kind: Test { 560 kind: Test {
366 test_id: Path( 561 test_id: Path(
367 "test_mod::test_foo1", 562 "test_mod::test_foo1",
@@ -375,6 +570,7 @@ mod tests {
375 ] 570 ]
376 "### 571 "###
377 ); 572 );
573 assert_actions(&runnables, &[&TEST, &TEST]);
378 } 574 }
379 575
380 #[test] 576 #[test]
@@ -396,14 +592,40 @@ mod tests {
396 @r###" 592 @r###"
397 [ 593 [
398 Runnable { 594 Runnable {
399 range: 23..85, 595 nav: NavigationTarget {
596 file_id: FileId(
597 1,
598 ),
599 full_range: 23..85,
600 name: "test_mod",
601 kind: MODULE,
602 focus_range: Some(
603 27..35,
604 ),
605 container_name: None,
606 description: None,
607 docs: None,
608 },
400 kind: TestMod { 609 kind: TestMod {
401 path: "foo::test_mod", 610 path: "foo::test_mod",
402 }, 611 },
403 cfg_exprs: [], 612 cfg_exprs: [],
404 }, 613 },
405 Runnable { 614 Runnable {
406 range: 46..79, 615 nav: NavigationTarget {
616 file_id: FileId(
617 1,
618 ),
619 full_range: 46..79,
620 name: "test_foo1",
621 kind: FN_DEF,
622 focus_range: Some(
623 65..74,
624 ),
625 container_name: None,
626 description: None,
627 docs: None,
628 },
407 kind: Test { 629 kind: Test {
408 test_id: Path( 630 test_id: Path(
409 "foo::test_mod::test_foo1", 631 "foo::test_mod::test_foo1",
@@ -417,6 +639,7 @@ mod tests {
417 ] 639 ]
418 "### 640 "###
419 ); 641 );
642 assert_actions(&runnables, &[&TEST, &TEST]);
420 } 643 }
421 644
422 #[test] 645 #[test]
@@ -440,14 +663,40 @@ mod tests {
440 @r###" 663 @r###"
441 [ 664 [
442 Runnable { 665 Runnable {
443 range: 41..115, 666 nav: NavigationTarget {
667 file_id: FileId(
668 1,
669 ),
670 full_range: 41..115,
671 name: "test_mod",
672 kind: MODULE,
673 focus_range: Some(
674 45..53,
675 ),
676 container_name: None,
677 description: None,
678 docs: None,
679 },
444 kind: TestMod { 680 kind: TestMod {
445 path: "foo::bar::test_mod", 681 path: "foo::bar::test_mod",
446 }, 682 },
447 cfg_exprs: [], 683 cfg_exprs: [],
448 }, 684 },
449 Runnable { 685 Runnable {
450 range: 68..105, 686 nav: NavigationTarget {
687 file_id: FileId(
688 1,
689 ),
690 full_range: 68..105,
691 name: "test_foo1",
692 kind: FN_DEF,
693 focus_range: Some(
694 91..100,
695 ),
696 container_name: None,
697 description: None,
698 docs: None,
699 },
451 kind: Test { 700 kind: Test {
452 test_id: Path( 701 test_id: Path(
453 "foo::bar::test_mod::test_foo1", 702 "foo::bar::test_mod::test_foo1",
@@ -461,6 +710,7 @@ mod tests {
461 ] 710 ]
462 "### 711 "###
463 ); 712 );
713 assert_actions(&runnables, &[&TEST, &TEST]);
464 } 714 }
465 715
466 #[test] 716 #[test]
@@ -479,7 +729,20 @@ mod tests {
479 @r###" 729 @r###"
480 [ 730 [
481 Runnable { 731 Runnable {
482 range: 1..58, 732 nav: NavigationTarget {
733 file_id: FileId(
734 1,
735 ),
736 full_range: 1..58,
737 name: "test_foo1",
738 kind: FN_DEF,
739 focus_range: Some(
740 44..53,
741 ),
742 container_name: None,
743 description: None,
744 docs: None,
745 },
483 kind: Test { 746 kind: Test {
484 test_id: Path( 747 test_id: Path(
485 "test_foo1", 748 "test_foo1",
@@ -498,6 +761,7 @@ mod tests {
498 ] 761 ]
499 "### 762 "###
500 ); 763 );
764 assert_actions(&runnables, &[&TEST]);
501 } 765 }
502 766
503 #[test] 767 #[test]
@@ -516,7 +780,20 @@ mod tests {
516 @r###" 780 @r###"
517 [ 781 [
518 Runnable { 782 Runnable {
519 range: 1..80, 783 nav: NavigationTarget {
784 file_id: FileId(
785 1,
786 ),
787 full_range: 1..80,
788 name: "test_foo1",
789 kind: FN_DEF,
790 focus_range: Some(
791 66..75,
792 ),
793 container_name: None,
794 description: None,
795 docs: None,
796 },
520 kind: Test { 797 kind: Test {
521 test_id: Path( 798 test_id: Path(
522 "test_foo1", 799 "test_foo1",
@@ -543,6 +820,7 @@ mod tests {
543 ] 820 ]
544 "### 821 "###
545 ); 822 );
823 assert_actions(&runnables, &[&TEST]);
546 } 824 }
547 825
548 #[test] 826 #[test]
diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/src/snapshots/highlight_doctest.html
new file mode 100644
index 000000000..0ae8c7efc
--- /dev/null
+++ b/crates/ra_ide/src/snapshots/highlight_doctest.html
@@ -0,0 +1,71 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
15.parameter { color: #94BFF3; }
16.text { color: #DCDCCC; }
17.type { color: #7CB8BB; }
18.builtin_type { color: #8CD0D3; }
19.type_param { color: #DFAF8F; }
20.attribute { color: #94BFF3; }
21.numeric_literal { color: #BFEBBF; }
22.bool_literal { color: #BFE6EB; }
23.macro { color: #94BFF3; }
24.module { color: #AFD8AF; }
25.variable { color: #DCDCCC; }
26.format_specifier { color: #CC696B; }
27.mutable { text-decoration: underline; }
28
29.keyword { color: #F0DFAF; font-weight: bold; }
30.keyword.unsafe { color: #BC8383; font-weight: bold; }
31.control { font-style: italic; }
32</style>
33<pre><code><span class="keyword">impl</span> <span class="unresolved_reference">Foo</span> {
34 <span class="comment">/// Constructs a new `Foo`.</span>
35 <span class="comment">///</span>
36 <span class="comment">/// # Examples</span>
37 <span class="comment">///</span>
38 <span class="comment">/// ```</span>
39 <span class="comment">/// #</span> <span class="attribute">#![</span><span class="function attribute">allow</span><span class="attribute">(unused_mut)]</span>
40 <span class="comment">/// </span><span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span>: <span class="unresolved_reference">Foo</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>();
41 <span class="comment">/// ```</span>
42 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span>() -&gt; <span class="unresolved_reference">Foo</span> {
43 <span class="unresolved_reference">Foo</span> { }
44 }
45
46 <span class="comment">/// `bar` method on `Foo`.</span>
47 <span class="comment">///</span>
48 <span class="comment">/// # Examples</span>
49 <span class="comment">///</span>
50 <span class="comment">/// ```</span>
51 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foo</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>();
52 <span class="comment">///</span>
53 <span class="comment">/// </span><span class="comment">// calls bar on foo</span>
54 <span class="comment">/// </span><span class="macro">assert!</span>(foo.bar());
55 <span class="comment">///</span>
56 <span class="comment">/// </span><span class="comment">/* multi-line
57 </span><span class="comment">/// </span><span class="comment"> comment */</span>
58 <span class="comment">///</span>
59 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">multi_line_string</span> = <span class="string_literal">"Foo
60 </span><span class="comment">/// </span><span class="string_literal"> bar
61 </span><span class="comment">/// </span><span class="string_literal"> "</span>;
62 <span class="comment">///</span>
63 <span class="comment">/// ```</span>
64 <span class="comment">///</span>
65 <span class="comment">/// ```</span>
66 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foobar</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>().<span class="unresolved_reference">bar</span>();
67 <span class="comment">/// ```</span>
68 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">bool</span> {
69 <span class="bool_literal">true</span>
70 }
71}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index 68fc589bc..dec06eb51 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -10,6 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
13.parameter { color: #94BFF3; } 15.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 16.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 17.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index 326744361..849eb3b73 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -10,6 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
13.parameter { color: #94BFF3; } 15.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 16.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 17.type { color: #7CB8BB; }
@@ -52,6 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
52 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span> 54 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span>
53 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span> 55 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span>
54 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span> 56 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span>
57 <span class="macro">println!</span>(<span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "{2}"</span>
55 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 58 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
56 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); 59 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
57 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); 60 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
@@ -61,7 +64,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
61 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 64 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
62 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 65 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
63 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); 66 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
64 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>); 67 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
65 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); 68 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
66 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>); 69 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>);
67 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>); 70 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/src/snapshots/highlight_unsafe.html
new file mode 100644
index 000000000..bd24e6e38
--- /dev/null
+++ b/crates/ra_ide/src/snapshots/highlight_unsafe.html
@@ -0,0 +1,49 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
15.parameter { color: #94BFF3; }
16.text { color: #DCDCCC; }
17.type { color: #7CB8BB; }
18.builtin_type { color: #8CD0D3; }
19.type_param { color: #DFAF8F; }
20.attribute { color: #94BFF3; }
21.numeric_literal { color: #BFEBBF; }
22.bool_literal { color: #BFE6EB; }
23.macro { color: #94BFF3; }
24.module { color: #AFD8AF; }
25.variable { color: #DCDCCC; }
26.format_specifier { color: #CC696B; }
27.mutable { text-decoration: underline; }
28
29.keyword { color: #F0DFAF; font-weight: bold; }
30.keyword.unsafe { color: #BC8383; font-weight: bold; }
31.control { font-style: italic; }
32</style>
33<pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span>() {}
34
35<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span>;
36
37<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> {
38 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span>(&<span class="self_keyword">self</span>) {}
39}
40
41<span class="keyword">fn</span> <span class="function declaration">main</span>() {
42 <span class="keyword">let</span> <span class="variable declaration">x</span> = &<span class="numeric_literal">5</span> <span class="keyword">as</span> *<span class="keyword">const</span> <span class="builtin_type">usize</span>;
43 <span class="keyword unsafe">unsafe</span> {
44 <span class="function unsafe">unsafe_fn</span>();
45 <span class="struct">HasUnsafeFn</span>.<span class="function unsafe">unsafe_method</span>();
46 <span class="keyword">let</span> <span class="variable declaration">y</span> = <span class="operator unsafe">*</span>(<span class="variable">x</span>);
47 <span class="keyword">let</span> <span class="variable declaration">z</span> = -<span class="variable">x</span>;
48 }
49}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 352e35095..33548d43c 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -10,6 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
13.parameter { color: #94BFF3; } 15.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 16.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 17.type { color: #7CB8BB; }
@@ -82,7 +84,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
82 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>; 84 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>;
83 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>; 85 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>;
84 86
85 <span class="variable mutable">y</span>; 87 <span class="keyword">let</span> <span class="struct">Foo</span> { <span class="field">x</span>: <span class="variable declaration">z</span>, <span class="field">y</span> } = <span class="struct">Foo</span> { <span class="field">x</span>: <span class="variable">z</span>, <span class="field">y</span> };
88
89 <span class="variable">y</span>;
86} 90}
87 91
88<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; { 92<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; {
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 2a0294f71..1ab06182c 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -10,6 +10,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.function.unsafe { color: #BC8383; }
14.operator.unsafe { color: #BC8383; }
13.parameter { color: #94BFF3; } 15.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 16.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 17.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0b53ebe69..ab45c364a 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,5 +1,6 @@
1mod tags; 1mod tags;
2mod html; 2mod html;
3mod injection;
3#[cfg(test)] 4#[cfg(test)]
4mod tests; 5mod tests;
5 6
@@ -10,14 +11,14 @@ use ra_ide_db::{
10}; 11};
11use ra_prof::profile; 12use ra_prof::profile;
12use ra_syntax::{ 13use ra_syntax::{
13 ast::{self, HasFormatSpecifier, HasQuotes, HasStringValue}, 14 ast::{self, HasFormatSpecifier},
14 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, 15 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement,
15 SyntaxKind::*, 16 SyntaxKind::*,
16 SyntaxToken, TextRange, WalkEvent, T, 17 TextRange, WalkEvent, T,
17}; 18};
18use rustc_hash::FxHashMap; 19use rustc_hash::FxHashMap;
19 20
20use crate::{call_info::ActiveParameter, Analysis, FileId}; 21use crate::FileId;
21 22
22use ast::FormatSpecifier; 23use ast::FormatSpecifier;
23pub(crate) use html::highlight_as_html; 24pub(crate) use html::highlight_as_html;
@@ -123,6 +124,23 @@ pub(crate) fn highlight(
123 _ => (), 124 _ => (),
124 } 125 }
125 126
127 // Check for Rust code in documentation
128 match &event {
129 WalkEvent::Leave(NodeOrToken::Node(node)) => {
130 if let Some((doctest, range_mapping, new_comments)) =
131 injection::extract_doc_comments(node)
132 {
133 injection::highlight_doc_comment(
134 doctest,
135 range_mapping,
136 new_comments,
137 &mut stack,
138 );
139 }
140 }
141 _ => (),
142 }
143
126 let element = match event { 144 let element = match event {
127 WalkEvent::Enter(it) => it, 145 WalkEvent::Enter(it) => it,
128 WalkEvent::Leave(_) => continue, 146 WalkEvent::Leave(_) => continue,
@@ -173,7 +191,7 @@ pub(crate) fn highlight(
173 191
174 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) { 192 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) {
175 let expanded = element_to_highlight.as_token().unwrap().clone(); 193 let expanded = element_to_highlight.as_token().unwrap().clone();
176 if highlight_injection(&mut stack, &sema, token, expanded).is_some() { 194 if injection::highlight_injection(&mut stack, &sema, token, expanded).is_some() {
177 continue; 195 continue;
178 } 196 }
179 } 197 }
@@ -259,9 +277,8 @@ impl HighlightedRangeStack {
259 let mut parent = prev.pop().unwrap(); 277 let mut parent = prev.pop().unwrap();
260 for ele in children { 278 for ele in children {
261 assert!(parent.range.contains_range(ele.range)); 279 assert!(parent.range.contains_range(ele.range));
262 let mut cloned = parent.clone(); 280
263 parent.range = TextRange::new(parent.range.start(), ele.range.start()); 281 let cloned = Self::intersect(&mut parent, &ele);
264 cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
265 if !parent.range.is_empty() { 282 if !parent.range.is_empty() {
266 prev.push(parent); 283 prev.push(parent);
267 } 284 }
@@ -274,6 +291,62 @@ impl HighlightedRangeStack {
274 } 291 }
275 } 292 }
276 293
294 /// Intersects the `HighlightedRange` `parent` with `child`.
295 /// `parent` is mutated in place, becoming the range before `child`.
296 /// Returns the range (of the same type as `parent`) *after* `child`.
297 fn intersect(parent: &mut HighlightedRange, child: &HighlightedRange) -> HighlightedRange {
298 assert!(parent.range.contains_range(child.range));
299
300 let mut cloned = parent.clone();
301 parent.range = TextRange::new(parent.range.start(), child.range.start());
302 cloned.range = TextRange::new(child.range.end(), cloned.range.end());
303
304 cloned
305 }
306
307 /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`)
308 /// can only modify the last range currently on the stack.
309 /// Can be used to do injections that span multiple ranges, like the
310 /// doctest injection below.
311 /// If `delete` is set to true, the parent range is deleted instead of
312 /// intersected.
313 ///
314 /// Note that `pop` can be simulated by `pop_and_inject(false)` but the
315 /// latter is computationally more expensive.
316 fn pop_and_inject(&mut self, delete: bool) {
317 let mut children = self.stack.pop().unwrap();
318 let prev = self.stack.last_mut().unwrap();
319 children.sort_by_key(|range| range.range.start());
320 prev.sort_by_key(|range| range.range.start());
321
322 for child in children {
323 if let Some(idx) =
324 prev.iter().position(|parent| parent.range.contains_range(child.range))
325 {
326 let cloned = Self::intersect(&mut prev[idx], &child);
327 let insert_idx = if delete || prev[idx].range.is_empty() {
328 prev.remove(idx);
329 idx
330 } else {
331 idx + 1
332 };
333 prev.insert(insert_idx, child);
334 if !delete && !cloned.range.is_empty() {
335 prev.insert(insert_idx + 1, cloned);
336 }
337 } else if let Some(_idx) =
338 prev.iter().position(|parent| parent.range.contains(child.range.start()))
339 {
340 unreachable!("child range should be completely contained in parent range");
341 } else {
342 let idx = prev
343 .binary_search_by_key(&child.range.start(), |range| range.range.start())
344 .unwrap_or_else(|x| x);
345 prev.insert(idx, child);
346 }
347 }
348 }
349
277 fn add(&mut self, range: HighlightedRange) { 350 fn add(&mut self, range: HighlightedRange) {
278 self.stack 351 self.stack
279 .last_mut() 352 .last_mut()
@@ -363,6 +436,7 @@ fn highlight_element(
363 highlight_name(db, def) | HighlightModifier::Definition 436 highlight_name(db, def) | HighlightModifier::Definition
364 } 437 }
365 Some(NameClass::ConstReference(def)) => highlight_name(db, def), 438 Some(NameClass::ConstReference(def)) => highlight_name(db, def),
439 Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(),
366 None => highlight_name_by_syntax(name) | HighlightModifier::Definition, 440 None => highlight_name_by_syntax(name) | HighlightModifier::Definition,
367 } 441 }
368 } 442 }
@@ -406,6 +480,19 @@ fn highlight_element(
406 _ => h, 480 _ => h,
407 } 481 }
408 } 482 }
483 T![*] => {
484 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
485
486 let expr = prefix_expr.expr()?;
487 let ty = sema.type_of_expr(&expr)?;
488 if !ty.is_raw_ptr() {
489 return None;
490 }
491
492 let mut h = Highlight::new(HighlightTag::Operator);
493 h |= HighlightModifier::Unsafe;
494 h
495 }
409 496
410 k if k.is_keyword() => { 497 k if k.is_keyword() => {
411 let h = Highlight::new(HighlightTag::Keyword); 498 let h = Highlight::new(HighlightTag::Keyword);
@@ -458,7 +545,13 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
458 Definition::Field(_) => HighlightTag::Field, 545 Definition::Field(_) => HighlightTag::Field,
459 Definition::ModuleDef(def) => match def { 546 Definition::ModuleDef(def) => match def {
460 hir::ModuleDef::Module(_) => HighlightTag::Module, 547 hir::ModuleDef::Module(_) => HighlightTag::Module,
461 hir::ModuleDef::Function(_) => HighlightTag::Function, 548 hir::ModuleDef::Function(func) => {
549 let mut h = HighlightTag::Function.into();
550 if func.is_unsafe(db) {
551 h |= HighlightModifier::Unsafe;
552 }
553 return h;
554 }
462 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 555 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
463 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, 556 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum,
464 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, 557 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union,
@@ -516,42 +609,3 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
516 609
517 tag.into() 610 tag.into()
518} 611}
519
520fn highlight_injection(
521 acc: &mut HighlightedRangeStack,
522 sema: &Semantics<RootDatabase>,
523 literal: ast::RawString,
524 expanded: SyntaxToken,
525) -> Option<()> {
526 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
527 if !active_parameter.name.starts_with("ra_fixture") {
528 return None;
529 }
530 let value = literal.value()?;
531 let (analysis, tmp_file_id) = Analysis::from_single_file(value);
532
533 if let Some(range) = literal.open_quote_text_range() {
534 acc.add(HighlightedRange {
535 range,
536 highlight: HighlightTag::StringLiteral.into(),
537 binding_hash: None,
538 })
539 }
540
541 for mut h in analysis.highlight(tmp_file_id).unwrap() {
542 if let Some(r) = literal.map_range_up(h.range) {
543 h.range = r;
544 acc.add(h)
545 }
546 }
547
548 if let Some(range) = literal.close_quote_text_range() {
549 acc.add(HighlightedRange {
550 range,
551 highlight: HighlightTag::StringLiteral.into(),
552 binding_hash: None,
553 })
554 }
555
556 Some(())
557}
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index edfe61f39..5bada6252 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -69,6 +69,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
69.string_literal { color: #CC9393; } 69.string_literal { color: #CC9393; }
70.field { color: #94BFF3; } 70.field { color: #94BFF3; }
71.function { color: #93E0E3; } 71.function { color: #93E0E3; }
72.function.unsafe { color: #BC8383; }
73.operator.unsafe { color: #BC8383; }
72.parameter { color: #94BFF3; } 74.parameter { color: #94BFF3; }
73.text { color: #DCDCCC; } 75.text { color: #DCDCCC; }
74.type { color: #7CB8BB; } 76.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
new file mode 100644
index 000000000..3575a0fc6
--- /dev/null
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -0,0 +1,168 @@
1//! Syntax highlighting injections such as highlighting of documentation tests.
2
3use std::{collections::BTreeMap, convert::TryFrom};
4
5use ast::{HasQuotes, HasStringValue};
6use hir::Semantics;
7use ra_syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
8use stdx::SepBy;
9
10use crate::{call_info::ActiveParameter, Analysis, HighlightTag, HighlightedRange, RootDatabase};
11
12use super::HighlightedRangeStack;
13
14pub(super) fn highlight_injection(
15 acc: &mut HighlightedRangeStack,
16 sema: &Semantics<RootDatabase>,
17 literal: ast::RawString,
18 expanded: SyntaxToken,
19) -> Option<()> {
20 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
21 if !active_parameter.name.starts_with("ra_fixture") {
22 return None;
23 }
24 let value = literal.value()?;
25 let (analysis, tmp_file_id) = Analysis::from_single_file(value);
26
27 if let Some(range) = literal.open_quote_text_range() {
28 acc.add(HighlightedRange {
29 range,
30 highlight: HighlightTag::StringLiteral.into(),
31 binding_hash: None,
32 })
33 }
34
35 for mut h in analysis.highlight(tmp_file_id).unwrap() {
36 if let Some(r) = literal.map_range_up(h.range) {
37 h.range = r;
38 acc.add(h)
39 }
40 }
41
42 if let Some(range) = literal.close_quote_text_range() {
43 acc.add(HighlightedRange {
44 range,
45 highlight: HighlightTag::StringLiteral.into(),
46 binding_hash: None,
47 })
48 }
49
50 Some(())
51}
52
53/// Mapping from extracted documentation code to original code
54type RangesMap = BTreeMap<TextSize, TextSize>;
55
56/// Extracts Rust code from documentation comments as well as a mapping from
57/// the extracted source code back to the original source ranges.
58/// Lastly, a vector of new comment highlight ranges (spanning only the
59/// comment prefix) is returned which is used in the syntax highlighting
60/// injection to replace the previous (line-spanning) comment ranges.
61pub(super) fn extract_doc_comments(
62 node: &SyntaxNode,
63) -> Option<(String, RangesMap, Vec<HighlightedRange>)> {
64 // wrap the doctest into function body to get correct syntax highlighting
65 let prefix = "fn doctest() {\n";
66 let suffix = "}\n";
67 // Mapping from extracted documentation code to original code
68 let mut range_mapping: RangesMap = BTreeMap::new();
69 let mut line_start = TextSize::try_from(prefix.len()).unwrap();
70 let mut is_doctest = false;
71 // Replace the original, line-spanning comment ranges by new, only comment-prefix
72 // spanning comment ranges.
73 let mut new_comments = Vec::new();
74 let doctest = node
75 .children_with_tokens()
76 .filter_map(|el| el.into_token().and_then(ast::Comment::cast))
77 .filter(|comment| comment.kind().doc.is_some())
78 .filter(|comment| {
79 if comment.text().contains("```") {
80 is_doctest = !is_doctest;
81 false
82 } else {
83 is_doctest
84 }
85 })
86 .map(|comment| {
87 let prefix_len = comment.prefix().len();
88 let line: &str = comment.text().as_str();
89 let range = comment.syntax().text_range();
90
91 // whitespace after comment is ignored
92 let pos = if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) {
93 prefix_len + ws.len_utf8()
94 } else {
95 prefix_len
96 };
97
98 // lines marked with `#` should be ignored in output, we skip the `#` char
99 let pos = if let Some(ws) = line.chars().nth(pos).filter(|&c| c == '#') {
100 pos + ws.len_utf8()
101 } else {
102 pos
103 };
104
105 range_mapping.insert(line_start, range.start() + TextSize::try_from(pos).unwrap());
106 new_comments.push(HighlightedRange {
107 range: TextRange::new(
108 range.start(),
109 range.start() + TextSize::try_from(pos).unwrap(),
110 ),
111 highlight: HighlightTag::Comment.into(),
112 binding_hash: None,
113 });
114 line_start += range.len() - TextSize::try_from(pos).unwrap();
115 line_start += TextSize::try_from('\n'.len_utf8()).unwrap();
116
117 line[pos..].to_owned()
118 })
119 .sep_by("\n")
120 .to_string();
121
122 if doctest.is_empty() {
123 return None;
124 }
125
126 let doctest = format!("{}{}{}", prefix, doctest, suffix);
127 Some((doctest, range_mapping, new_comments))
128}
129
130/// Injection of syntax highlighting of doctests.
131pub(super) fn highlight_doc_comment(
132 text: String,
133 range_mapping: RangesMap,
134 new_comments: Vec<HighlightedRange>,
135 stack: &mut HighlightedRangeStack,
136) {
137 let (analysis, tmp_file_id) = Analysis::from_single_file(text);
138
139 stack.push();
140 for mut h in analysis.highlight(tmp_file_id).unwrap() {
141 // Determine start offset and end offset in case of multi-line ranges
142 let mut start_offset = None;
143 let mut end_offset = None;
144 for (line_start, orig_line_start) in range_mapping.range(..h.range.end()).rev() {
145 if line_start <= &h.range.start() {
146 start_offset.get_or_insert(orig_line_start - line_start);
147 break;
148 } else {
149 end_offset.get_or_insert(orig_line_start - line_start);
150 }
151 }
152 if let Some(start_offset) = start_offset {
153 h.range = TextRange::new(
154 h.range.start() + start_offset,
155 h.range.end() + end_offset.unwrap_or(start_offset),
156 );
157 stack.add(h);
158 }
159 }
160
161 // Inject the comment prefix highlight ranges
162 stack.push();
163 for comment in new_comments {
164 stack.add(comment);
165 }
166 stack.pop_and_inject(false);
167 stack.pop_and_inject(true);
168}
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 1514531de..94f466966 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -24,12 +24,14 @@ pub enum HighlightTag {
24 Enum, 24 Enum,
25 EnumVariant, 25 EnumVariant,
26 Field, 26 Field,
27 FormatSpecifier,
27 Function, 28 Function,
28 Keyword, 29 Keyword,
29 Lifetime, 30 Lifetime,
30 Macro, 31 Macro,
31 Module, 32 Module,
32 NumericLiteral, 33 NumericLiteral,
34 Operator,
33 SelfKeyword, 35 SelfKeyword,
34 SelfType, 36 SelfType,
35 Static, 37 Static,
@@ -41,8 +43,6 @@ pub enum HighlightTag {
41 Union, 43 Union,
42 Local, 44 Local,
43 UnresolvedReference, 45 UnresolvedReference,
44 FormatSpecifier,
45 Operator,
46} 46}
47 47
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -72,12 +72,14 @@ impl HighlightTag {
72 HighlightTag::Enum => "enum", 72 HighlightTag::Enum => "enum",
73 HighlightTag::EnumVariant => "enum_variant", 73 HighlightTag::EnumVariant => "enum_variant",
74 HighlightTag::Field => "field", 74 HighlightTag::Field => "field",
75 HighlightTag::FormatSpecifier => "format_specifier",
75 HighlightTag::Function => "function", 76 HighlightTag::Function => "function",
76 HighlightTag::Keyword => "keyword", 77 HighlightTag::Keyword => "keyword",
77 HighlightTag::Lifetime => "lifetime", 78 HighlightTag::Lifetime => "lifetime",
78 HighlightTag::Macro => "macro", 79 HighlightTag::Macro => "macro",
79 HighlightTag::Module => "module", 80 HighlightTag::Module => "module",
80 HighlightTag::NumericLiteral => "numeric_literal", 81 HighlightTag::NumericLiteral => "numeric_literal",
82 HighlightTag::Operator => "operator",
81 HighlightTag::SelfKeyword => "self_keyword", 83 HighlightTag::SelfKeyword => "self_keyword",
82 HighlightTag::SelfType => "self_type", 84 HighlightTag::SelfType => "self_type",
83 HighlightTag::Static => "static", 85 HighlightTag::Static => "static",
@@ -89,8 +91,6 @@ impl HighlightTag {
89 HighlightTag::Union => "union", 91 HighlightTag::Union => "union",
90 HighlightTag::Local => "variable", 92 HighlightTag::Local => "variable",
91 HighlightTag::UnresolvedReference => "unresolved_reference", 93 HighlightTag::UnresolvedReference => "unresolved_reference",
92 HighlightTag::FormatSpecifier => "format_specifier",
93 HighlightTag::Operator => "operator",
94 } 94 }
95 } 95 }
96} 96}
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index eb43a23da..949bf59a0 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -9,7 +9,7 @@ use crate::{
9 9
10#[test] 10#[test]
11fn test_highlighting() { 11fn test_highlighting() {
12 let (analysis, file_id) = single_file( 12 check_highlighting(
13 r#" 13 r#"
14#[derive(Clone, Debug)] 14#[derive(Clone, Debug)]
15struct Foo { 15struct Foo {
@@ -65,6 +65,8 @@ fn main() {
65 let y = &mut x; 65 let y = &mut x;
66 let z = &y; 66 let z = &y;
67 67
68 let Foo { x: z, y } = Foo { x: z, y };
69
68 y; 70 y;
69} 71}
70 72
@@ -84,17 +86,14 @@ impl<T> Option<T> {
84} 86}
85"# 87"#
86 .trim(), 88 .trim(),
89 "crates/ra_ide/src/snapshots/highlighting.html",
90 false,
87 ); 91 );
88 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html");
89 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
90 let expected_html = &read_text(&dst_file);
91 fs::write(dst_file, &actual_html).unwrap();
92 assert_eq_text!(expected_html, actual_html);
93} 92}
94 93
95#[test] 94#[test]
96fn test_rainbow_highlighting() { 95fn test_rainbow_highlighting() {
97 let (analysis, file_id) = single_file( 96 check_highlighting(
98 r#" 97 r#"
99fn main() { 98fn main() {
100 let hello = "hello"; 99 let hello = "hello";
@@ -110,12 +109,9 @@ fn bar() {
110} 109}
111"# 110"#
112 .trim(), 111 .trim(),
112 "crates/ra_ide/src/snapshots/rainbow_highlighting.html",
113 true,
113 ); 114 );
114 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/rainbow_highlighting.html");
115 let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
116 let expected_html = &read_text(&dst_file);
117 fs::write(dst_file, &actual_html).unwrap();
118 assert_eq_text!(expected_html, actual_html);
119} 115}
120 116
121#[test] 117#[test]
@@ -153,7 +149,7 @@ fn test_ranges() {
153 149
154#[test] 150#[test]
155fn test_flattening() { 151fn test_flattening() {
156 let (analysis, file_id) = single_file( 152 check_highlighting(
157 r##" 153 r##"
158fn fixture(ra_fixture: &str) {} 154fn fixture(ra_fixture: &str) {}
159 155
@@ -167,13 +163,9 @@ fn main() {
167 ); 163 );
168}"## 164}"##
169 .trim(), 165 .trim(),
166 "crates/ra_ide/src/snapshots/highlight_injection.html",
167 false,
170 ); 168 );
171
172 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_injection.html");
173 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
174 let expected_html = &read_text(&dst_file);
175 fs::write(dst_file, &actual_html).unwrap();
176 assert_eq_text!(expected_html, actual_html);
177} 169}
178 170
179#[test] 171#[test]
@@ -192,7 +184,7 @@ macro_rules! test {}
192fn test_string_highlighting() { 184fn test_string_highlighting() {
193 // The format string detection is based on macro-expansion, 185 // The format string detection is based on macro-expansion,
194 // thus, we have to copy the macro definition from `std` 186 // thus, we have to copy the macro definition from `std`
195 let (analysis, file_id) = single_file( 187 check_highlighting(
196 r#" 188 r#"
197macro_rules! println { 189macro_rules! println {
198 ($($arg:tt)*) => ({ 190 ($($arg:tt)*) => ({
@@ -218,6 +210,7 @@ fn main() {
218 println!("{argument}", argument = "test"); // => "test" 210 println!("{argument}", argument = "test"); // => "test"
219 println!("{name} {}", 1, name = 2); // => "2 1" 211 println!("{name} {}", 1, name = 2); // => "2 1"
220 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" 212 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b"
213 println!("{{{}}}", 2); // => "{2}"
221 println!("Hello {:5}!", "x"); 214 println!("Hello {:5}!", "x");
222 println!("Hello {:1$}!", "x", 5); 215 println!("Hello {:1$}!", "x", 5);
223 println!("Hello {1:0$}!", 5, "x"); 216 println!("Hello {1:0$}!", 5, "x");
@@ -249,10 +242,96 @@ fn main() {
249 println!("{ничоси}", ничоси = 92); 242 println!("{ничоси}", ничоси = 92);
250}"# 243}"#
251 .trim(), 244 .trim(),
245 "crates/ra_ide/src/snapshots/highlight_strings.html",
246 false,
252 ); 247 );
248}
249
250#[test]
251fn test_unsafe_highlighting() {
252 check_highlighting(
253 r#"
254unsafe fn unsafe_fn() {}
255
256struct HasUnsafeFn;
257
258impl HasUnsafeFn {
259 unsafe fn unsafe_method(&self) {}
260}
261
262fn main() {
263 let x = &5 as *const usize;
264 unsafe {
265 unsafe_fn();
266 HasUnsafeFn.unsafe_method();
267 let y = *(x);
268 let z = -x;
269 }
270}
271"#
272 .trim(),
273 "crates/ra_ide/src/snapshots/highlight_unsafe.html",
274 false,
275 );
276}
277
278#[test]
279fn test_highlight_doctest() {
280 check_highlighting(
281 r#"
282impl Foo {
283 /// Constructs a new `Foo`.
284 ///
285 /// # Examples
286 ///
287 /// ```
288 /// # #![allow(unused_mut)]
289 /// let mut foo: Foo = Foo::new();
290 /// ```
291 pub const fn new() -> Foo {
292 Foo { }
293 }
294
295 /// `bar` method on `Foo`.
296 ///
297 /// # Examples
298 ///
299 /// ```
300 /// let foo = Foo::new();
301 ///
302 /// // calls bar on foo
303 /// assert!(foo.bar());
304 ///
305 /// /* multi-line
306 /// comment */
307 ///
308 /// let multi_line_string = "Foo
309 /// bar
310 /// ";
311 ///
312 /// ```
313 ///
314 /// ```
315 /// let foobar = Foo::new().bar();
316 /// ```
317 pub fn foo(&self) -> bool {
318 true
319 }
320}
321"#
322 .trim(),
323 "crates/ra_ide/src/snapshots/highlight_doctest.html",
324 false,
325 )
326}
253 327
254 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_strings.html"); 328/// Highlights the code given by the `ra_fixture` argument, renders the
255 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); 329/// result as HTML, and compares it with the HTML file given as `snapshot`.
330/// Note that the `snapshot` file is overwritten by the rendered HTML.
331fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) {
332 let (analysis, file_id) = single_file(ra_fixture);
333 let dst_file = project_dir().join(snapshot);
334 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
256 let expected_html = &read_text(&dst_file); 335 let expected_html = &read_text(&dst_file);
257 fs::write(dst_file, &actual_html).unwrap(); 336 fs::write(dst_file, &actual_html).unwrap();
258 assert_eq_text!(expected_html, actual_html); 337 assert_eq_text!(expected_html, actual_html);
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 67e2c33a0..83776d2b6 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -17,11 +17,13 @@ mod on_enter;
17 17
18use ra_db::{FilePosition, SourceDatabase}; 18use ra_db::{FilePosition, SourceDatabase};
19use ra_fmt::leading_indent; 19use ra_fmt::leading_indent;
20use ra_ide_db::RootDatabase; 20use ra_ide_db::{source_change::SourceFileEdit, RootDatabase};
21use ra_syntax::{ 21use ra_syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, AstToken}, 23 ast::{self, AstToken},
24 AstNode, SourceFile, TextRange, TextSize, 24 AstNode, SourceFile,
25 SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR},
26 TextRange, TextSize,
25}; 27};
26 28
27use ra_text_edit::TextEdit; 29use ra_text_edit::TextEdit;
@@ -47,8 +49,8 @@ pub(crate) fn on_char_typed(
47 assert!(TRIGGER_CHARS.contains(char_typed)); 49 assert!(TRIGGER_CHARS.contains(char_typed));
48 let file = &db.parse(position.file_id).tree(); 50 let file = &db.parse(position.file_id).tree();
49 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); 51 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
50 let text_edit = on_char_typed_inner(file, position.offset, char_typed)?; 52 let edit = on_char_typed_inner(file, position.offset, char_typed)?;
51 Some(SourceChange::source_file_edit_from(position.file_id, text_edit)) 53 Some(SourceFileEdit { file_id: position.file_id, edit }.into())
52} 54}
53 55
54fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { 56fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> {
@@ -98,9 +100,12 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
98 }; 100 };
99 let current_indent_len = TextSize::of(current_indent); 101 let current_indent_len = TextSize::of(current_indent);
100 102
103 let parent = whitespace.syntax().parent();
101 // Make sure dot is a part of call chain 104 // Make sure dot is a part of call chain
102 let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?; 105 if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) {
103 let prev_indent = leading_indent(field_expr.syntax())?; 106 return None;
107 }
108 let prev_indent = leading_indent(&parent)?;
104 let target_indent = format!(" {}", prev_indent); 109 let target_indent = format!(" {}", prev_indent);
105 let target_indent_len = TextSize::of(&target_indent); 110 let target_indent_len = TextSize::of(&target_indent);
106 if current_indent_len == target_indent_len { 111 if current_indent_len == target_indent_len {
@@ -143,11 +148,11 @@ mod tests {
143 }) 148 })
144 } 149 }
145 150
146 fn type_char(char_typed: char, before: &str, after: &str) { 151 fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
147 let actual = do_type_char(char_typed, before) 152 let actual = do_type_char(char_typed, ra_fixture_before)
148 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); 153 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
149 154
150 assert_eq_text!(after, &actual); 155 assert_eq_text!(ra_fixture_after, &actual);
151 } 156 }
152 157
153 fn type_char_noop(char_typed: char, before: &str) { 158 fn type_char_noop(char_typed: char, before: &str) {
@@ -249,6 +254,27 @@ fn foo() {
249 } 254 }
250 255
251 #[test] 256 #[test]
257 fn indents_new_chain_call_with_let() {
258 type_char(
259 '.',
260 r#"
261fn main() {
262 let _ = foo
263 <|>
264 bar()
265}
266"#,
267 r#"
268fn main() {
269 let _ = foo
270 .
271 bar()
272}
273"#,
274 );
275 }
276
277 #[test]
252 fn indents_continued_chain_call() { 278 fn indents_continued_chain_call() {
253 type_char( 279 type_char(
254 '.', 280 '.',
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs
index 8446ef88e..2fc796a85 100644
--- a/crates/ra_ide_db/src/change.rs
+++ b/crates/ra_ide_db/src/change.rs
@@ -16,7 +16,7 @@ use rustc_hash::FxHashMap;
16 16
17use crate::{ 17use crate::{
18 symbol_index::{SymbolIndex, SymbolsDatabase}, 18 symbol_index::{SymbolIndex, SymbolsDatabase},
19 DebugData, RootDatabase, 19 RootDatabase,
20}; 20};
21 21
22#[derive(Default)] 22#[derive(Default)]
@@ -26,7 +26,6 @@ pub struct AnalysisChange {
26 files_changed: Vec<(FileId, Arc<String>)>, 26 files_changed: Vec<(FileId, Arc<String>)>,
27 libraries_added: Vec<LibraryData>, 27 libraries_added: Vec<LibraryData>,
28 crate_graph: Option<CrateGraph>, 28 crate_graph: Option<CrateGraph>,
29 debug_data: DebugData,
30} 29}
31 30
32impl fmt::Debug for AnalysisChange { 31impl fmt::Debug for AnalysisChange {
@@ -87,10 +86,6 @@ impl AnalysisChange {
87 pub fn set_crate_graph(&mut self, graph: CrateGraph) { 86 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
88 self.crate_graph = Some(graph); 87 self.crate_graph = Some(graph);
89 } 88 }
90
91 pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) {
92 self.debug_data.root_paths.insert(source_root_id, path);
93 }
94} 89}
95 90
96#[derive(Debug)] 91#[derive(Debug)]
@@ -218,8 +213,6 @@ impl RootDatabase {
218 if let Some(crate_graph) = change.crate_graph { 213 if let Some(crate_graph) = change.crate_graph {
219 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) 214 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
220 } 215 }
221
222 Arc::make_mut(&mut self.debug_data).merge(change.debug_data)
223 } 216 }
224 217
225 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { 218 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
@@ -334,6 +327,7 @@ impl RootDatabase {
334 hir::db::CrateLangItemsQuery 327 hir::db::CrateLangItemsQuery
335 hir::db::LangItemQuery 328 hir::db::LangItemQuery
336 hir::db::DocumentationQuery 329 hir::db::DocumentationQuery
330 hir::db::ImportMapQuery
337 331
338 // InternDatabase 332 // InternDatabase
339 hir::db::InternFunctionQuery 333 hir::db::InternFunctionQuery
@@ -369,6 +363,7 @@ impl RootDatabase {
369 hir::db::ImplDatumQuery 363 hir::db::ImplDatumQuery
370 hir::db::AssociatedTyValueQuery 364 hir::db::AssociatedTyValueQuery
371 hir::db::TraitSolveQuery 365 hir::db::TraitSolveQuery
366 hir::db::ReturnTypeImplTraitsQuery
372 367
373 // SymbolsDatabase 368 // SymbolsDatabase
374 crate::symbol_index::FileSymbolsQuery 369 crate::symbol_index::FileSymbolsQuery
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index 8b06cbfc5..3ef5e74b6 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -18,7 +18,7 @@ use ra_syntax::{
18use crate::RootDatabase; 18use crate::RootDatabase;
19 19
20// FIXME: a more precise name would probably be `Symbol`? 20// FIXME: a more precise name would probably be `Symbol`?
21#[derive(Debug, PartialEq, Eq)] 21#[derive(Debug, PartialEq, Eq, Copy, Clone)]
22pub enum Definition { 22pub enum Definition {
23 Macro(MacroDef), 23 Macro(MacroDef),
24 Field(Field), 24 Field(Field),
@@ -78,10 +78,15 @@ impl Definition {
78 } 78 }
79} 79}
80 80
81#[derive(Debug)]
81pub enum NameClass { 82pub enum NameClass {
82 Definition(Definition), 83 Definition(Definition),
83 /// `None` in `if let None = Some(82) {}` 84 /// `None` in `if let None = Some(82) {}`
84 ConstReference(Definition), 85 ConstReference(Definition),
86 FieldShorthand {
87 local: Local,
88 field: Definition,
89 },
85} 90}
86 91
87impl NameClass { 92impl NameClass {
@@ -89,12 +94,14 @@ impl NameClass {
89 match self { 94 match self {
90 NameClass::Definition(it) => Some(it), 95 NameClass::Definition(it) => Some(it),
91 NameClass::ConstReference(_) => None, 96 NameClass::ConstReference(_) => None,
97 NameClass::FieldShorthand { local, field: _ } => Some(Definition::Local(local)),
92 } 98 }
93 } 99 }
94 100
95 pub fn definition(self) -> Definition { 101 pub fn definition(self) -> Definition {
96 match self { 102 match self {
97 NameClass::Definition(it) | NameClass::ConstReference(it) => it, 103 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
104 NameClass::FieldShorthand { local: _, field } => field,
98 } 105 }
99 } 106 }
100} 107}
@@ -102,18 +109,14 @@ impl NameClass {
102pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> { 109pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
103 let _p = profile("classify_name"); 110 let _p = profile("classify_name");
104 111
105 if let Some(bind_pat) = name.syntax().parent().and_then(ast::BindPat::cast) { 112 let parent = name.syntax().parent()?;
113
114 if let Some(bind_pat) = ast::BindPat::cast(parent.clone()) {
106 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) { 115 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
107 return Some(NameClass::ConstReference(Definition::ModuleDef(def))); 116 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
108 } 117 }
109 } 118 }
110 119
111 classify_name_inner(sema, name).map(NameClass::Definition)
112}
113
114fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<Definition> {
115 let parent = name.syntax().parent()?;
116
117 match_ast! { 120 match_ast! {
118 match parent { 121 match parent {
119 ast::Alias(it) => { 122 ast::Alias(it) => {
@@ -123,63 +126,73 @@ fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Opti
123 let name_ref = path_segment.name_ref()?; 126 let name_ref = path_segment.name_ref()?;
124 let name_ref_class = classify_name_ref(sema, &name_ref)?; 127 let name_ref_class = classify_name_ref(sema, &name_ref)?;
125 128
126 Some(name_ref_class.definition()) 129 Some(NameClass::Definition(name_ref_class.definition()))
127 }, 130 },
128 ast::BindPat(it) => { 131 ast::BindPat(it) => {
129 let local = sema.to_def(&it)?; 132 let local = sema.to_def(&it)?;
130 Some(Definition::Local(local)) 133
134 if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) {
135 if record_field_pat.name_ref().is_none() {
136 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
137 let field = Definition::Field(field);
138 return Some(NameClass::FieldShorthand { local, field });
139 }
140 }
141 }
142
143 Some(NameClass::Definition(Definition::Local(local)))
131 }, 144 },
132 ast::RecordFieldDef(it) => { 145 ast::RecordFieldDef(it) => {
133 let field: hir::Field = sema.to_def(&it)?; 146 let field: hir::Field = sema.to_def(&it)?;
134 Some(Definition::Field(field)) 147 Some(NameClass::Definition(Definition::Field(field)))
135 }, 148 },
136 ast::Module(it) => { 149 ast::Module(it) => {
137 let def = sema.to_def(&it)?; 150 let def = sema.to_def(&it)?;
138 Some(Definition::ModuleDef(def.into())) 151 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
139 }, 152 },
140 ast::StructDef(it) => { 153 ast::StructDef(it) => {
141 let def: hir::Struct = sema.to_def(&it)?; 154 let def: hir::Struct = sema.to_def(&it)?;
142 Some(Definition::ModuleDef(def.into())) 155 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
143 }, 156 },
144 ast::UnionDef(it) => { 157 ast::UnionDef(it) => {
145 let def: hir::Union = sema.to_def(&it)?; 158 let def: hir::Union = sema.to_def(&it)?;
146 Some(Definition::ModuleDef(def.into())) 159 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
147 }, 160 },
148 ast::EnumDef(it) => { 161 ast::EnumDef(it) => {
149 let def: hir::Enum = sema.to_def(&it)?; 162 let def: hir::Enum = sema.to_def(&it)?;
150 Some(Definition::ModuleDef(def.into())) 163 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
151 }, 164 },
152 ast::TraitDef(it) => { 165 ast::TraitDef(it) => {
153 let def: hir::Trait = sema.to_def(&it)?; 166 let def: hir::Trait = sema.to_def(&it)?;
154 Some(Definition::ModuleDef(def.into())) 167 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
155 }, 168 },
156 ast::StaticDef(it) => { 169 ast::StaticDef(it) => {
157 let def: hir::Static = sema.to_def(&it)?; 170 let def: hir::Static = sema.to_def(&it)?;
158 Some(Definition::ModuleDef(def.into())) 171 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
159 }, 172 },
160 ast::EnumVariant(it) => { 173 ast::EnumVariant(it) => {
161 let def: hir::EnumVariant = sema.to_def(&it)?; 174 let def: hir::EnumVariant = sema.to_def(&it)?;
162 Some(Definition::ModuleDef(def.into())) 175 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
163 }, 176 },
164 ast::FnDef(it) => { 177 ast::FnDef(it) => {
165 let def: hir::Function = sema.to_def(&it)?; 178 let def: hir::Function = sema.to_def(&it)?;
166 Some(Definition::ModuleDef(def.into())) 179 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
167 }, 180 },
168 ast::ConstDef(it) => { 181 ast::ConstDef(it) => {
169 let def: hir::Const = sema.to_def(&it)?; 182 let def: hir::Const = sema.to_def(&it)?;
170 Some(Definition::ModuleDef(def.into())) 183 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
171 }, 184 },
172 ast::TypeAliasDef(it) => { 185 ast::TypeAliasDef(it) => {
173 let def: hir::TypeAlias = sema.to_def(&it)?; 186 let def: hir::TypeAlias = sema.to_def(&it)?;
174 Some(Definition::ModuleDef(def.into())) 187 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
175 }, 188 },
176 ast::MacroCall(it) => { 189 ast::MacroCall(it) => {
177 let def = sema.to_def(&it)?; 190 let def = sema.to_def(&it)?;
178 Some(Definition::Macro(def)) 191 Some(NameClass::Definition(Definition::Macro(def)))
179 }, 192 },
180 ast::TypeParam(it) => { 193 ast::TypeParam(it) => {
181 let def = sema.to_def(&it)?; 194 let def = sema.to_def(&it)?;
182 Some(Definition::TypeParam(def)) 195 Some(NameClass::Definition(Definition::TypeParam(def)))
183 }, 196 },
184 _ => None, 197 _ => None,
185 } 198 }
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs
index bf0d8db60..fff112e66 100644
--- a/crates/ra_ide_db/src/imports_locator.rs
+++ b/crates/ra_ide_db/src/imports_locator.rs
@@ -1,7 +1,7 @@
1//! This module contains an import search funcionality that is provided to the ra_assists module. 1//! This module contains an import search funcionality that is provided to the ra_assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
3 3
4use hir::{MacroDef, ModuleDef, Semantics}; 4use hir::{Crate, MacroDef, ModuleDef, Semantics};
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; 6use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
7 7
@@ -11,44 +11,46 @@ use crate::{
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use either::Either; 13use either::Either;
14use rustc_hash::FxHashSet;
14 15
15pub struct ImportsLocator<'a> { 16pub struct ImportsLocator<'a> {
16 sema: Semantics<'a, RootDatabase>, 17 sema: Semantics<'a, RootDatabase>,
18 krate: Crate,
17} 19}
18 20
19impl<'a> ImportsLocator<'a> { 21impl<'a> ImportsLocator<'a> {
20 pub fn new(db: &'a RootDatabase) -> Self { 22 pub fn new(db: &'a RootDatabase, krate: Crate) -> Self {
21 Self { sema: Semantics::new(db) } 23 Self { sema: Semantics::new(db), krate }
22 } 24 }
23 25
24 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> { 26 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> {
25 let _p = profile("search_for_imports"); 27 let _p = profile("search_for_imports");
26 let db = self.sema.db; 28 let db = self.sema.db;
27 29
28 let project_results = { 30 // Query dependencies first.
29 let mut query = Query::new(name_to_import.to_string()); 31 let mut candidates: FxHashSet<_> =
30 query.exact(); 32 self.krate.query_external_importables(db, name_to_import).collect();
31 query.limit(40); 33
32 symbol_index::world_symbols(db, query) 34 // Query the local crate using the symbol index.
33 }; 35 let local_results = {
34 let lib_results = {
35 let mut query = Query::new(name_to_import.to_string()); 36 let mut query = Query::new(name_to_import.to_string());
36 query.libs();
37 query.exact(); 37 query.exact();
38 query.limit(40); 38 query.limit(40);
39 symbol_index::world_symbols(db, query) 39 symbol_index::crate_symbols(db, self.krate.into(), query)
40 }; 40 };
41 41
42 project_results 42 candidates.extend(
43 .into_iter() 43 local_results
44 .chain(lib_results.into_iter()) 44 .into_iter()
45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) 45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate))
46 .filter_map(|name_definition_to_import| match name_definition_to_import { 46 .filter_map(|name_definition_to_import| match name_definition_to_import {
47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), 47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)), 48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
49 _ => None, 49 _ => None,
50 }) 50 }),
51 .collect() 51 );
52
53 candidates.into_iter().collect()
52 } 54 }
53 55
54 fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> { 56 fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> {
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 1b74e6558..a808de4f1 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -16,10 +16,10 @@ use std::sync::Arc;
16use hir::db::{AstDatabase, DefDatabase}; 16use hir::db::{AstDatabase, DefDatabase};
17use ra_db::{ 17use ra_db::{
18 salsa::{self, Database, Durability}, 18 salsa::{self, Database, Durability},
19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, 19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase,
20 SourceDatabase, SourceRootId, Upcast, 20 Upcast,
21}; 21};
22use rustc_hash::FxHashMap; 22use rustc_hash::FxHashSet;
23 23
24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; 24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
25 25
@@ -36,7 +36,6 @@ use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
36#[derive(Debug)] 36#[derive(Debug)]
37pub struct RootDatabase { 37pub struct RootDatabase {
38 runtime: salsa::Runtime<RootDatabase>, 38 runtime: salsa::Runtime<RootDatabase>,
39 pub(crate) debug_data: Arc<DebugData>,
40 pub last_gc: crate::wasm_shims::Instant, 39 pub last_gc: crate::wasm_shims::Instant,
41 pub last_gc_check: crate::wasm_shims::Instant, 40 pub last_gc_check: crate::wasm_shims::Instant,
42} 41}
@@ -57,23 +56,12 @@ impl FileLoader for RootDatabase {
57 fn file_text(&self, file_id: FileId) -> Arc<String> { 56 fn file_text(&self, file_id: FileId) -> Arc<String> {
58 FileLoaderDelegate(self).file_text(file_id) 57 FileLoaderDelegate(self).file_text(file_id)
59 } 58 }
60 fn resolve_relative_path( 59 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
61 &self, 60 FileLoaderDelegate(self).resolve_path(anchor, path)
62 anchor: FileId,
63 relative_path: &RelativePath,
64 ) -> Option<FileId> {
65 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
66 } 61 }
67 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 62 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
68 FileLoaderDelegate(self).relevant_crates(file_id) 63 FileLoaderDelegate(self).relevant_crates(file_id)
69 } 64 }
70 fn resolve_extern_path(
71 &self,
72 extern_id: ra_db::ExternSourceId,
73 relative_path: &RelativePath,
74 ) -> Option<FileId> {
75 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
76 }
77} 65}
78 66
79impl salsa::Database for RootDatabase { 67impl salsa::Database for RootDatabase {
@@ -109,7 +97,6 @@ impl RootDatabase {
109 runtime: salsa::Runtime::default(), 97 runtime: salsa::Runtime::default(),
110 last_gc: crate::wasm_shims::Instant::now(), 98 last_gc: crate::wasm_shims::Instant::now(),
111 last_gc_check: crate::wasm_shims::Instant::now(), 99 last_gc_check: crate::wasm_shims::Instant::now(),
112 debug_data: Default::default(),
113 }; 100 };
114 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); 101 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
115 db.set_local_roots_with_durability(Default::default(), Durability::HIGH); 102 db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
@@ -132,7 +119,6 @@ impl salsa::ParallelDatabase for RootDatabase {
132 runtime: self.runtime.snapshot(self), 119 runtime: self.runtime.snapshot(self),
133 last_gc: self.last_gc, 120 last_gc: self.last_gc,
134 last_gc_check: self.last_gc_check, 121 last_gc_check: self.last_gc_check,
135 debug_data: Arc::clone(&self.debug_data),
136 }) 122 })
137 } 123 }
138} 124}
@@ -146,14 +132,3 @@ fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
146 let text = db.file_text(file_id); 132 let text = db.file_text(file_id);
147 Arc::new(LineIndex::new(&*text)) 133 Arc::new(LineIndex::new(&*text))
148} 134}
149
150#[derive(Debug, Default, Clone)]
151pub(crate) struct DebugData {
152 pub(crate) root_paths: FxHashMap<SourceRootId, String>,
153}
154
155impl DebugData {
156 pub(crate) fn merge(&mut self, other: DebugData) {
157 self.root_paths.extend(other.root_paths.into_iter());
158 }
159}
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs
index e713f4b7e..f40ae8304 100644
--- a/crates/ra_ide_db/src/source_change.rs
+++ b/crates/ra_ide_db/src/source_change.rs
@@ -22,17 +22,6 @@ impl SourceChange {
22 ) -> Self { 22 ) -> Self {
23 SourceChange { source_file_edits, file_system_edits, is_snippet: false } 23 SourceChange { source_file_edits, file_system_edits, is_snippet: false }
24 } 24 }
25
26 /// Creates a new SourceChange with the given label,
27 /// containing only the given `SourceFileEdits`.
28 pub fn source_file_edits(edits: Vec<SourceFileEdit>) -> Self {
29 SourceChange { source_file_edits: edits, file_system_edits: vec![], is_snippet: false }
30 }
31 /// Creates a new SourceChange with the given label
32 /// from the given `FileId` and `TextEdit`
33 pub fn source_file_edit_from(file_id: FileId, edit: TextEdit) -> Self {
34 SourceFileEdit { file_id, edit }.into()
35 }
36} 25}
37 26
38#[derive(Debug, Clone)] 27#[derive(Debug, Clone)]
@@ -43,11 +32,13 @@ pub struct SourceFileEdit {
43 32
44impl From<SourceFileEdit> for SourceChange { 33impl From<SourceFileEdit> for SourceChange {
45 fn from(edit: SourceFileEdit) -> SourceChange { 34 fn from(edit: SourceFileEdit) -> SourceChange {
46 SourceChange { 35 vec![edit].into()
47 source_file_edits: vec![edit], 36 }
48 file_system_edits: Vec::new(), 37}
49 is_snippet: false, 38
50 } 39impl From<Vec<SourceFileEdit>> for SourceChange {
40 fn from(source_file_edits: Vec<SourceFileEdit>) -> SourceChange {
41 SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
51 } 42 }
52} 43}
53 44
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index acc31fe3b..aab918973 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -29,9 +29,10 @@ use std::{
29}; 29};
30 30
31use fst::{self, Streamer}; 31use fst::{self, Streamer};
32use hir::db::DefDatabase;
32use ra_db::{ 33use ra_db::{
33 salsa::{self, ParallelDatabase}, 34 salsa::{self, ParallelDatabase},
34 FileId, SourceDatabaseExt, SourceRootId, 35 CrateId, FileId, SourceDatabaseExt, SourceRootId,
35}; 36};
36use ra_syntax::{ 37use ra_syntax::{
37 ast::{self, NameOwner}, 38 ast::{self, NameOwner},
@@ -110,6 +111,14 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
110 Arc::new(SymbolIndex::new(symbols)) 111 Arc::new(SymbolIndex::new(symbols))
111} 112}
112 113
114/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
115struct Snap(salsa::Snapshot<RootDatabase>);
116impl Clone for Snap {
117 fn clone(&self) -> Snap {
118 Snap(self.0.snapshot())
119 }
120}
121
113// Feature: Workspace Symbol 122// Feature: Workspace Symbol
114// 123//
115// Uses fuzzy-search to find types, modules and functions by name across your 124// Uses fuzzy-search to find types, modules and functions by name across your
@@ -132,13 +141,7 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
132// | VS Code | kbd:[Ctrl+T] 141// | VS Code | kbd:[Ctrl+T]
133// |=== 142// |===
134pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { 143pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
135 /// Need to wrap Snapshot to provide `Clone` impl for `map_with` 144 let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone());
136 struct Snap(salsa::Snapshot<RootDatabase>);
137 impl Clone for Snap {
138 fn clone(&self) -> Snap {
139 Snap(self.0.snapshot())
140 }
141 }
142 145
143 let buf: Vec<Arc<SymbolIndex>> = if query.libs { 146 let buf: Vec<Arc<SymbolIndex>> = if query.libs {
144 let snap = Snap(db.snapshot()); 147 let snap = Snap(db.snapshot());
@@ -173,6 +176,33 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
173 query.search(&buf) 176 query.search(&buf)
174} 177}
175 178
179pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> {
180 // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from
181 // that instead?
182
183 let def_map = db.crate_def_map(krate);
184 let mut files = Vec::new();
185 let mut modules = vec![def_map.root];
186 while let Some(module) = modules.pop() {
187 let data = &def_map[module];
188 files.extend(data.origin.file_id());
189 modules.extend(data.children.values());
190 }
191
192 let snap = Snap(db.snapshot());
193
194 #[cfg(not(feature = "wasm"))]
195 let buf = files
196 .par_iter()
197 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
198 .collect::<Vec<_>>();
199
200 #[cfg(feature = "wasm")]
201 let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect::<Vec<_>>();
202
203 query.search(&buf)
204}
205
176pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> { 206pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
177 let name = name_ref.text(); 207 let name = name_ref.text();
178 let mut query = Query::new(name.to_string()); 208 let mut query = Query::new(name.to_string());
@@ -298,9 +328,6 @@ impl Query {
298 let mut stream = op.union(); 328 let mut stream = op.union();
299 let mut res = Vec::new(); 329 let mut res = Vec::new();
300 while let Some((_, indexed_values)) = stream.next() { 330 while let Some((_, indexed_values)) = stream.next() {
301 if res.len() >= self.limit {
302 break;
303 }
304 for indexed_value in indexed_values { 331 for indexed_value in indexed_values {
305 let symbol_index = &indices[indexed_value.index]; 332 let symbol_index = &indices[indexed_value.index];
306 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); 333 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);
@@ -312,7 +339,11 @@ impl Query {
312 if self.exact && symbol.name != self.query { 339 if self.exact && symbol.name != self.query {
313 continue; 340 continue;
314 } 341 }
342
315 res.push(symbol.clone()); 343 res.push(symbol.clone());
344 if res.len() >= self.limit {
345 return res;
346 }
316 } 347 }
317 } 348 }
318 } 349 }
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs
index be0cd5661..293baecf6 100644
--- a/crates/ra_parser/src/grammar.rs
+++ b/crates/ra_parser/src/grammar.rs
@@ -18,9 +18,10 @@
18//! // fn foo() {} 18//! // fn foo() {}
19//! ``` 19//! ```
20//! 20//!
21//! After adding a new inline-test, run `cargo collect-tests` to extract 21//! After adding a new inline-test, run `cargo xtask codegen` to
22//! it as a standalone text-fixture into `tests/data/parser/inline`, and 22//! extract it as a standalone text-fixture into
23//! run `cargo test` once to create the "gold" value. 23//! `crates/ra_syntax/test_data/parser/`, and run `cargo test` once to
24//! create the "gold" value.
24//! 25//!
25//! Coding convention: rules like `where_clause` always produce either a 26//! Coding convention: rules like `where_clause` always produce either a
26//! node or an error, rules like `opt_where_clause` may produce nothing. 27//! node or an error, rules like `opt_where_clause` may produce nothing.
diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs
index 67a924de5..97642bc24 100644
--- a/crates/ra_parser/src/grammar/items.rs
+++ b/crates/ra_parser/src/grammar/items.rs
@@ -118,7 +118,22 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul
118 && p.at_contextual_kw("default") 118 && p.at_contextual_kw("default")
119 && (match p.nth(1) { 119 && (match p.nth(1) {
120 T![impl] => true, 120 T![impl] => true,
121 T![fn] | T![type] => { 121 T![unsafe] => {
122 // test default_unsafe_impl
123 // default unsafe impl Foo {}
124
125 // test default_unsafe_fn
126 // impl T for Foo {
127 // default unsafe fn foo() {}
128 // }
129 if p.nth(2) == T![impl] || p.nth(2) == T![fn] {
130 p.bump_remap(T![default]);
131 p.bump(T![unsafe]);
132 has_mods = true;
133 }
134 false
135 }
136 T![fn] | T![type] | T![const] => {
122 if let ItemFlavor::Mod = flavor { 137 if let ItemFlavor::Mod = flavor {
123 true 138 true
124 } else { 139 } else {
@@ -198,6 +213,9 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul
198 // default type T = Bar; 213 // default type T = Bar;
199 // default fn foo() {} 214 // default fn foo() {}
200 // } 215 // }
216 T![const] => {
217 consts::const_def(p, m);
218 }
201 219
202 // test unsafe_default_impl 220 // test unsafe_default_impl
203 // unsafe default impl Foo {} 221 // unsafe default impl Foo {}
diff --git a/crates/ra_parser/src/grammar/paths.rs b/crates/ra_parser/src/grammar/paths.rs
index 332acc1a0..428aa711e 100644
--- a/crates/ra_parser/src/grammar/paths.rs
+++ b/crates/ra_parser/src/grammar/paths.rs
@@ -3,7 +3,7 @@
3use super::*; 3use super::*;
4 4
5pub(super) const PATH_FIRST: TokenSet = 5pub(super) const PATH_FIRST: TokenSet =
6 token_set![IDENT, SELF_KW, SUPER_KW, CRATE_KW, COLON, L_ANGLE]; 6 token_set![IDENT, T![self], T![super], T![crate], T![:], T![<]];
7 7
8pub(super) fn is_path_start(p: &Parser) -> bool { 8pub(super) fn is_path_start(p: &Parser) -> bool {
9 is_use_path_start(p) || p.at(T![<]) 9 is_use_path_start(p) || p.at(T![<])
diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs
index 68fb2fc73..427c0eb49 100644
--- a/crates/ra_parser/src/grammar/patterns.rs
+++ b/crates/ra_parser/src/grammar/patterns.rs
@@ -4,7 +4,7 @@ use super::*;
4 4
5pub(super) const PATTERN_FIRST: TokenSet = expressions::LITERAL_FIRST 5pub(super) const PATTERN_FIRST: TokenSet = expressions::LITERAL_FIRST
6 .union(paths::PATH_FIRST) 6 .union(paths::PATH_FIRST)
7 .union(token_set![BOX_KW, REF_KW, MUT_KW, L_PAREN, L_BRACK, AMP, UNDERSCORE, MINUS, DOT]); 7 .union(token_set![T![box], T![ref], T![mut], T!['('], T!['['], T![&], T![_], T![-], T![.]]);
8 8
9pub(crate) fn pattern(p: &mut Parser) { 9pub(crate) fn pattern(p: &mut Parser) {
10 pattern_r(p, PAT_RECOVERY_SET); 10 pattern_r(p, PAT_RECOVERY_SET);
@@ -88,7 +88,9 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
88 _ => bind_pat(p, true), 88 _ => bind_pat(p, true),
89 }, 89 },
90 90
91 _ if paths::is_use_path_start(p) => path_or_macro_pat(p), 91 // test type_path_in_pattern
92 // fn main() { let <_>::Foo = (); }
93 _ if paths::is_path_start(p) => path_or_macro_pat(p),
92 _ if is_literal_pat_start(p) => literal_pat(p), 94 _ if is_literal_pat_start(p) => literal_pat(p),
93 95
94 T![.] if p.at(T![..]) => dot_dot_pat(p), 96 T![.] if p.at(T![..]) => dot_dot_pat(p),
@@ -138,7 +140,7 @@ fn literal_pat(p: &mut Parser) -> CompletedMarker {
138// let Bar(..) = (); 140// let Bar(..) = ();
139// } 141// }
140fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker { 142fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker {
141 assert!(paths::is_use_path_start(p)); 143 assert!(paths::is_path_start(p));
142 let m = p.start(); 144 let m = p.start();
143 paths::expr_path(p); 145 paths::expr_path(p);
144 let kind = match p.current() { 146 let kind = match p.current() {
diff --git a/crates/ra_parser/src/grammar/type_params.rs b/crates/ra_parser/src/grammar/type_params.rs
index 50e4900c3..325d566ad 100644
--- a/crates/ra_parser/src/grammar/type_params.rs
+++ b/crates/ra_parser/src/grammar/type_params.rs
@@ -191,10 +191,14 @@ fn where_predicate(p: &mut Parser) {
191 } 191 }
192 _ => { 192 _ => {
193 // test where_pred_for 193 // test where_pred_for
194 // fn test<F>() 194 // fn for_trait<F>()
195 // where 195 // where
196 // for<'a> F: Fn(&'a str) 196 // for<'a> F: Fn(&'a str)
197 // { } 197 // { }
198 if p.at(T![for]) {
199 types::for_binder(p);
200 }
201
198 types::type_(p); 202 types::type_(p);
199 203
200 if p.at(T![:]) { 204 if p.at(T![:]) {
diff --git a/crates/ra_parser/src/grammar/types.rs b/crates/ra_parser/src/grammar/types.rs
index fe1a039cb..9e8e3bd97 100644
--- a/crates/ra_parser/src/grammar/types.rs
+++ b/crates/ra_parser/src/grammar/types.rs
@@ -216,19 +216,21 @@ pub(super) fn for_binder(p: &mut Parser) {
216 216
217// test for_type 217// test for_type
218// type A = for<'a> fn() -> (); 218// type A = for<'a> fn() -> ();
219// fn foo<T>(_t: &T) where for<'a> &'a T: Iterator {} 219// type B = for<'a> unsafe extern "C" fn(&'a ()) -> ();
220// fn bar<T>(_t: &T) where for<'a> &'a mut T: Iterator {} 220// type Obj = for<'a> PartialEq<&'a i32>;
221// fn baz<T>(_t: &T) where for<'a> <&'a T as Baz>::Foo: Iterator {}
222pub(super) fn for_type(p: &mut Parser) { 221pub(super) fn for_type(p: &mut Parser) {
223 assert!(p.at(T![for])); 222 assert!(p.at(T![for]));
224 let m = p.start(); 223 let m = p.start();
225 for_binder(p); 224 for_binder(p);
226 match p.current() { 225 match p.current() {
227 T![fn] | T![unsafe] | T![extern] => fn_pointer_type(p), 226 T![fn] | T![unsafe] | T![extern] => {}
228 T![&] => reference_type(p), 227 // OK: legacy trait object format
229 _ if paths::is_path_start(p) => path_type_(p, false), 228 _ if paths::is_use_path_start(p) => {}
230 _ => p.error("expected a path"), 229 _ => {
230 p.error("expected a function pointer or path");
231 }
231 } 232 }
233 type_no_bounds(p);
232 m.complete(p, FOR_TYPE); 234 m.complete(p, FOR_TYPE);
233} 235}
234 236
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml
index bb3003278..582102945 100644
--- a/crates/ra_proc_macro_srv/Cargo.toml
+++ b/crates/ra_proc_macro_srv/Cargo.toml
@@ -22,3 +22,4 @@ cargo_metadata = "0.10.0"
22difference = "2.0.0" 22difference = "2.0.0"
23# used as proc macro test target 23# used as proc macro test target
24serde_derive = "1.0.106" 24serde_derive = "1.0.106"
25ra_toolchain = { path = "../ra_toolchain" }
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs
index 84348b5de..8d85f2d8a 100644
--- a/crates/ra_proc_macro_srv/src/tests/utils.rs
+++ b/crates/ra_proc_macro_srv/src/tests/utils.rs
@@ -2,7 +2,6 @@
2 2
3use crate::dylib; 3use crate::dylib;
4use crate::ProcMacroSrv; 4use crate::ProcMacroSrv;
5pub use difference::Changeset as __Changeset;
6use ra_proc_macro::ListMacrosTask; 5use ra_proc_macro::ListMacrosTask;
7use std::str::FromStr; 6use std::str::FromStr;
8use test_utils::assert_eq_text; 7use test_utils::assert_eq_text;
@@ -13,7 +12,7 @@ mod fixtures {
13 12
14 // Use current project metadata to get the proc-macro dylib path 13 // Use current project metadata to get the proc-macro dylib path
15 pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf { 14 pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf {
16 let command = Command::new("cargo") 15 let command = Command::new(ra_toolchain::cargo())
17 .args(&["check", "--message-format", "json"]) 16 .args(&["check", "--message-format", "json"])
18 .output() 17 .output()
19 .unwrap() 18 .unwrap()
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index a306ce95f..4b7444039 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -64,7 +64,7 @@ impl Default for CargoConfig {
64 fn default() -> Self { 64 fn default() -> Self {
65 CargoConfig { 65 CargoConfig {
66 no_default_features: false, 66 no_default_features: false,
67 all_features: true, 67 all_features: false,
68 features: Vec::new(), 68 features: Vec::new(),
69 load_out_dirs_from_check: false, 69 load_out_dirs_from_check: false,
70 target: None, 70 target: None,
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs
index b030c8a6a..ee2de4c25 100644
--- a/crates/ra_project_model/src/json_project.rs
+++ b/crates/ra_project_model/src/json_project.rs
@@ -2,9 +2,16 @@
2 2
3use std::path::PathBuf; 3use std::path::PathBuf;
4 4
5use rustc_hash::{FxHashMap, FxHashSet}; 5use rustc_hash::FxHashSet;
6use serde::Deserialize; 6use serde::Deserialize;
7 7
8/// Roots and crates that compose this Rust project.
9#[derive(Clone, Debug, Deserialize)]
10pub struct JsonProject {
11 pub(crate) roots: Vec<Root>,
12 pub(crate) crates: Vec<Crate>,
13}
14
8/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in 15/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in
9/// all roots. Roots might be nested. 16/// all roots. Roots might be nested.
10#[derive(Clone, Debug, Deserialize)] 17#[derive(Clone, Debug, Deserialize)]
@@ -20,8 +27,10 @@ pub struct Crate {
20 pub(crate) root_module: PathBuf, 27 pub(crate) root_module: PathBuf,
21 pub(crate) edition: Edition, 28 pub(crate) edition: Edition,
22 pub(crate) deps: Vec<Dep>, 29 pub(crate) deps: Vec<Dep>,
23 pub(crate) atom_cfgs: FxHashSet<String>, 30
24 pub(crate) key_value_cfgs: FxHashMap<String, String>, 31 #[serde(default)]
32 pub(crate) cfg: FxHashSet<String>,
33
25 pub(crate) out_dir: Option<PathBuf>, 34 pub(crate) out_dir: Option<PathBuf>,
26 pub(crate) proc_macro_dylib_path: Option<PathBuf>, 35 pub(crate) proc_macro_dylib_path: Option<PathBuf>,
27} 36}
@@ -48,9 +57,39 @@ pub struct Dep {
48 pub(crate) name: String, 57 pub(crate) name: String,
49} 58}
50 59
51/// Roots and crates that compose this Rust project. 60#[cfg(test)]
52#[derive(Clone, Debug, Deserialize)] 61mod tests {
53pub struct JsonProject { 62 use super::*;
54 pub(crate) roots: Vec<Root>, 63 use serde_json::json;
55 pub(crate) crates: Vec<Crate>, 64
65 #[test]
66 fn test_crate_deserialization() {
67 let raw_json = json!( {
68 "crate_id": 2,
69 "root_module": "this/is/a/file/path.rs",
70 "deps": [
71 {
72 "crate": 1,
73 "name": "some_dep_crate"
74 },
75 ],
76 "edition": "2015",
77 "cfg": [
78 "atom_1",
79 "atom_2",
80 "feature=feature_1",
81 "feature=feature_2",
82 "other=value",
83 ],
84
85 });
86
87 let krate: Crate = serde_json::from_value(raw_json).unwrap();
88
89 assert!(krate.cfg.contains(&"atom_1".to_string()));
90 assert!(krate.cfg.contains(&"atom_2".to_string()));
91 assert!(krate.cfg.contains(&"feature=feature_1".to_string()));
92 assert!(krate.cfg.contains(&"feature=feature_2".to_string()));
93 assert!(krate.cfg.contains(&"other=value".to_string()));
94 }
56} 95}
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index a2e9f65ef..cb0e27dce 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -14,7 +14,7 @@ use std::{
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
15use ra_cfg::CfgOptions; 15use ra_cfg::CfgOptions;
16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; 16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId};
17use rustc_hash::FxHashMap; 17use rustc_hash::{FxHashMap, FxHashSet};
18use serde_json::from_reader; 18use serde_json::from_reader;
19 19
20pub use crate::{ 20pub use crate::{
@@ -32,6 +32,12 @@ pub enum ProjectWorkspace {
32 Json { project: JsonProject }, 32 Json { project: JsonProject },
33} 33}
34 34
35impl From<JsonProject> for ProjectWorkspace {
36 fn from(project: JsonProject) -> ProjectWorkspace {
37 ProjectWorkspace::Json { project }
38 }
39}
40
35/// `PackageRoot` describes a package root folder. 41/// `PackageRoot` describes a package root folder.
36/// Which may be an external dependency, or a member of 42/// Which may be an external dependency, or a member of
37/// the current workspace. 43/// the current workspace.
@@ -41,41 +47,45 @@ pub struct PackageRoot {
41 path: PathBuf, 47 path: PathBuf,
42 /// Is a member of the current workspace 48 /// Is a member of the current workspace
43 is_member: bool, 49 is_member: bool,
50 out_dir: Option<PathBuf>,
44} 51}
45impl PackageRoot { 52impl PackageRoot {
46 pub fn new_member(path: PathBuf) -> PackageRoot { 53 pub fn new_member(path: PathBuf) -> PackageRoot {
47 Self { path, is_member: true } 54 Self { path, is_member: true, out_dir: None }
48 } 55 }
49 pub fn new_non_member(path: PathBuf) -> PackageRoot { 56 pub fn new_non_member(path: PathBuf) -> PackageRoot {
50 Self { path, is_member: false } 57 Self { path, is_member: false, out_dir: None }
51 } 58 }
52 pub fn path(&self) -> &Path { 59 pub fn path(&self) -> &Path {
53 &self.path 60 &self.path
54 } 61 }
62 pub fn out_dir(&self) -> Option<&Path> {
63 self.out_dir.as_deref()
64 }
55 pub fn is_member(&self) -> bool { 65 pub fn is_member(&self) -> bool {
56 self.is_member 66 self.is_member
57 } 67 }
58} 68}
59 69
60#[derive(Debug, Clone, PartialEq, Eq, Hash)] 70#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
61pub enum ProjectRoot { 71pub enum ProjectManifest {
62 ProjectJson(PathBuf), 72 ProjectJson(PathBuf),
63 CargoToml(PathBuf), 73 CargoToml(PathBuf),
64} 74}
65 75
66impl ProjectRoot { 76impl ProjectManifest {
67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { 77 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> {
68 if path.ends_with("rust-project.json") { 78 if path.ends_with("rust-project.json") {
69 return Ok(ProjectRoot::ProjectJson(path)); 79 return Ok(ProjectManifest::ProjectJson(path));
70 } 80 }
71 if path.ends_with("Cargo.toml") { 81 if path.ends_with("Cargo.toml") {
72 return Ok(ProjectRoot::CargoToml(path)); 82 return Ok(ProjectManifest::CargoToml(path));
73 } 83 }
74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) 84 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
75 } 85 }
76 86
77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> { 87 pub fn discover_single(path: &Path) -> Result<ProjectManifest> {
78 let mut candidates = ProjectRoot::discover(path)?; 88 let mut candidates = ProjectManifest::discover(path)?;
79 let res = match candidates.pop() { 89 let res = match candidates.pop() {
80 None => bail!("no projects"), 90 None => bail!("no projects"),
81 Some(it) => it, 91 Some(it) => it,
@@ -87,12 +97,12 @@ impl ProjectRoot {
87 Ok(res) 97 Ok(res)
88 } 98 }
89 99
90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { 100 pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> {
91 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { 101 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
92 return Ok(vec![ProjectRoot::ProjectJson(project_json)]); 102 return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
93 } 103 }
94 return find_cargo_toml(path) 104 return find_cargo_toml(path)
95 .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); 105 .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
96 106
97 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { 107 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
98 match find_in_parent_dirs(path, "Cargo.toml") { 108 match find_in_parent_dirs(path, "Cargo.toml") {
@@ -128,16 +138,28 @@ impl ProjectRoot {
128 .collect() 138 .collect()
129 } 139 }
130 } 140 }
141
142 pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> {
143 let mut res = paths
144 .iter()
145 .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
146 .flatten()
147 .collect::<FxHashSet<_>>()
148 .into_iter()
149 .collect::<Vec<_>>();
150 res.sort();
151 res
152 }
131} 153}
132 154
133impl ProjectWorkspace { 155impl ProjectWorkspace {
134 pub fn load( 156 pub fn load(
135 root: ProjectRoot, 157 manifest: ProjectManifest,
136 cargo_features: &CargoConfig, 158 cargo_features: &CargoConfig,
137 with_sysroot: bool, 159 with_sysroot: bool,
138 ) -> Result<ProjectWorkspace> { 160 ) -> Result<ProjectWorkspace> {
139 let res = match root { 161 let res = match manifest {
140 ProjectRoot::ProjectJson(project_json) => { 162 ProjectManifest::ProjectJson(project_json) => {
141 let file = File::open(&project_json).with_context(|| { 163 let file = File::open(&project_json).with_context(|| {
142 format!("Failed to open json file {}", project_json.display()) 164 format!("Failed to open json file {}", project_json.display())
143 })?; 165 })?;
@@ -148,7 +170,7 @@ impl ProjectWorkspace {
148 })?, 170 })?,
149 } 171 }
150 } 172 }
151 ProjectRoot::CargoToml(cargo_toml) => { 173 ProjectManifest::CargoToml(cargo_toml) => {
152 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 174 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
153 .with_context(|| { 175 .with_context(|| {
154 format!( 176 format!(
@@ -186,6 +208,7 @@ impl ProjectWorkspace {
186 .map(|pkg| PackageRoot { 208 .map(|pkg| PackageRoot {
187 path: cargo[pkg].root().to_path_buf(), 209 path: cargo[pkg].root().to_path_buf(),
188 is_member: cargo[pkg].is_member, 210 is_member: cargo[pkg].is_member,
211 out_dir: cargo[pkg].out_dir.clone(),
189 }) 212 })
190 .chain(sysroot.crates().map(|krate| { 213 .chain(sysroot.crates().map(|krate| {
191 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) 214 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf())
@@ -194,17 +217,6 @@ impl ProjectWorkspace {
194 } 217 }
195 } 218 }
196 219
197 pub fn out_dirs(&self) -> Vec<PathBuf> {
198 match self {
199 ProjectWorkspace::Json { project } => {
200 project.crates.iter().filter_map(|krate| krate.out_dir.as_ref()).cloned().collect()
201 }
202 ProjectWorkspace::Cargo { cargo, sysroot: _ } => {
203 cargo.packages().filter_map(|pkg| cargo[pkg].out_dir.as_ref()).cloned().collect()
204 }
205 }
206 }
207
208 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> { 220 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
209 match self { 221 match self {
210 ProjectWorkspace::Json { project } => project 222 ProjectWorkspace::Json { project } => project
@@ -232,7 +244,7 @@ impl ProjectWorkspace {
232 244
233 pub fn to_crate_graph( 245 pub fn to_crate_graph(
234 &self, 246 &self,
235 default_cfg_options: &CfgOptions, 247 target: Option<&str>,
236 extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>, 248 extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>,
237 proc_macro_client: &ProcMacroClient, 249 proc_macro_client: &ProcMacroClient,
238 load: &mut dyn FnMut(&Path) -> Option<FileId>, 250 load: &mut dyn FnMut(&Path) -> Option<FileId>,
@@ -251,12 +263,16 @@ impl ProjectWorkspace {
251 json_project::Edition::Edition2018 => Edition::Edition2018, 263 json_project::Edition::Edition2018 => Edition::Edition2018,
252 }; 264 };
253 let cfg_options = { 265 let cfg_options = {
254 let mut opts = default_cfg_options.clone(); 266 let mut opts = CfgOptions::default();
255 for name in &krate.atom_cfgs { 267 for cfg in &krate.cfg {
256 opts.insert_atom(name.into()); 268 match cfg.find('=') {
257 } 269 None => opts.insert_atom(cfg.into()),
258 for (key, value) in &krate.key_value_cfgs { 270 Some(pos) => {
259 opts.insert_key_value(key.into(), value.into()); 271 let key = &cfg[..pos];
272 let value = cfg[pos + 1..].trim_matches('"');
273 opts.insert_key_value(key.into(), value.into());
274 }
275 }
260 } 276 }
261 opts 277 opts
262 }; 278 };
@@ -315,18 +331,13 @@ impl ProjectWorkspace {
315 } 331 }
316 } 332 }
317 ProjectWorkspace::Cargo { cargo, sysroot } => { 333 ProjectWorkspace::Cargo { cargo, sysroot } => {
334 let mut cfg_options = get_rustc_cfg_options(target);
335
318 let sysroot_crates: FxHashMap<_, _> = sysroot 336 let sysroot_crates: FxHashMap<_, _> = sysroot
319 .crates() 337 .crates()
320 .filter_map(|krate| { 338 .filter_map(|krate| {
321 let file_id = load(&sysroot[krate].root)?; 339 let file_id = load(&sysroot[krate].root)?;
322 340
323 // Crates from sysroot have `cfg(test)` disabled
324 let cfg_options = {
325 let mut opts = default_cfg_options.clone();
326 opts.remove_atom("test");
327 opts
328 };
329
330 let env = Env::default(); 341 let env = Env::default();
331 let extern_source = ExternSource::default(); 342 let extern_source = ExternSource::default();
332 let proc_macro = vec![]; 343 let proc_macro = vec![];
@@ -337,7 +348,7 @@ impl ProjectWorkspace {
337 file_id, 348 file_id,
338 Edition::Edition2018, 349 Edition::Edition2018,
339 Some(crate_name), 350 Some(crate_name),
340 cfg_options, 351 cfg_options.clone(),
341 env, 352 env,
342 extern_source, 353 extern_source,
343 proc_macro, 354 proc_macro,
@@ -368,6 +379,10 @@ impl ProjectWorkspace {
368 379
369 let mut pkg_to_lib_crate = FxHashMap::default(); 380 let mut pkg_to_lib_crate = FxHashMap::default();
370 let mut pkg_crates = FxHashMap::default(); 381 let mut pkg_crates = FxHashMap::default();
382
383 // Add test cfg for non-sysroot crates
384 cfg_options.insert_atom("test".into());
385
371 // Next, create crates for each package, target pair 386 // Next, create crates for each package, target pair
372 for pkg in cargo.packages() { 387 for pkg in cargo.packages() {
373 let mut lib_tgt = None; 388 let mut lib_tgt = None;
@@ -376,7 +391,7 @@ impl ProjectWorkspace {
376 if let Some(file_id) = load(root) { 391 if let Some(file_id) = load(root) {
377 let edition = cargo[pkg].edition; 392 let edition = cargo[pkg].edition;
378 let cfg_options = { 393 let cfg_options = {
379 let mut opts = default_cfg_options.clone(); 394 let mut opts = cfg_options.clone();
380 for feature in cargo[pkg].features.iter() { 395 for feature in cargo[pkg].features.iter() {
381 opts.insert_key_value("feature".into(), feature.into()); 396 opts.insert_key_value("feature".into(), feature.into());
382 } 397 }
@@ -533,7 +548,7 @@ impl ProjectWorkspace {
533 } 548 }
534} 549}
535 550
536pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { 551fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions {
537 let mut cfg_options = CfgOptions::default(); 552 let mut cfg_options = CfgOptions::default();
538 553
539 // Some nightly-only cfgs, which are required for stdlib 554 // Some nightly-only cfgs, which are required for stdlib
@@ -551,7 +566,7 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
551 let mut cmd = Command::new(ra_toolchain::rustc()); 566 let mut cmd = Command::new(ra_toolchain::rustc());
552 cmd.args(&["--print", "cfg", "-O"]); 567 cmd.args(&["--print", "cfg", "-O"]);
553 if let Some(target) = target { 568 if let Some(target) = target {
554 cmd.args(&["--target", target.as_str()]); 569 cmd.args(&["--target", target]);
555 } 570 }
556 let output = output(cmd)?; 571 let output = output(cmd)?;
557 Ok(String::from_utf8(output.stdout)?) 572 Ok(String::from_utf8(output.stdout)?)
@@ -573,6 +588,8 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
573 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), 588 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
574 } 589 }
575 590
591 cfg_options.insert_atom("debug_assertions".into());
592
576 cfg_options 593 cfg_options
577} 594}
578 595
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 1876afe95..9d02aeef3 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -75,7 +75,7 @@ impl<N> AstChildren<N> {
75impl<N: AstNode> Iterator for AstChildren<N> { 75impl<N: AstNode> Iterator for AstChildren<N> {
76 type Item = N; 76 type Item = N;
77 fn next(&mut self) -> Option<N> { 77 fn next(&mut self) -> Option<N> {
78 self.inner.by_ref().find_map(N::cast) 78 self.inner.find_map(N::cast)
79 } 79 }
80} 80}
81 81
@@ -285,6 +285,8 @@ where
285 let pred = predicates.next().unwrap(); 285 let pred = predicates.next().unwrap();
286 let mut bounds = pred.type_bound_list().unwrap().bounds(); 286 let mut bounds = pred.type_bound_list().unwrap().bounds();
287 287
288 assert!(pred.for_token().is_none());
289 assert!(pred.type_param_list().is_none());
288 assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string()); 290 assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string());
289 assert_bound("Clone", bounds.next()); 291 assert_bound("Clone", bounds.next());
290 assert_bound("Copy", bounds.next()); 292 assert_bound("Copy", bounds.next());
@@ -322,6 +324,8 @@ where
322 let pred = predicates.next().unwrap(); 324 let pred = predicates.next().unwrap();
323 let mut bounds = pred.type_bound_list().unwrap().bounds(); 325 let mut bounds = pred.type_bound_list().unwrap().bounds();
324 326
325 assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string()); 327 assert!(pred.for_token().is_some());
328 assert_eq!("<'a>", pred.type_param_list().unwrap().syntax().text().to_string());
329 assert_eq!("F", pred.type_ref().unwrap().syntax().text().to_string());
326 assert_bound("Fn(&'a str)", bounds.next()); 330 assert_bound("Fn(&'a str)", bounds.next());
327} 331}
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 29eb3fcb9..2ef173a03 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -579,12 +579,17 @@ pub trait AstNodeEdit: AstNode + Clone + Sized {
579 rewriter.rewrite_ast(self) 579 rewriter.rewrite_ast(self)
580 } 580 }
581 #[must_use] 581 #[must_use]
582 fn indent(&self, indent: IndentLevel) -> Self { 582 fn indent(&self, level: IndentLevel) -> Self {
583 Self::cast(indent.increase_indent(self.syntax().clone())).unwrap() 583 Self::cast(level.increase_indent(self.syntax().clone())).unwrap()
584 } 584 }
585 #[must_use] 585 #[must_use]
586 fn dedent(&self, indent: IndentLevel) -> Self { 586 fn dedent(&self, level: IndentLevel) -> Self {
587 Self::cast(indent.decrease_indent(self.syntax().clone())).unwrap() 587 Self::cast(level.decrease_indent(self.syntax().clone())).unwrap()
588 }
589 #[must_use]
590 fn reset_indent(&self) -> Self {
591 let level = IndentLevel::from_node(self.syntax());
592 self.dedent(level)
588 } 593 }
589} 594}
590 595
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index cb430ca01..58141da11 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -2052,6 +2052,8 @@ pub struct WherePred {
2052} 2052}
2053impl ast::TypeBoundsOwner for WherePred {} 2053impl ast::TypeBoundsOwner for WherePred {}
2054impl WherePred { 2054impl WherePred {
2055 pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) }
2056 pub fn type_param_list(&self) -> Option<TypeParamList> { support::child(&self.syntax) }
2055 pub fn lifetime_token(&self) -> Option<SyntaxToken> { 2057 pub fn lifetime_token(&self) -> Option<SyntaxToken> {
2056 support::token(&self.syntax, T![lifetime]) 2058 support::token(&self.syntax, T![lifetime])
2057 } 2059 }
@@ -4849,687 +4851,687 @@ impl AstNode for FieldDefList {
4849 } 4851 }
4850} 4852}
4851impl std::fmt::Display for NominalDef { 4853impl std::fmt::Display for NominalDef {
4852 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4854 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4853 std::fmt::Display::fmt(self.syntax(), f) 4855 std::fmt::Display::fmt(self.syntax(), f)
4854 } 4856 }
4855} 4857}
4856impl std::fmt::Display for GenericParam { 4858impl std::fmt::Display for GenericParam {
4857 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4859 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4858 std::fmt::Display::fmt(self.syntax(), f) 4860 std::fmt::Display::fmt(self.syntax(), f)
4859 } 4861 }
4860} 4862}
4861impl std::fmt::Display for GenericArg { 4863impl std::fmt::Display for GenericArg {
4862 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4864 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4863 std::fmt::Display::fmt(self.syntax(), f) 4865 std::fmt::Display::fmt(self.syntax(), f)
4864 } 4866 }
4865} 4867}
4866impl std::fmt::Display for TypeRef { 4868impl std::fmt::Display for TypeRef {
4867 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4869 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4868 std::fmt::Display::fmt(self.syntax(), f) 4870 std::fmt::Display::fmt(self.syntax(), f)
4869 } 4871 }
4870} 4872}
4871impl std::fmt::Display for ModuleItem { 4873impl std::fmt::Display for ModuleItem {
4872 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4874 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4873 std::fmt::Display::fmt(self.syntax(), f) 4875 std::fmt::Display::fmt(self.syntax(), f)
4874 } 4876 }
4875} 4877}
4876impl std::fmt::Display for AssocItem { 4878impl std::fmt::Display for AssocItem {
4877 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4879 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4878 std::fmt::Display::fmt(self.syntax(), f) 4880 std::fmt::Display::fmt(self.syntax(), f)
4879 } 4881 }
4880} 4882}
4881impl std::fmt::Display for ExternItem { 4883impl std::fmt::Display for ExternItem {
4882 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4884 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4883 std::fmt::Display::fmt(self.syntax(), f) 4885 std::fmt::Display::fmt(self.syntax(), f)
4884 } 4886 }
4885} 4887}
4886impl std::fmt::Display for Expr { 4888impl std::fmt::Display for Expr {
4887 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4889 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4888 std::fmt::Display::fmt(self.syntax(), f) 4890 std::fmt::Display::fmt(self.syntax(), f)
4889 } 4891 }
4890} 4892}
4891impl std::fmt::Display for Pat { 4893impl std::fmt::Display for Pat {
4892 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4894 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4893 std::fmt::Display::fmt(self.syntax(), f) 4895 std::fmt::Display::fmt(self.syntax(), f)
4894 } 4896 }
4895} 4897}
4896impl std::fmt::Display for RecordInnerPat { 4898impl std::fmt::Display for RecordInnerPat {
4897 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4899 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4898 std::fmt::Display::fmt(self.syntax(), f) 4900 std::fmt::Display::fmt(self.syntax(), f)
4899 } 4901 }
4900} 4902}
4901impl std::fmt::Display for AttrInput { 4903impl std::fmt::Display for AttrInput {
4902 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4904 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4903 std::fmt::Display::fmt(self.syntax(), f) 4905 std::fmt::Display::fmt(self.syntax(), f)
4904 } 4906 }
4905} 4907}
4906impl std::fmt::Display for Stmt { 4908impl std::fmt::Display for Stmt {
4907 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4909 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4908 std::fmt::Display::fmt(self.syntax(), f) 4910 std::fmt::Display::fmt(self.syntax(), f)
4909 } 4911 }
4910} 4912}
4911impl std::fmt::Display for FieldDefList { 4913impl std::fmt::Display for FieldDefList {
4912 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4914 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4913 std::fmt::Display::fmt(self.syntax(), f) 4915 std::fmt::Display::fmt(self.syntax(), f)
4914 } 4916 }
4915} 4917}
4916impl std::fmt::Display for SourceFile { 4918impl std::fmt::Display for SourceFile {
4917 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4919 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4918 std::fmt::Display::fmt(self.syntax(), f) 4920 std::fmt::Display::fmt(self.syntax(), f)
4919 } 4921 }
4920} 4922}
4921impl std::fmt::Display for FnDef { 4923impl std::fmt::Display for FnDef {
4922 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4924 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4923 std::fmt::Display::fmt(self.syntax(), f) 4925 std::fmt::Display::fmt(self.syntax(), f)
4924 } 4926 }
4925} 4927}
4926impl std::fmt::Display for RetType { 4928impl std::fmt::Display for RetType {
4927 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4929 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4928 std::fmt::Display::fmt(self.syntax(), f) 4930 std::fmt::Display::fmt(self.syntax(), f)
4929 } 4931 }
4930} 4932}
4931impl std::fmt::Display for StructDef { 4933impl std::fmt::Display for StructDef {
4932 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4934 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4933 std::fmt::Display::fmt(self.syntax(), f) 4935 std::fmt::Display::fmt(self.syntax(), f)
4934 } 4936 }
4935} 4937}
4936impl std::fmt::Display for UnionDef { 4938impl std::fmt::Display for UnionDef {
4937 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4939 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4938 std::fmt::Display::fmt(self.syntax(), f) 4940 std::fmt::Display::fmt(self.syntax(), f)
4939 } 4941 }
4940} 4942}
4941impl std::fmt::Display for RecordFieldDefList { 4943impl std::fmt::Display for RecordFieldDefList {
4942 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4944 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4943 std::fmt::Display::fmt(self.syntax(), f) 4945 std::fmt::Display::fmt(self.syntax(), f)
4944 } 4946 }
4945} 4947}
4946impl std::fmt::Display for RecordFieldDef { 4948impl std::fmt::Display for RecordFieldDef {
4947 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4949 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4948 std::fmt::Display::fmt(self.syntax(), f) 4950 std::fmt::Display::fmt(self.syntax(), f)
4949 } 4951 }
4950} 4952}
4951impl std::fmt::Display for TupleFieldDefList { 4953impl std::fmt::Display for TupleFieldDefList {
4952 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4954 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4953 std::fmt::Display::fmt(self.syntax(), f) 4955 std::fmt::Display::fmt(self.syntax(), f)
4954 } 4956 }
4955} 4957}
4956impl std::fmt::Display for TupleFieldDef { 4958impl std::fmt::Display for TupleFieldDef {
4957 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4959 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4958 std::fmt::Display::fmt(self.syntax(), f) 4960 std::fmt::Display::fmt(self.syntax(), f)
4959 } 4961 }
4960} 4962}
4961impl std::fmt::Display for EnumDef { 4963impl std::fmt::Display for EnumDef {
4962 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4964 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4963 std::fmt::Display::fmt(self.syntax(), f) 4965 std::fmt::Display::fmt(self.syntax(), f)
4964 } 4966 }
4965} 4967}
4966impl std::fmt::Display for EnumVariantList { 4968impl std::fmt::Display for EnumVariantList {
4967 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4969 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4968 std::fmt::Display::fmt(self.syntax(), f) 4970 std::fmt::Display::fmt(self.syntax(), f)
4969 } 4971 }
4970} 4972}
4971impl std::fmt::Display for EnumVariant { 4973impl std::fmt::Display for EnumVariant {
4972 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4974 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4973 std::fmt::Display::fmt(self.syntax(), f) 4975 std::fmt::Display::fmt(self.syntax(), f)
4974 } 4976 }
4975} 4977}
4976impl std::fmt::Display for TraitDef { 4978impl std::fmt::Display for TraitDef {
4977 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4979 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4978 std::fmt::Display::fmt(self.syntax(), f) 4980 std::fmt::Display::fmt(self.syntax(), f)
4979 } 4981 }
4980} 4982}
4981impl std::fmt::Display for Module { 4983impl std::fmt::Display for Module {
4982 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4984 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4983 std::fmt::Display::fmt(self.syntax(), f) 4985 std::fmt::Display::fmt(self.syntax(), f)
4984 } 4986 }
4985} 4987}
4986impl std::fmt::Display for ItemList { 4988impl std::fmt::Display for ItemList {
4987 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4989 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4988 std::fmt::Display::fmt(self.syntax(), f) 4990 std::fmt::Display::fmt(self.syntax(), f)
4989 } 4991 }
4990} 4992}
4991impl std::fmt::Display for ConstDef { 4993impl std::fmt::Display for ConstDef {
4992 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4994 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4993 std::fmt::Display::fmt(self.syntax(), f) 4995 std::fmt::Display::fmt(self.syntax(), f)
4994 } 4996 }
4995} 4997}
4996impl std::fmt::Display for StaticDef { 4998impl std::fmt::Display for StaticDef {
4997 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4999 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4998 std::fmt::Display::fmt(self.syntax(), f) 5000 std::fmt::Display::fmt(self.syntax(), f)
4999 } 5001 }
5000} 5002}
5001impl std::fmt::Display for TypeAliasDef { 5003impl std::fmt::Display for TypeAliasDef {
5002 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5004 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5003 std::fmt::Display::fmt(self.syntax(), f) 5005 std::fmt::Display::fmt(self.syntax(), f)
5004 } 5006 }
5005} 5007}
5006impl std::fmt::Display for ImplDef { 5008impl std::fmt::Display for ImplDef {
5007 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5009 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5008 std::fmt::Display::fmt(self.syntax(), f) 5010 std::fmt::Display::fmt(self.syntax(), f)
5009 } 5011 }
5010} 5012}
5011impl std::fmt::Display for ParenType { 5013impl std::fmt::Display for ParenType {
5012 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5014 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5013 std::fmt::Display::fmt(self.syntax(), f) 5015 std::fmt::Display::fmt(self.syntax(), f)
5014 } 5016 }
5015} 5017}
5016impl std::fmt::Display for TupleType { 5018impl std::fmt::Display for TupleType {
5017 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5019 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5018 std::fmt::Display::fmt(self.syntax(), f) 5020 std::fmt::Display::fmt(self.syntax(), f)
5019 } 5021 }
5020} 5022}
5021impl std::fmt::Display for NeverType { 5023impl std::fmt::Display for NeverType {
5022 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5024 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5023 std::fmt::Display::fmt(self.syntax(), f) 5025 std::fmt::Display::fmt(self.syntax(), f)
5024 } 5026 }
5025} 5027}
5026impl std::fmt::Display for PathType { 5028impl std::fmt::Display for PathType {
5027 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5029 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5028 std::fmt::Display::fmt(self.syntax(), f) 5030 std::fmt::Display::fmt(self.syntax(), f)
5029 } 5031 }
5030} 5032}
5031impl std::fmt::Display for PointerType { 5033impl std::fmt::Display for PointerType {
5032 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5034 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5033 std::fmt::Display::fmt(self.syntax(), f) 5035 std::fmt::Display::fmt(self.syntax(), f)
5034 } 5036 }
5035} 5037}
5036impl std::fmt::Display for ArrayType { 5038impl std::fmt::Display for ArrayType {
5037 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5039 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5038 std::fmt::Display::fmt(self.syntax(), f) 5040 std::fmt::Display::fmt(self.syntax(), f)
5039 } 5041 }
5040} 5042}
5041impl std::fmt::Display for SliceType { 5043impl std::fmt::Display for SliceType {
5042 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5044 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5043 std::fmt::Display::fmt(self.syntax(), f) 5045 std::fmt::Display::fmt(self.syntax(), f)
5044 } 5046 }
5045} 5047}
5046impl std::fmt::Display for ReferenceType { 5048impl std::fmt::Display for ReferenceType {
5047 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5049 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5048 std::fmt::Display::fmt(self.syntax(), f) 5050 std::fmt::Display::fmt(self.syntax(), f)
5049 } 5051 }
5050} 5052}
5051impl std::fmt::Display for PlaceholderType { 5053impl std::fmt::Display for PlaceholderType {
5052 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5054 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5053 std::fmt::Display::fmt(self.syntax(), f) 5055 std::fmt::Display::fmt(self.syntax(), f)
5054 } 5056 }
5055} 5057}
5056impl std::fmt::Display for FnPointerType { 5058impl std::fmt::Display for FnPointerType {
5057 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5059 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5058 std::fmt::Display::fmt(self.syntax(), f) 5060 std::fmt::Display::fmt(self.syntax(), f)
5059 } 5061 }
5060} 5062}
5061impl std::fmt::Display for ForType { 5063impl std::fmt::Display for ForType {
5062 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5064 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5063 std::fmt::Display::fmt(self.syntax(), f) 5065 std::fmt::Display::fmt(self.syntax(), f)
5064 } 5066 }
5065} 5067}
5066impl std::fmt::Display for ImplTraitType { 5068impl std::fmt::Display for ImplTraitType {
5067 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5069 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5068 std::fmt::Display::fmt(self.syntax(), f) 5070 std::fmt::Display::fmt(self.syntax(), f)
5069 } 5071 }
5070} 5072}
5071impl std::fmt::Display for DynTraitType { 5073impl std::fmt::Display for DynTraitType {
5072 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5074 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5073 std::fmt::Display::fmt(self.syntax(), f) 5075 std::fmt::Display::fmt(self.syntax(), f)
5074 } 5076 }
5075} 5077}
5076impl std::fmt::Display for TupleExpr { 5078impl std::fmt::Display for TupleExpr {
5077 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5079 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5078 std::fmt::Display::fmt(self.syntax(), f) 5080 std::fmt::Display::fmt(self.syntax(), f)
5079 } 5081 }
5080} 5082}
5081impl std::fmt::Display for ArrayExpr { 5083impl std::fmt::Display for ArrayExpr {
5082 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5084 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5083 std::fmt::Display::fmt(self.syntax(), f) 5085 std::fmt::Display::fmt(self.syntax(), f)
5084 } 5086 }
5085} 5087}
5086impl std::fmt::Display for ParenExpr { 5088impl std::fmt::Display for ParenExpr {
5087 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5089 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5088 std::fmt::Display::fmt(self.syntax(), f) 5090 std::fmt::Display::fmt(self.syntax(), f)
5089 } 5091 }
5090} 5092}
5091impl std::fmt::Display for PathExpr { 5093impl std::fmt::Display for PathExpr {
5092 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5094 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5093 std::fmt::Display::fmt(self.syntax(), f) 5095 std::fmt::Display::fmt(self.syntax(), f)
5094 } 5096 }
5095} 5097}
5096impl std::fmt::Display for LambdaExpr { 5098impl std::fmt::Display for LambdaExpr {
5097 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5099 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5098 std::fmt::Display::fmt(self.syntax(), f) 5100 std::fmt::Display::fmt(self.syntax(), f)
5099 } 5101 }
5100} 5102}
5101impl std::fmt::Display for IfExpr { 5103impl std::fmt::Display for IfExpr {
5102 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5103 std::fmt::Display::fmt(self.syntax(), f) 5105 std::fmt::Display::fmt(self.syntax(), f)
5104 } 5106 }
5105} 5107}
5106impl std::fmt::Display for LoopExpr { 5108impl std::fmt::Display for LoopExpr {
5107 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5108 std::fmt::Display::fmt(self.syntax(), f) 5110 std::fmt::Display::fmt(self.syntax(), f)
5109 } 5111 }
5110} 5112}
5111impl std::fmt::Display for EffectExpr { 5113impl std::fmt::Display for EffectExpr {
5112 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5113 std::fmt::Display::fmt(self.syntax(), f) 5115 std::fmt::Display::fmt(self.syntax(), f)
5114 } 5116 }
5115} 5117}
5116impl std::fmt::Display for ForExpr { 5118impl std::fmt::Display for ForExpr {
5117 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5118 std::fmt::Display::fmt(self.syntax(), f) 5120 std::fmt::Display::fmt(self.syntax(), f)
5119 } 5121 }
5120} 5122}
5121impl std::fmt::Display for WhileExpr { 5123impl std::fmt::Display for WhileExpr {
5122 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5123 std::fmt::Display::fmt(self.syntax(), f) 5125 std::fmt::Display::fmt(self.syntax(), f)
5124 } 5126 }
5125} 5127}
5126impl std::fmt::Display for ContinueExpr { 5128impl std::fmt::Display for ContinueExpr {
5127 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5128 std::fmt::Display::fmt(self.syntax(), f) 5130 std::fmt::Display::fmt(self.syntax(), f)
5129 } 5131 }
5130} 5132}
5131impl std::fmt::Display for BreakExpr { 5133impl std::fmt::Display for BreakExpr {
5132 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5133 std::fmt::Display::fmt(self.syntax(), f) 5135 std::fmt::Display::fmt(self.syntax(), f)
5134 } 5136 }
5135} 5137}
5136impl std::fmt::Display for Label { 5138impl std::fmt::Display for Label {
5137 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5138 std::fmt::Display::fmt(self.syntax(), f) 5140 std::fmt::Display::fmt(self.syntax(), f)
5139 } 5141 }
5140} 5142}
5141impl std::fmt::Display for BlockExpr { 5143impl std::fmt::Display for BlockExpr {
5142 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5143 std::fmt::Display::fmt(self.syntax(), f) 5145 std::fmt::Display::fmt(self.syntax(), f)
5144 } 5146 }
5145} 5147}
5146impl std::fmt::Display for ReturnExpr { 5148impl std::fmt::Display for ReturnExpr {
5147 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5148 std::fmt::Display::fmt(self.syntax(), f) 5150 std::fmt::Display::fmt(self.syntax(), f)
5149 } 5151 }
5150} 5152}
5151impl std::fmt::Display for CallExpr { 5153impl std::fmt::Display for CallExpr {
5152 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5153 std::fmt::Display::fmt(self.syntax(), f) 5155 std::fmt::Display::fmt(self.syntax(), f)
5154 } 5156 }
5155} 5157}
5156impl std::fmt::Display for MethodCallExpr { 5158impl std::fmt::Display for MethodCallExpr {
5157 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5158 std::fmt::Display::fmt(self.syntax(), f) 5160 std::fmt::Display::fmt(self.syntax(), f)
5159 } 5161 }
5160} 5162}
5161impl std::fmt::Display for IndexExpr { 5163impl std::fmt::Display for IndexExpr {
5162 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5163 std::fmt::Display::fmt(self.syntax(), f) 5165 std::fmt::Display::fmt(self.syntax(), f)
5164 } 5166 }
5165} 5167}
5166impl std::fmt::Display for FieldExpr { 5168impl std::fmt::Display for FieldExpr {
5167 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5168 std::fmt::Display::fmt(self.syntax(), f) 5170 std::fmt::Display::fmt(self.syntax(), f)
5169 } 5171 }
5170} 5172}
5171impl std::fmt::Display for AwaitExpr { 5173impl std::fmt::Display for AwaitExpr {
5172 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5173 std::fmt::Display::fmt(self.syntax(), f) 5175 std::fmt::Display::fmt(self.syntax(), f)
5174 } 5176 }
5175} 5177}
5176impl std::fmt::Display for TryExpr { 5178impl std::fmt::Display for TryExpr {
5177 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5178 std::fmt::Display::fmt(self.syntax(), f) 5180 std::fmt::Display::fmt(self.syntax(), f)
5179 } 5181 }
5180} 5182}
5181impl std::fmt::Display for CastExpr { 5183impl std::fmt::Display for CastExpr {
5182 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5183 std::fmt::Display::fmt(self.syntax(), f) 5185 std::fmt::Display::fmt(self.syntax(), f)
5184 } 5186 }
5185} 5187}
5186impl std::fmt::Display for RefExpr { 5188impl std::fmt::Display for RefExpr {
5187 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5188 std::fmt::Display::fmt(self.syntax(), f) 5190 std::fmt::Display::fmt(self.syntax(), f)
5189 } 5191 }
5190} 5192}
5191impl std::fmt::Display for PrefixExpr { 5193impl std::fmt::Display for PrefixExpr {
5192 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5193 std::fmt::Display::fmt(self.syntax(), f) 5195 std::fmt::Display::fmt(self.syntax(), f)
5194 } 5196 }
5195} 5197}
5196impl std::fmt::Display for BoxExpr { 5198impl std::fmt::Display for BoxExpr {
5197 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5198 std::fmt::Display::fmt(self.syntax(), f) 5200 std::fmt::Display::fmt(self.syntax(), f)
5199 } 5201 }
5200} 5202}
5201impl std::fmt::Display for RangeExpr { 5203impl std::fmt::Display for RangeExpr {
5202 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5203 std::fmt::Display::fmt(self.syntax(), f) 5205 std::fmt::Display::fmt(self.syntax(), f)
5204 } 5206 }
5205} 5207}
5206impl std::fmt::Display for BinExpr { 5208impl std::fmt::Display for BinExpr {
5207 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5208 std::fmt::Display::fmt(self.syntax(), f) 5210 std::fmt::Display::fmt(self.syntax(), f)
5209 } 5211 }
5210} 5212}
5211impl std::fmt::Display for Literal { 5213impl std::fmt::Display for Literal {
5212 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5213 std::fmt::Display::fmt(self.syntax(), f) 5215 std::fmt::Display::fmt(self.syntax(), f)
5214 } 5216 }
5215} 5217}
5216impl std::fmt::Display for MatchExpr { 5218impl std::fmt::Display for MatchExpr {
5217 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5218 std::fmt::Display::fmt(self.syntax(), f) 5220 std::fmt::Display::fmt(self.syntax(), f)
5219 } 5221 }
5220} 5222}
5221impl std::fmt::Display for MatchArmList { 5223impl std::fmt::Display for MatchArmList {
5222 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5223 std::fmt::Display::fmt(self.syntax(), f) 5225 std::fmt::Display::fmt(self.syntax(), f)
5224 } 5226 }
5225} 5227}
5226impl std::fmt::Display for MatchArm { 5228impl std::fmt::Display for MatchArm {
5227 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5228 std::fmt::Display::fmt(self.syntax(), f) 5230 std::fmt::Display::fmt(self.syntax(), f)
5229 } 5231 }
5230} 5232}
5231impl std::fmt::Display for MatchGuard { 5233impl std::fmt::Display for MatchGuard {
5232 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5233 std::fmt::Display::fmt(self.syntax(), f) 5235 std::fmt::Display::fmt(self.syntax(), f)
5234 } 5236 }
5235} 5237}
5236impl std::fmt::Display for RecordLit { 5238impl std::fmt::Display for RecordLit {
5237 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5238 std::fmt::Display::fmt(self.syntax(), f) 5240 std::fmt::Display::fmt(self.syntax(), f)
5239 } 5241 }
5240} 5242}
5241impl std::fmt::Display for RecordFieldList { 5243impl std::fmt::Display for RecordFieldList {
5242 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5243 std::fmt::Display::fmt(self.syntax(), f) 5245 std::fmt::Display::fmt(self.syntax(), f)
5244 } 5246 }
5245} 5247}
5246impl std::fmt::Display for RecordField { 5248impl std::fmt::Display for RecordField {
5247 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5248 std::fmt::Display::fmt(self.syntax(), f) 5250 std::fmt::Display::fmt(self.syntax(), f)
5249 } 5251 }
5250} 5252}
5251impl std::fmt::Display for OrPat { 5253impl std::fmt::Display for OrPat {
5252 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5254 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5253 std::fmt::Display::fmt(self.syntax(), f) 5255 std::fmt::Display::fmt(self.syntax(), f)
5254 } 5256 }
5255} 5257}
5256impl std::fmt::Display for ParenPat { 5258impl std::fmt::Display for ParenPat {
5257 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5258 std::fmt::Display::fmt(self.syntax(), f) 5260 std::fmt::Display::fmt(self.syntax(), f)
5259 } 5261 }
5260} 5262}
5261impl std::fmt::Display for RefPat { 5263impl std::fmt::Display for RefPat {
5262 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5263 std::fmt::Display::fmt(self.syntax(), f) 5265 std::fmt::Display::fmt(self.syntax(), f)
5264 } 5266 }
5265} 5267}
5266impl std::fmt::Display for BoxPat { 5268impl std::fmt::Display for BoxPat {
5267 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5268 std::fmt::Display::fmt(self.syntax(), f) 5270 std::fmt::Display::fmt(self.syntax(), f)
5269 } 5271 }
5270} 5272}
5271impl std::fmt::Display for BindPat { 5273impl std::fmt::Display for BindPat {
5272 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5273 std::fmt::Display::fmt(self.syntax(), f) 5275 std::fmt::Display::fmt(self.syntax(), f)
5274 } 5276 }
5275} 5277}
5276impl std::fmt::Display for PlaceholderPat { 5278impl std::fmt::Display for PlaceholderPat {
5277 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5278 std::fmt::Display::fmt(self.syntax(), f) 5280 std::fmt::Display::fmt(self.syntax(), f)
5279 } 5281 }
5280} 5282}
5281impl std::fmt::Display for DotDotPat { 5283impl std::fmt::Display for DotDotPat {
5282 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5283 std::fmt::Display::fmt(self.syntax(), f) 5285 std::fmt::Display::fmt(self.syntax(), f)
5284 } 5286 }
5285} 5287}
5286impl std::fmt::Display for PathPat { 5288impl std::fmt::Display for PathPat {
5287 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5288 std::fmt::Display::fmt(self.syntax(), f) 5290 std::fmt::Display::fmt(self.syntax(), f)
5289 } 5291 }
5290} 5292}
5291impl std::fmt::Display for SlicePat { 5293impl std::fmt::Display for SlicePat {
5292 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5294 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5293 std::fmt::Display::fmt(self.syntax(), f) 5295 std::fmt::Display::fmt(self.syntax(), f)
5294 } 5296 }
5295} 5297}
5296impl std::fmt::Display for RangePat { 5298impl std::fmt::Display for RangePat {
5297 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5298 std::fmt::Display::fmt(self.syntax(), f) 5300 std::fmt::Display::fmt(self.syntax(), f)
5299 } 5301 }
5300} 5302}
5301impl std::fmt::Display for LiteralPat { 5303impl std::fmt::Display for LiteralPat {
5302 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5304 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5303 std::fmt::Display::fmt(self.syntax(), f) 5305 std::fmt::Display::fmt(self.syntax(), f)
5304 } 5306 }
5305} 5307}
5306impl std::fmt::Display for MacroPat { 5308impl std::fmt::Display for MacroPat {
5307 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5309 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5308 std::fmt::Display::fmt(self.syntax(), f) 5310 std::fmt::Display::fmt(self.syntax(), f)
5309 } 5311 }
5310} 5312}
5311impl std::fmt::Display for RecordPat { 5313impl std::fmt::Display for RecordPat {
5312 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5313 std::fmt::Display::fmt(self.syntax(), f) 5315 std::fmt::Display::fmt(self.syntax(), f)
5314 } 5316 }
5315} 5317}
5316impl std::fmt::Display for RecordFieldPatList { 5318impl std::fmt::Display for RecordFieldPatList {
5317 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5318 std::fmt::Display::fmt(self.syntax(), f) 5320 std::fmt::Display::fmt(self.syntax(), f)
5319 } 5321 }
5320} 5322}
5321impl std::fmt::Display for RecordFieldPat { 5323impl std::fmt::Display for RecordFieldPat {
5322 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5324 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5323 std::fmt::Display::fmt(self.syntax(), f) 5325 std::fmt::Display::fmt(self.syntax(), f)
5324 } 5326 }
5325} 5327}
5326impl std::fmt::Display for TupleStructPat { 5328impl std::fmt::Display for TupleStructPat {
5327 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5329 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5328 std::fmt::Display::fmt(self.syntax(), f) 5330 std::fmt::Display::fmt(self.syntax(), f)
5329 } 5331 }
5330} 5332}
5331impl std::fmt::Display for TuplePat { 5333impl std::fmt::Display for TuplePat {
5332 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5333 std::fmt::Display::fmt(self.syntax(), f) 5335 std::fmt::Display::fmt(self.syntax(), f)
5334 } 5336 }
5335} 5337}
5336impl std::fmt::Display for Visibility { 5338impl std::fmt::Display for Visibility {
5337 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5338 std::fmt::Display::fmt(self.syntax(), f) 5340 std::fmt::Display::fmt(self.syntax(), f)
5339 } 5341 }
5340} 5342}
5341impl std::fmt::Display for Name { 5343impl std::fmt::Display for Name {
5342 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5344 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5343 std::fmt::Display::fmt(self.syntax(), f) 5345 std::fmt::Display::fmt(self.syntax(), f)
5344 } 5346 }
5345} 5347}
5346impl std::fmt::Display for NameRef { 5348impl std::fmt::Display for NameRef {
5347 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5348 std::fmt::Display::fmt(self.syntax(), f) 5350 std::fmt::Display::fmt(self.syntax(), f)
5349 } 5351 }
5350} 5352}
5351impl std::fmt::Display for MacroCall { 5353impl std::fmt::Display for MacroCall {
5352 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5354 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5353 std::fmt::Display::fmt(self.syntax(), f) 5355 std::fmt::Display::fmt(self.syntax(), f)
5354 } 5356 }
5355} 5357}
5356impl std::fmt::Display for Attr { 5358impl std::fmt::Display for Attr {
5357 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5359 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5358 std::fmt::Display::fmt(self.syntax(), f) 5360 std::fmt::Display::fmt(self.syntax(), f)
5359 } 5361 }
5360} 5362}
5361impl std::fmt::Display for TokenTree { 5363impl std::fmt::Display for TokenTree {
5362 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5364 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5363 std::fmt::Display::fmt(self.syntax(), f) 5365 std::fmt::Display::fmt(self.syntax(), f)
5364 } 5366 }
5365} 5367}
5366impl std::fmt::Display for TypeParamList { 5368impl std::fmt::Display for TypeParamList {
5367 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5368 std::fmt::Display::fmt(self.syntax(), f) 5370 std::fmt::Display::fmt(self.syntax(), f)
5369 } 5371 }
5370} 5372}
5371impl std::fmt::Display for TypeParam { 5373impl std::fmt::Display for TypeParam {
5372 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5374 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5373 std::fmt::Display::fmt(self.syntax(), f) 5375 std::fmt::Display::fmt(self.syntax(), f)
5374 } 5376 }
5375} 5377}
5376impl std::fmt::Display for ConstParam { 5378impl std::fmt::Display for ConstParam {
5377 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5379 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5378 std::fmt::Display::fmt(self.syntax(), f) 5380 std::fmt::Display::fmt(self.syntax(), f)
5379 } 5381 }
5380} 5382}
5381impl std::fmt::Display for LifetimeParam { 5383impl std::fmt::Display for LifetimeParam {
5382 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5383 std::fmt::Display::fmt(self.syntax(), f) 5385 std::fmt::Display::fmt(self.syntax(), f)
5384 } 5386 }
5385} 5387}
5386impl std::fmt::Display for TypeBound { 5388impl std::fmt::Display for TypeBound {
5387 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5389 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5388 std::fmt::Display::fmt(self.syntax(), f) 5390 std::fmt::Display::fmt(self.syntax(), f)
5389 } 5391 }
5390} 5392}
5391impl std::fmt::Display for TypeBoundList { 5393impl std::fmt::Display for TypeBoundList {
5392 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5394 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5393 std::fmt::Display::fmt(self.syntax(), f) 5395 std::fmt::Display::fmt(self.syntax(), f)
5394 } 5396 }
5395} 5397}
5396impl std::fmt::Display for WherePred { 5398impl std::fmt::Display for WherePred {
5397 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5399 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5398 std::fmt::Display::fmt(self.syntax(), f) 5400 std::fmt::Display::fmt(self.syntax(), f)
5399 } 5401 }
5400} 5402}
5401impl std::fmt::Display for WhereClause { 5403impl std::fmt::Display for WhereClause {
5402 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5404 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5403 std::fmt::Display::fmt(self.syntax(), f) 5405 std::fmt::Display::fmt(self.syntax(), f)
5404 } 5406 }
5405} 5407}
5406impl std::fmt::Display for Abi { 5408impl std::fmt::Display for Abi {
5407 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5408 std::fmt::Display::fmt(self.syntax(), f) 5410 std::fmt::Display::fmt(self.syntax(), f)
5409 } 5411 }
5410} 5412}
5411impl std::fmt::Display for ExprStmt { 5413impl std::fmt::Display for ExprStmt {
5412 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5413 std::fmt::Display::fmt(self.syntax(), f) 5415 std::fmt::Display::fmt(self.syntax(), f)
5414 } 5416 }
5415} 5417}
5416impl std::fmt::Display for LetStmt { 5418impl std::fmt::Display for LetStmt {
5417 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5419 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5418 std::fmt::Display::fmt(self.syntax(), f) 5420 std::fmt::Display::fmt(self.syntax(), f)
5419 } 5421 }
5420} 5422}
5421impl std::fmt::Display for Condition { 5423impl std::fmt::Display for Condition {
5422 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5424 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5423 std::fmt::Display::fmt(self.syntax(), f) 5425 std::fmt::Display::fmt(self.syntax(), f)
5424 } 5426 }
5425} 5427}
5426impl std::fmt::Display for ParamList { 5428impl std::fmt::Display for ParamList {
5427 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5429 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5428 std::fmt::Display::fmt(self.syntax(), f) 5430 std::fmt::Display::fmt(self.syntax(), f)
5429 } 5431 }
5430} 5432}
5431impl std::fmt::Display for SelfParam { 5433impl std::fmt::Display for SelfParam {
5432 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5434 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5433 std::fmt::Display::fmt(self.syntax(), f) 5435 std::fmt::Display::fmt(self.syntax(), f)
5434 } 5436 }
5435} 5437}
5436impl std::fmt::Display for Param { 5438impl std::fmt::Display for Param {
5437 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5439 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5438 std::fmt::Display::fmt(self.syntax(), f) 5440 std::fmt::Display::fmt(self.syntax(), f)
5439 } 5441 }
5440} 5442}
5441impl std::fmt::Display for UseItem { 5443impl std::fmt::Display for UseItem {
5442 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5444 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5443 std::fmt::Display::fmt(self.syntax(), f) 5445 std::fmt::Display::fmt(self.syntax(), f)
5444 } 5446 }
5445} 5447}
5446impl std::fmt::Display for UseTree { 5448impl std::fmt::Display for UseTree {
5447 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5449 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5448 std::fmt::Display::fmt(self.syntax(), f) 5450 std::fmt::Display::fmt(self.syntax(), f)
5449 } 5451 }
5450} 5452}
5451impl std::fmt::Display for Alias { 5453impl std::fmt::Display for Alias {
5452 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5454 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5453 std::fmt::Display::fmt(self.syntax(), f) 5455 std::fmt::Display::fmt(self.syntax(), f)
5454 } 5456 }
5455} 5457}
5456impl std::fmt::Display for UseTreeList { 5458impl std::fmt::Display for UseTreeList {
5457 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5458 std::fmt::Display::fmt(self.syntax(), f) 5460 std::fmt::Display::fmt(self.syntax(), f)
5459 } 5461 }
5460} 5462}
5461impl std::fmt::Display for ExternCrateItem { 5463impl std::fmt::Display for ExternCrateItem {
5462 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5464 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5463 std::fmt::Display::fmt(self.syntax(), f) 5465 std::fmt::Display::fmt(self.syntax(), f)
5464 } 5466 }
5465} 5467}
5466impl std::fmt::Display for ArgList { 5468impl std::fmt::Display for ArgList {
5467 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5469 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5468 std::fmt::Display::fmt(self.syntax(), f) 5470 std::fmt::Display::fmt(self.syntax(), f)
5469 } 5471 }
5470} 5472}
5471impl std::fmt::Display for Path { 5473impl std::fmt::Display for Path {
5472 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5474 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5473 std::fmt::Display::fmt(self.syntax(), f) 5475 std::fmt::Display::fmt(self.syntax(), f)
5474 } 5476 }
5475} 5477}
5476impl std::fmt::Display for PathSegment { 5478impl std::fmt::Display for PathSegment {
5477 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5479 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5478 std::fmt::Display::fmt(self.syntax(), f) 5480 std::fmt::Display::fmt(self.syntax(), f)
5479 } 5481 }
5480} 5482}
5481impl std::fmt::Display for TypeArgList { 5483impl std::fmt::Display for TypeArgList {
5482 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5484 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5483 std::fmt::Display::fmt(self.syntax(), f) 5485 std::fmt::Display::fmt(self.syntax(), f)
5484 } 5486 }
5485} 5487}
5486impl std::fmt::Display for TypeArg { 5488impl std::fmt::Display for TypeArg {
5487 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5488 std::fmt::Display::fmt(self.syntax(), f) 5490 std::fmt::Display::fmt(self.syntax(), f)
5489 } 5491 }
5490} 5492}
5491impl std::fmt::Display for AssocTypeArg { 5493impl std::fmt::Display for AssocTypeArg {
5492 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5494 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5493 std::fmt::Display::fmt(self.syntax(), f) 5495 std::fmt::Display::fmt(self.syntax(), f)
5494 } 5496 }
5495} 5497}
5496impl std::fmt::Display for LifetimeArg { 5498impl std::fmt::Display for LifetimeArg {
5497 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5499 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5498 std::fmt::Display::fmt(self.syntax(), f) 5500 std::fmt::Display::fmt(self.syntax(), f)
5499 } 5501 }
5500} 5502}
5501impl std::fmt::Display for ConstArg { 5503impl std::fmt::Display for ConstArg {
5502 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5504 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5503 std::fmt::Display::fmt(self.syntax(), f) 5505 std::fmt::Display::fmt(self.syntax(), f)
5504 } 5506 }
5505} 5507}
5506impl std::fmt::Display for MacroItems { 5508impl std::fmt::Display for MacroItems {
5507 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5508 std::fmt::Display::fmt(self.syntax(), f) 5510 std::fmt::Display::fmt(self.syntax(), f)
5509 } 5511 }
5510} 5512}
5511impl std::fmt::Display for MacroStmts { 5513impl std::fmt::Display for MacroStmts {
5512 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5514 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5513 std::fmt::Display::fmt(self.syntax(), f) 5515 std::fmt::Display::fmt(self.syntax(), f)
5514 } 5516 }
5515} 5517}
5516impl std::fmt::Display for ExternItemList { 5518impl std::fmt::Display for ExternItemList {
5517 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5519 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5518 std::fmt::Display::fmt(self.syntax(), f) 5520 std::fmt::Display::fmt(self.syntax(), f)
5519 } 5521 }
5520} 5522}
5521impl std::fmt::Display for ExternBlock { 5523impl std::fmt::Display for ExternBlock {
5522 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5524 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5523 std::fmt::Display::fmt(self.syntax(), f) 5525 std::fmt::Display::fmt(self.syntax(), f)
5524 } 5526 }
5525} 5527}
5526impl std::fmt::Display for MetaItem { 5528impl std::fmt::Display for MetaItem {
5527 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5529 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5528 std::fmt::Display::fmt(self.syntax(), f) 5530 std::fmt::Display::fmt(self.syntax(), f)
5529 } 5531 }
5530} 5532}
5531impl std::fmt::Display for MacroDef { 5533impl std::fmt::Display for MacroDef {
5532 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5534 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5533 std::fmt::Display::fmt(self.syntax(), f) 5535 std::fmt::Display::fmt(self.syntax(), f)
5534 } 5536 }
5535} 5537}
diff --git a/crates/ra_syntax/src/ast/generated/tokens.rs b/crates/ra_syntax/src/ast/generated/tokens.rs
index f91befaac..abadd0b61 100644
--- a/crates/ra_syntax/src/ast/generated/tokens.rs
+++ b/crates/ra_syntax/src/ast/generated/tokens.rs
@@ -11,7 +11,7 @@ pub struct Whitespace {
11 pub(crate) syntax: SyntaxToken, 11 pub(crate) syntax: SyntaxToken,
12} 12}
13impl std::fmt::Display for Whitespace { 13impl std::fmt::Display for Whitespace {
14 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 std::fmt::Display::fmt(&self.syntax, f) 15 std::fmt::Display::fmt(&self.syntax, f)
16 } 16 }
17} 17}
@@ -32,7 +32,7 @@ pub struct Comment {
32 pub(crate) syntax: SyntaxToken, 32 pub(crate) syntax: SyntaxToken,
33} 33}
34impl std::fmt::Display for Comment { 34impl std::fmt::Display for Comment {
35 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 std::fmt::Display::fmt(&self.syntax, f) 36 std::fmt::Display::fmt(&self.syntax, f)
37 } 37 }
38} 38}
@@ -53,7 +53,7 @@ pub struct String {
53 pub(crate) syntax: SyntaxToken, 53 pub(crate) syntax: SyntaxToken,
54} 54}
55impl std::fmt::Display for String { 55impl std::fmt::Display for String {
56 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 std::fmt::Display::fmt(&self.syntax, f) 57 std::fmt::Display::fmt(&self.syntax, f)
58 } 58 }
59} 59}
@@ -74,7 +74,7 @@ pub struct RawString {
74 pub(crate) syntax: SyntaxToken, 74 pub(crate) syntax: SyntaxToken,
75} 75}
76impl std::fmt::Display for RawString { 76impl std::fmt::Display for RawString {
77 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 std::fmt::Display::fmt(&self.syntax, f) 78 std::fmt::Display::fmt(&self.syntax, f)
79 } 79 }
80} 80}
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 3cd6d99c3..2e72d4927 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -84,7 +84,7 @@ impl Whitespace {
84} 84}
85 85
86pub struct QuoteOffsets { 86pub struct QuoteOffsets {
87 pub quotes: [TextRange; 2], 87 pub quotes: (TextRange, TextRange),
88 pub contents: TextRange, 88 pub contents: TextRange,
89} 89}
90 90
@@ -103,7 +103,7 @@ impl QuoteOffsets {
103 let end = TextSize::of(literal); 103 let end = TextSize::of(literal);
104 104
105 let res = QuoteOffsets { 105 let res = QuoteOffsets {
106 quotes: [TextRange::new(start, left_quote), TextRange::new(right_quote, end)], 106 quotes: (TextRange::new(start, left_quote), TextRange::new(right_quote, end)),
107 contents: TextRange::new(left_quote, right_quote), 107 contents: TextRange::new(left_quote, right_quote),
108 }; 108 };
109 Some(res) 109 Some(res)
@@ -116,17 +116,17 @@ pub trait HasQuotes: AstToken {
116 let offsets = QuoteOffsets::new(text)?; 116 let offsets = QuoteOffsets::new(text)?;
117 let o = self.syntax().text_range().start(); 117 let o = self.syntax().text_range().start();
118 let offsets = QuoteOffsets { 118 let offsets = QuoteOffsets {
119 quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o], 119 quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o),
120 contents: offsets.contents + o, 120 contents: offsets.contents + o,
121 }; 121 };
122 Some(offsets) 122 Some(offsets)
123 } 123 }
124 fn open_quote_text_range(&self) -> Option<TextRange> { 124 fn open_quote_text_range(&self) -> Option<TextRange> {
125 self.quote_offsets().map(|it| it.quotes[0]) 125 self.quote_offsets().map(|it| it.quotes.0)
126 } 126 }
127 127
128 fn close_quote_text_range(&self) -> Option<TextRange> { 128 fn close_quote_text_range(&self) -> Option<TextRange> {
129 self.quote_offsets().map(|it| it.quotes[1]) 129 self.quote_offsets().map(|it| it.quotes.1)
130 } 130 }
131 131
132 fn text_range_between_quotes(&self) -> Option<TextRange> { 132 fn text_range_between_quotes(&self) -> Option<TextRange> {
@@ -335,16 +335,26 @@ pub trait HasFormatSpecifier: AstToken {
335 } 335 }
336 c if c == '_' || c.is_alphabetic() => { 336 c if c == '_' || c.is_alphabetic() => {
337 read_identifier(&mut chars, &mut callback); 337 read_identifier(&mut chars, &mut callback);
338 if chars.peek().and_then(|next| next.1.as_ref().ok()).copied() 338 // can be either width (indicated by dollar sign, or type in which case
339 != Some('$') 339 // the next sign has to be `}`)
340 { 340 let next =
341 continue; 341 chars.peek().and_then(|next| next.1.as_ref().ok()).copied();
342 } 342 match next {
343 skip_char_and_emit( 343 Some('$') => skip_char_and_emit(
344 &mut chars, 344 &mut chars,
345 FormatSpecifier::DollarSign, 345 FormatSpecifier::DollarSign,
346 &mut callback, 346 &mut callback,
347 ); 347 ),
348 Some('}') => {
349 skip_char_and_emit(
350 &mut chars,
351 FormatSpecifier::Close,
352 &mut callback,
353 );
354 continue;
355 }
356 _ => continue,
357 };
348 } 358 }
349 _ => {} 359 _ => {}
350 } 360 }
@@ -416,17 +426,11 @@ pub trait HasFormatSpecifier: AstToken {
416 } 426 }
417 } 427 }
418 428
419 let mut cloned = chars.clone().take(2); 429 if let Some((_, Ok('}'))) = chars.peek() {
420 let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); 430 skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
421 let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); 431 } else {
422 if first != Some('}') {
423 continue;
424 }
425 if second == Some('}') {
426 // Escaped format end specifier, `}}`
427 continue; 432 continue;
428 } 433 }
429 skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
430 } 434 }
431 _ => { 435 _ => {
432 while let Some((_, Ok(next_char))) = chars.peek() { 436 while let Some((_, Ok(next_char))) = chars.peek() {
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs
index bfc05e08b..a8f2454fd 100644
--- a/crates/ra_syntax/src/ast/traits.rs
+++ b/crates/ra_syntax/src/ast/traits.rs
@@ -83,13 +83,22 @@ pub trait DocCommentsOwner: AstNode {
83 CommentIter { iter: self.syntax().children_with_tokens() } 83 CommentIter { iter: self.syntax().children_with_tokens() }
84 } 84 }
85 85
86 fn doc_comment_text(&self) -> Option<String> {
87 self.doc_comments().doc_comment_text()
88 }
89}
90
91impl CommentIter {
92 pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter {
93 CommentIter { iter: syntax_node.children_with_tokens() }
94 }
95
86 /// Returns the textual content of a doc comment block as a single string. 96 /// Returns the textual content of a doc comment block as a single string.
87 /// That is, strips leading `///` (+ optional 1 character of whitespace), 97 /// That is, strips leading `///` (+ optional 1 character of whitespace),
88 /// trailing `*/`, trailing whitespace and then joins the lines. 98 /// trailing `*/`, trailing whitespace and then joins the lines.
89 fn doc_comment_text(&self) -> Option<String> { 99 pub fn doc_comment_text(self) -> Option<String> {
90 let mut has_comments = false; 100 let mut has_comments = false;
91 let docs = self 101 let docs = self
92 .doc_comments()
93 .filter(|comment| comment.kind().doc.is_some()) 102 .filter(|comment| comment.kind().doc.is_some())
94 .map(|comment| { 103 .map(|comment| {
95 has_comments = true; 104 has_comments = true;
diff --git a/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast b/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast
index 7c957fdde..48610a5eb 100644
--- a/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast
+++ b/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast
@@ -180,44 +180,45 @@ [email protected]
180 [email protected] 180 [email protected]
181 [email protected] 181 [email protected]
182 [email protected] "(" 182 [email protected] "("
183 [email protected] 183 [email protected]
184 [email protected] 184 [email protected] "for"
185 [email protected] 185 [email protected]
186 [email protected] 186 [email protected]
187 [email protected] 187 [email protected]
188 [email protected] 188 [email protected] "<"
189 [email protected] "for" 189 [email protected]
190 [email protected] 190 [email protected] "\'a"
191 [email protected] "<" 191 [email protected] ">"
192 [email protected] 192 [email protected] " "
193 [email protected] "\'a" 193 [email protected]
194 [email protected] ">" 194 [email protected]
195 [email protected] " " 195 [email protected]
196 [email protected]
196 [email protected] 197 [email protected]
197 [email protected] 198 [email protected]
198 [email protected] 199 [email protected]
199 [email protected] 200 [email protected]
200 [email protected] "Trait" 201 [email protected] "Trait"
201 [email protected] "<" 202 [email protected] "<"
202 [email protected] 203 [email protected]
203 [email protected] "\'a" 204 [email protected] "\'a"
204 [email protected] ">" 205 [email protected] ">"
205 [email protected] 206 [email protected]
206 [email protected] ")" 207 [email protected] ")"
207 [email protected] " " 208 [email protected] " "
208 [email protected] "+" 209 [email protected] "+"
209 [email protected] " " 210 [email protected] " "
210 [email protected] 211 [email protected]
211 [email protected] "(" 212 [email protected] "("
212 [email protected] 213 [email protected]
213 [email protected] 214 [email protected]
214 [email protected] 215 [email protected]
215 [email protected] 216 [email protected]
216 [email protected] "Copy" 217 [email protected] "Copy"
217 [email protected] ")" 218 [email protected] ")"
218 [email protected] ">" 219 [email protected] ">"
219 [email protected] 220 [email protected]
220 [email protected] ";" 221 [email protected] ";"
221 [email protected] "\n " 222 [email protected] "\n "
222 [email protected] 223 [email protected]
223 [email protected] "let" 224 [email protected] "let"
@@ -302,13 +303,12 @@ error 146..146: expected expression
302error 147..147: expected SEMICOLON 303error 147..147: expected SEMICOLON
303error 148..148: expected expression 304error 148..148: expected expression
304error 149..149: expected SEMICOLON 305error 149..149: expected SEMICOLON
305error 154..154: expected pattern 306error 155..155: expected type
306error 155..155: expected IN_KW 307error 158..158: expected IN_KW
307error 155..155: expected expression
308error 157..157: expected a block
309error 165..165: expected expression 308error 165..165: expected expression
310error 168..168: expected expression 309error 168..168: expected expression
311error 179..179: expected expression 310error 179..179: expected expression
311error 180..180: expected a block
312error 180..180: expected COMMA 312error 180..180: expected COMMA
313error 180..180: expected expression 313error 180..180: expected expression
314error 180..180: expected R_PAREN 314error 180..180: expected R_PAREN
diff --git a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
index 568a4cc02..4d6461d1e 100644
--- a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
+++ b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
@@ -12,17 +12,16 @@ [email protected]
12 [email protected] "where" 12 [email protected] "where"
13 [email protected] " " 13 [email protected] " "
14 [email protected] 14 [email protected]
15 [email protected] 15 [email protected] "for"
16 [email protected] "for" 16 [email protected]
17 [email protected] 17 [email protected] "<"
18 [email protected] "<" 18 [email protected]
19 [email protected] 19 [email protected] "\'a"
20 [email protected] "\'a" 20 [email protected] ">"
21 [email protected] ">"
22 [email protected] "\n" 21 [email protected] "\n"
23 [email protected] 22 [email protected]
24 [email protected] "{" 23 [email protected] "{"
25 [email protected] "}" 24 [email protected] "}"
26 [email protected] "\n" 25 [email protected] "\n"
27error 26..26: expected a path 26error 26..26: expected type
28error 26..26: expected colon 27error 26..26: expected colon
diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rast b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast
new file mode 100644
index 000000000..8eb583ef8
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast
@@ -0,0 +1,40 @@
1[email protected]
2 [email protected]
3 [email protected] "trait"
4 [email protected] " "
5 [email protected]
6 [email protected] "T"
7 [email protected] " "
8 [email protected]
9 [email protected] "{"
10 [email protected] "\n "
11 [email protected]
12 [email protected]
13 [email protected]
14 [email protected]
15 [email protected] "default"
16 [email protected] " "
17 [email protected]
18 [email protected] "const"
19 [email protected] " "
20 [email protected]
21 [email protected] "f"
22 [email protected] ":"
23 [email protected] " "
24 [email protected]
25 [email protected]
26 [email protected]
27 [email protected]
28 [email protected] "u8"
29 [email protected] " "
30 [email protected] "="
31 [email protected] " "
32 [email protected]
33 [email protected] "0"
34 [email protected] ";"
35 [email protected] "\n"
36 [email protected] "}"
37 [email protected] "\n"
38error 19..19: expected BANG
39error 19..19: expected `{`, `[`, `(`
40error 19..19: expected SEMICOLON
diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rs b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs
new file mode 100644
index 000000000..80f15474a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs
@@ -0,0 +1,3 @@
1trait T {
2 default const f: u8 = 0;
3}
diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast
new file mode 100644
index 000000000..cb90f28bc
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast
@@ -0,0 +1,240 @@
1[email protected]
2 [email protected]
3 [email protected] "type"
4 [email protected] " "
5 [email protected]
6 [email protected] "ForRef"
7 [email protected] " "
8 [email protected] "="
9 [email protected] " "
10 [email protected]
11 [email protected] "for"
12 [email protected]
13 [email protected] "<"
14 [email protected]
15 [email protected] "\'a"
16 [email protected] ">"
17 [email protected] " "
18 [email protected]
19 [email protected] "&"
20 [email protected] "\'a"
21 [email protected] " "
22 [email protected]
23 [email protected]
24 [email protected]
25 [email protected]
26 [email protected] "u32"
27 [email protected] ";"
28 [email protected] "\n"
29 [email protected]
30 [email protected] "type"
31 [email protected] " "
32 [email protected]
33 [email protected] "ForTup"
34 [email protected] " "
35 [email protected] "="
36 [email protected] " "
37 [email protected]
38 [email protected] "for"
39 [email protected]
40 [email protected] "<"
41 [email protected]
42 [email protected] "\'a"
43 [email protected] ">"
44 [email protected] " "
45 [email protected]
46 [email protected] "("
47 [email protected]
48 [email protected] "&"
49 [email protected] "\'a"
50 [email protected] " "
51 [email protected]
52 [email protected]
53 [email protected]
54 [email protected]
55 [email protected] "u32"
56 [email protected] ","
57 [email protected] ")"
58 [email protected] ";"
59 [email protected] "\n"
60 [email protected]
61 [email protected] "type"
62 [email protected] " "
63 [email protected]
64 [email protected] "ForSlice"
65 [email protected] " "
66 [email protected] "="
67 [email protected] " "
68 [email protected]
69 [email protected] "for"
70 [email protected]
71 [email protected] "<"
72 [email protected]
73 [email protected] "\'a"
74 [email protected] ">"
75 [email protected] " "
76 [email protected]
77 [email protected] "["
78 [email protected]
79 [email protected]
80 [email protected]
81 [email protected]
82 [email protected] "u32"
83 [email protected] "]"
84 [email protected] ";"
85 [email protected] "\n"
86 [email protected]
87 [email protected] "type"
88 [email protected] " "
89 [email protected]
90 [email protected] "ForForFn"
91 [email protected] " "
92 [email protected] "="
93 [email protected] " "
94 [email protected]
95 [email protected] "for"
96 [email protected]
97 [email protected] "<"
98 [email protected]
99 [email protected] "\'a"
100 [email protected] ">"
101 [email protected] " "
102 [email protected]
103 [email protected] "for"
104 [email protected]
105 [email protected] "<"
106 [email protected]
107 [email protected] "\'b"
108 [email protected] ">"
109 [email protected] " "
110 [email protected]
111 [email protected] "fn"
112 [email protected]
113 [email protected] "("
114 [email protected]
115 [email protected]
116 [email protected] "&"
117 [email protected] "\'a"
118 [email protected] " "
119 [email protected]
120 [email protected]
121 [email protected]
122 [email protected]
123 [email protected] "i32"
124 [email protected] ","
125 [email protected] " "
126 [email protected]
127 [email protected]
128 [email protected] "&"
129 [email protected] "\'b"
130 [email protected] " "
131 [email protected]
132 [email protected]
133 [email protected]
134 [email protected]
135 [email protected] "i32"
136 [email protected] ")"
137 [email protected] ";"
138 [email protected] "\n"
139 [email protected]
140 [email protected] "fn"
141 [email protected] " "
142 [email protected]
143 [email protected] "for_for_for"
144 [email protected]
145 [email protected] "<"
146 [email protected]
147 [email protected]
148 [email protected] "T"
149 [email protected] ">"
150 [email protected]
151 [email protected] "("
152 [email protected] ")"
153 [email protected] "\n"
154 [email protected]
155 [email protected] "where"
156 [email protected] "\n "
157 [email protected]
158 [email protected] "for"
159 [email protected]
160 [email protected] "<"
161 [email protected]
162 [email protected] "\'a"
163 [email protected] ">"
164 [email protected] " "
165 [email protected]
166 [email protected] "for"
167 [email protected]
168 [email protected] "<"
169 [email protected]
170 [email protected] "\'b"
171 [email protected] ">"
172 [email protected] " "
173 [email protected]
174 [email protected] "for"
175 [email protected]
176 [email protected] "<"
177 [email protected]
178 [email protected] "\'c"
179 [email protected] ">"
180 [email protected] " "
181 [email protected]
182 [email protected] "fn"
183 [email protected]
184 [email protected] "("
185 [email protected]
186 [email protected]
187 [email protected] "&"
188 [email protected] "\'a"
189 [email protected] " "
190 [email protected]
191 [email protected]
192 [email protected]
193 [email protected]
194 [email protected] "T"
195 [email protected] ","
196 [email protected] " "
197 [email protected]
198 [email protected]
199 [email protected] "&"
200 [email protected] "\'b"
201 [email protected] " "
202 [email protected]
203 [email protected]
204 [email protected]
205 [email protected]
206 [email protected] "T"
207 [email protected] ","
208 [email protected] " "
209 [email protected]
210 [email protected]
211 [email protected] "&"
212 [email protected] "\'c"
213 [email protected] " "
214 [email protected]
215 [email protected]
216 [email protected]
217 [email protected]
218 [email protected] "T"
219 [email protected] ")"
220 [email protected] ":"
221 [email protected] " "
222 [email protected]
223 [email protected]
224 [email protected]
225 [email protected]
226 [email protected]
227 [email protected]
228 [email protected] "Copy"
229 [email protected] ","
230 [email protected] "\n"
231 [email protected]
232 [email protected] "{"
233 [email protected] "\n"
234 [email protected] "}"
235 [email protected] "\n"
236error 21..21: expected a function pointer or path
237error 52..52: expected a function pointer or path
238error 88..88: expected a function pointer or path
239error 119..119: expected a function pointer or path
240error 195..195: expected a function pointer or path
diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs
new file mode 100644
index 000000000..0e9f8ccb4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs
@@ -0,0 +1,9 @@
1type ForRef = for<'a> &'a u32;
2type ForTup = for<'a> (&'a u32,);
3type ForSlice = for<'a> [u32];
4type ForForFn = for<'a> for<'b> fn(&'a i32, &'b i32);
5fn for_for_for<T>()
6where
7 for<'a> for<'b> for<'c> fn(&'a T, &'b T, &'c T): Copy,
8{
9}
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast
index 9be441110..53f7ebaf9 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast
+++ b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast
@@ -17,23 +17,29 @@ [email protected]
17 [email protected] "{" 17 [email protected] "{"
18 [email protected] "}" 18 [email protected] "}"
19 [email protected] "\n" 19 [email protected] "\n"
20 ERROR@25..31 20 CONST_DEF@25..46
21 [email protected] "unsafe" 21 [email protected] "unsafe"
22 [email protected] " " 22 [email protected] " "
23 [email protected]
24 [email protected] "const" 23 [email protected] "const"
25 [email protected] " " 24 [email protected] " "
26 [email protected] "fn" 25 [email protected]
26 [email protected] "fn"
27 [email protected] " " 27 [email protected] " "
28 [email protected] 28 [email protected]
29 [email protected] "bar" 29 [email protected]
30 [email protected] 30 [email protected]
31 [email protected] "(" 31 [email protected]
32 [email protected] ")" 32 [email protected] "bar"
33 [email protected] " " 33 [email protected]
34 [email protected] 34 [email protected] "("
35 [email protected] "{" 35 [email protected] ")"
36 [email protected] "}" 36 [email protected] " "
37 [email protected]
38 [email protected] "{"
39 [email protected] "}"
37 [email protected] "\n" 40 [email protected] "\n"
38error 6..6: expected existential, fn, trait or impl 41error 6..6: expected existential, fn, trait or impl
39error 31..31: expected existential, fn, trait or impl 42error 38..38: expected a name
43error 40..40: expected COLON
44error 46..46: expected SEMICOLON
45error 47..47: expected an item
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
index 9dc473e43..cd0892451 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
@@ -1,61 +1,60 @@
1[email protected]9 1SOURCE_FILE@0..54
2 FN_DEF@0..48 2 FN_DEF@0..53
3 [email protected] "fn" 3 [email protected] "fn"
4 [email protected] " " 4 [email protected] " "
5 [email protected] 5 [email protected]
6 [email protected] "test" 6 [email protected] "for_trait"
7 [email protected] 7 [email protected]
8 [email protected] "<" 8 [email protected] "<"
9 [email protected] 9 [email protected]
10 [email protected] 10 [email protected]
11 [email protected] "F" 11 [email protected] "F"
12 [email protected] ">" 12 [email protected] ">"
13 [email protected] 13 [email protected]
14 [email protected] "(" 14 [email protected] "("
15 [email protected] ")" 15 [email protected] ")"
16 [email protected] "\n" 16 [email protected] "\n"
17 [email protected] 17 [email protected]
18 [email protected] "where" 18 [email protected] "where"
19 [email protected] "\n " 19 [email protected] "\n "
20 [email protected] 20 [email protected]
21 [email protected] 21 [email protected] "for"
22 [email protected] "for" 22 [email protected]
23 [email protected] 23 [email protected] "<"
24 [email protected] "<" 24 [email protected]
25 [email protected] 25 [email protected] "\'a"
26 [email protected] "\'a" 26 [email protected] ">"
27 [email protected] ">" 27 [email protected] " "
28 [email protected] " " 28 [email protected]
29 [email protected] 29 [email protected]
30 [email protected] 30 [email protected]
31 [email protected] 31 [email protected]
32 [email protected] 32 [email protected] "F"
33 [email protected] "F" 33 [email protected] ":"
34 [email protected] ":" 34 [email protected] " "
35 [email protected] " " 35 [email protected]
36 [email protected] 36 [email protected]
37 [email protected] 37 [email protected]
38 [email protected] 38 [email protected]
39 [email protected] 39 [email protected]
40 [email protected] 40 [email protected]
41 [email protected] 41 [email protected] "Fn"
42 [email protected] "Fn" 42 [email protected]
43 [email protected] 43 [email protected] "("
44 [email protected] "(" 44 [email protected]
45 [email protected] 45 [email protected]
46 [email protected] 46 [email protected] "&"
47 [email protected] "&" 47 [email protected] "\'a"
48 [email protected] "\'a" 48 [email protected] " "
49 [email protected] " " 49 [email protected]
50 [email protected] 50 [email protected]
51 [email protected] 51 [email protected]
52 [email protected] 52 [email protected]
53 [email protected] 53 [email protected] "str"
54 [email protected] "str" 54 [email protected] ")"
55 [email protected] ")" 55 [email protected] "\n"
56 [email protected] "\n" 56 [email protected]
57 [email protected] 57 [email protected] "{"
58 [email protected] "{" 58 [email protected] " "
59 [email protected] " " 59 [email protected] "}"
60 [email protected] "}" 60 [email protected] "\n"
61 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
index b448c6178..423bc105b 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
@@ -1,4 +1,4 @@
1fn test<F>() 1fn for_trait<F>()
2where 2where
3 for<'a> F: Fn(&'a str) 3 for<'a> F: Fn(&'a str)
4{ } 4{ }
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
index dfb8d57ad..b26ac2d36 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
@@ -1,4 +1,4 @@
1[email protected]00 1SOURCE_FILE@0..121
2 [email protected] 2 [email protected]
3 [email protected] "type" 3 [email protected] "type"
4 [email protected] " " 4 [email protected] " "
@@ -29,212 +29,84 @@ [email protected]
29 [email protected] ")" 29 [email protected] ")"
30 [email protected] ";" 30 [email protected] ";"
31 [email protected] "\n" 31 [email protected] "\n"
32 [email protected] 32 [email protected]
33 [email protected] "fn" 33 [email protected] "type"
34 [email protected] " " 34 [email protected] " "
35 [email protected] 35 [email protected]
36 [email protected] "foo" 36 [email protected] "B"
37 [email protected] 37 [email protected] " "
38 [email protected] "<" 38 [email protected] "="
39 [email protected] 39 [email protected] " "
40 [email protected] 40 [email protected]
41 [email protected] "T" 41 [email protected] "for"
42 [email protected] ">" 42 [email protected]
43 [email protected] 43 [email protected] "<"
44 [email protected] "(" 44 [email protected]
45 [email protected] 45 [email protected] "\'a"
46 [email protected] 46 [email protected] ">"
47 [email protected] 47 [email protected] " "
48 [email protected] "_t" 48 [email protected]
49 [email protected] ":" 49 [email protected] "unsafe"
50 [email protected] " " 50 [email protected] " "
51 [email protected] 51 [email protected]
52 [email protected] "&" 52 [email protected] "extern"
53 [email protected] 53 [email protected] " "
54 [email protected] 54 [email protected] "\"C\""
55 [email protected] 55 [email protected] " "
56 [email protected] 56 [email protected] "fn"
57 [email protected] "T" 57 [email protected]
58 [email protected] ")" 58 [email protected] "("
59 [email protected] " " 59 [email protected]
60 [email protected] 60 [email protected]
61 [email protected] "where" 61 [email protected] "&"
62 [email protected] " " 62 [email protected] "\'a"
63 [email protected] 63 [email protected] " "
64 [email protected] 64 [email protected]
65 [email protected] "for" 65 [email protected] "("
66 [email protected] 66 [email protected] ")"
67 [email protected] "<" 67 [email protected] ")"
68 [email protected] 68 [email protected] " "
69 [email protected] "\'a" 69 [email protected]
70 [email protected] ">" 70 [email protected] "->"
71 [email protected] " " 71 [email protected] " "
72 [email protected] 72 [email protected]
73 [email protected] "&" 73 [email protected] "("
74 [email protected] "\'a" 74 [email protected] ")"
75 [email protected] " " 75 [email protected] ";"
76 [email protected] 76 [email protected] "\n"
77 [email protected] 77 [email protected]
78 [email protected] 78 [email protected] "type"
79 [email protected] 79 [email protected] " "
80 [email protected] "T" 80 [email protected]
81 [email protected] ":" 81 [email protected] "Obj"
82 [email protected] " " 82 [email protected] " "
83 [email protected] 83 [email protected] "="
84 [email protected] 84 [email protected] " "
85 [email protected] 85 [email protected]
86 [email protected] 86 [email protected] "for"
87 [email protected] 87 [email protected]
88 [email protected] 88 [email protected] "<"
89 [email protected] "Iterator" 89 [email protected]
90 [email protected] " " 90 [email protected] "\'a"
91 [email protected] 91 [email protected] ">"
92 [email protected] "{" 92 [email protected] " "
93 [email protected] "}" 93 [email protected]
94 [email protected] "\n" 94 [email protected]
95 [email protected] 95 [email protected]
96 [email protected] "fn" 96 [email protected]
97 [email protected] " " 97 [email protected] "PartialEq"
98 [email protected] 98 [email protected]
99 [email protected] "bar" 99 [email protected] "<"
100 [email protected] 100 [email protected]
101 [email protected] "<" 101 [email protected]
102 [email protected] 102 [email protected] "&"
103 [email protected] 103 [email protected] "\'a"
104 [email protected] "T" 104 [email protected] " "
105 [email protected] ">" 105 [email protected]
106 [email protected] 106 [email protected]
107 [email protected] "(" 107 [email protected]
108 [email protected] 108 [email protected]
109 [email protected] 109 [email protected] "i32"
110 [email protected] 110 [email protected] ">"
111 [email protected] "_t" 111 [email protected] ";"
112 [email protected] ":" 112 [email protected] "\n"
113 [email protected] " "
114 [email protected]
115 [email protected] "&"
116 [email protected]
117 [email protected]
118 [email protected]
119 [email protected]
120 [email protected] "T"
121 [email protected] ")"
122 [email protected] " "
123 [email protected]
124 [email protected] "where"
125 [email protected] " "
126 [email protected]
127 [email protected]
128 [email protected] "for"
129 [email protected]
130 [email protected] "<"
131 [email protected]
132 [email protected] "\'a"
133 [email protected] ">"
134 [email protected] " "
135 [email protected]
136 [email protected] "&"
137 [email protected] "\'a"
138 [email protected] " "
139 [email protected] "mut"
140 [email protected] " "
141 [email protected]
142 [email protected]
143 [email protected]
144 [email protected]
145 [email protected] "T"
146 [email protected] ":"
147 [email protected] " "
148 [email protected]
149 [email protected]
150 [email protected]
151 [email protected]
152 [email protected]
153 [email protected]
154 [email protected] "Iterator"
155 [email protected] " "
156 [email protected]
157 [email protected] "{"
158 [email protected] "}"
159 [email protected] "\n"
160 [email protected]
161 [email protected] "fn"
162 [email protected] " "
163 [email protected]
164 [email protected] "baz"
165 [email protected]
166 [email protected] "<"
167 [email protected]
168 [email protected]
169 [email protected] "T"
170 [email protected] ">"
171 [email protected]
172 [email protected] "("
173 [email protected]
174 [email protected]
175 [email protected]
176 [email protected] "_t"
177 [email protected] ":"
178 [email protected] " "
179 [email protected]
180 [email protected] "&"
181 [email protected]
182 [email protected]
183 [email protected]
184 [email protected]
185 [email protected] "T"
186 [email protected] ")"
187 [email protected] " "
188 [email protected]
189 [email protected] "where"
190 [email protected] " "
191 [email protected]
192 [email protected]
193 [email protected] "for"
194 [email protected]
195 [email protected] "<"
196 [email protected]
197 [email protected] "\'a"
198 [email protected] ">"
199 [email protected] " "
200 [email protected]
201 [email protected]
202 [email protected]
203 [email protected]
204 [email protected] "<"
205 [email protected]
206 [email protected] "&"
207 [email protected] "\'a"
208 [email protected] " "
209 [email protected]
210 [email protected]
211 [email protected]
212 [email protected]
213 [email protected] "T"
214 [email protected] " "
215 [email protected] "as"
216 [email protected] " "
217 [email protected]
218 [email protected]
219 [email protected]
220 [email protected]
221 [email protected] "Baz"
222 [email protected] ">"
223 [email protected] "::"
224 [email protected]
225 [email protected]
226 [email protected] "Foo"
227 [email protected] ":"
228 [email protected] " "
229 [email protected]
230 [email protected]
231 [email protected]
232 [email protected]
233 [email protected]
234 [email protected]
235 [email protected] "Iterator"
236 [email protected] " "
237 [email protected]
238 [email protected] "{"
239 [email protected] "}"
240 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
index d6774d438..8ac7b9e10 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
@@ -1,4 +1,3 @@
1type A = for<'a> fn() -> (); 1type A = for<'a> fn() -> ();
2fn foo<T>(_t: &T) where for<'a> &'a T: Iterator {} 2type B = for<'a> unsafe extern "C" fn(&'a ()) -> ();
3fn bar<T>(_t: &T) where for<'a> &'a mut T: Iterator {} 3type Obj = for<'a> PartialEq<&'a i32>;
4fn baz<T>(_t: &T) where for<'a> <&'a T as Baz>::Foo: Iterator {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast
new file mode 100644
index 000000000..adb6159f4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast
@@ -0,0 +1,40 @@
1[email protected]
2 [email protected]
3 [email protected] "impl"
4 [email protected] " "
5 [email protected]
6 [email protected]
7 [email protected]
8 [email protected]
9 [email protected] "T"
10 [email protected] " "
11 [email protected] "for"
12 [email protected] " "
13 [email protected]
14 [email protected]
15 [email protected]
16 [email protected]
17 [email protected] "Foo"
18 [email protected] " "
19 [email protected]
20 [email protected] "{"
21 [email protected] "\n "
22 [email protected]
23 [email protected] "default"
24 [email protected] " "
25 [email protected] "unsafe"
26 [email protected] " "
27 [email protected] "fn"
28 [email protected] " "
29 [email protected]
30 [email protected] "foo"
31 [email protected]
32 [email protected] "("
33 [email protected] ")"
34 [email protected] " "
35 [email protected]
36 [email protected] "{"
37 [email protected] "}"
38 [email protected] "\n"
39 [email protected] "}"
40 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs
new file mode 100644
index 000000000..12926cd8a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs
@@ -0,0 +1,3 @@
1impl T for Foo {
2 default unsafe fn foo() {}
3}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast
new file mode 100644
index 000000000..a9eda5668
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast
@@ -0,0 +1,18 @@
1[email protected]
2 [email protected]
3 [email protected] "default"
4 [email protected] " "
5 [email protected] "unsafe"
6 [email protected] " "
7 [email protected] "impl"
8 [email protected] " "
9 [email protected]
10 [email protected]
11 [email protected]
12 [email protected]
13 [email protected] "Foo"
14 [email protected] " "
15 [email protected]
16 [email protected] "{"
17 [email protected] "}"
18 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs
new file mode 100644
index 000000000..ba0998ff4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs
@@ -0,0 +1 @@
default unsafe impl Foo {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast
new file mode 100644
index 000000000..868899275
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast
@@ -0,0 +1,38 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "main"
7 [email protected]
8 [email protected] "("
9 [email protected] ")"
10 [email protected] " "
11 [email protected]
12 [email protected] "{"
13 [email protected] " "
14 [email protected]
15 [email protected] "let"
16 [email protected] " "
17 [email protected]
18 [email protected]
19 [email protected]
20 [email protected]
21 [email protected] "<"
22 [email protected]
23 [email protected] "_"
24 [email protected] ">"
25 [email protected] "::"
26 [email protected]
27 [email protected]
28 [email protected] "Foo"
29 [email protected] " "
30 [email protected] "="
31 [email protected] " "
32 [email protected]
33 [email protected] "("
34 [email protected] ")"
35 [email protected] ";"
36 [email protected] " "
37 [email protected] "}"
38 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs
new file mode 100644
index 000000000..ebe26834d
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs
@@ -0,0 +1 @@
fn main() { let <_>::Foo = (); }
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast
new file mode 100644
index 000000000..dab0247ee
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast
@@ -0,0 +1,44 @@
1[email protected]
2 [email protected]
3 [email protected] "impl"
4 [email protected] " "
5 [email protected]
6 [email protected]
7 [email protected]
8 [email protected]
9 [email protected] "T"
10 [email protected] " "
11 [email protected] "for"
12 [email protected] " "
13 [email protected]
14 [email protected]
15 [email protected]
16 [email protected]
17 [email protected] "Foo"
18 [email protected] " "
19 [email protected]
20 [email protected] "{"
21 [email protected] "\n "
22 [email protected]
23 [email protected] "default"
24 [email protected] " "
25 [email protected] "const"
26 [email protected] " "
27 [email protected]
28 [email protected] "f"
29 [email protected] ":"
30 [email protected] " "
31 [email protected]
32 [email protected]
33 [email protected]
34 [email protected]
35 [email protected] "u8"
36 [email protected] " "
37 [email protected] "="
38 [email protected] " "
39 [email protected]
40 [email protected] "0"
41 [email protected] ";"
42 [email protected] "\n"
43 [email protected] "}"
44 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs
new file mode 100644
index 000000000..dfb3b92dc
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs
@@ -0,0 +1,3 @@
1impl T for Foo {
2 default const f: u8 = 0;
3}
diff --git a/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast
new file mode 100644
index 000000000..503585103
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast
@@ -0,0 +1,392 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "for_trait"
7 [email protected]
8 [email protected] "<"
9 [email protected]
10 [email protected]
11 [email protected] "F"
12 [email protected] ">"
13 [email protected]
14 [email protected] "("
15 [email protected] ")"
16 [email protected] "\n"
17 [email protected]
18 [email protected] "where"
19 [email protected] "\n "
20 [email protected]
21 [email protected] "for"
22 [email protected]
23 [email protected] "<"
24 [email protected]
25 [email protected] "\'a"
26 [email protected] ">"
27 [email protected] " "
28 [email protected]
29 [email protected]
30 [email protected]
31 [email protected]
32 [email protected] "F"
33 [email protected] ":"
34 [email protected] " "
35 [email protected]
36 [email protected]
37 [email protected]
38 [email protected]
39 [email protected]
40 [email protected]
41 [email protected] "Fn"
42 [email protected]
43 [email protected] "("
44 [email protected]
45 [email protected]
46 [email protected] "&"
47 [email protected] "\'a"
48 [email protected] " "
49 [email protected]
50 [email protected]
51 [email protected]
52 [email protected]
53 [email protected] "str"
54 [email protected] ")"
55 [email protected] ","
56 [email protected] "\n"
57 [email protected]
58 [email protected] "{"
59 [email protected] "\n"
60 [email protected] "}"
61 [email protected] "\n"
62 [email protected]
63 [email protected] "fn"
64 [email protected] " "
65 [email protected]
66 [email protected] "for_ref"
67 [email protected]
68 [email protected] "<"
69 [email protected]
70 [email protected]
71 [email protected] "F"
72 [email protected] ">"
73 [email protected]
74 [email protected] "("
75 [email protected] ")"
76 [email protected] "\n"
77 [email protected]
78 [email protected] "where"
79 [email protected] "\n "
80 [email protected]
81 [email protected] "for"
82 [email protected]
83 [email protected] "<"
84 [email protected]
85 [email protected] "\'a"
86 [email protected] ">"
87 [email protected] " "
88 [email protected]
89 [email protected] "&"
90 [email protected] "\'a"
91 [email protected] " "
92 [email protected]
93 [email protected]
94 [email protected]
95 [email protected]
96 [email protected] "F"
97 [email protected] ":"
98 [email protected] " "
99 [email protected]
100 [email protected]
101 [email protected]
102 [email protected]
103 [email protected]
104 [email protected]
105 [email protected] "Debug"
106 [email protected] ","
107 [email protected] "\n"
108 [email protected]
109 [email protected] "{"
110 [email protected] "\n"
111 [email protected] "}"
112 [email protected] "\n"
113 [email protected]
114 [email protected] "fn"
115 [email protected] " "
116 [email protected]
117 [email protected] "for_parens"
118 [email protected]
119 [email protected] "<"
120 [email protected]
121 [email protected]
122 [email protected] "F"
123 [email protected] ">"
124 [email protected]
125 [email protected] "("
126 [email protected] ")"
127 [email protected] "\n"
128 [email protected]
129 [email protected] "where"
130 [email protected] "\n "
131 [email protected]
132 [email protected] "for"
133 [email protected]
134 [email protected] "<"
135 [email protected]
136 [email protected] "\'a"
137 [email protected] ">"
138 [email protected] " "
139 [email protected]
140 [email protected] "("
141 [email protected]
142 [email protected] "&"
143 [email protected] "\'a"
144 [email protected] " "
145 [email protected]
146 [email protected]
147 [email protected]
148 [email protected]
149 [email protected] "F"
150 [email protected] ")"
151 [email protected] ":"
152 [email protected] " "
153 [email protected]
154 [email protected]
155 [email protected]
156 [email protected]
157 [email protected]
158 [email protected]
159 [email protected] "Fn"
160 [email protected]
161 [email protected] "("
162 [email protected]
163 [email protected]
164 [email protected] "&"
165 [email protected] "\'a"
166 [email protected] " "
167 [email protected]
168 [email protected]
169 [email protected]
170 [email protected]
171 [email protected] "str"
172 [email protected] ")"
173 [email protected] ","
174 [email protected] "\n"
175 [email protected]
176 [email protected] "{"
177 [email protected] "\n"
178 [email protected] "}"
179 [email protected] "\n"
180 [email protected]
181 [email protected] "fn"
182 [email protected] " "
183 [email protected]
184 [email protected] "for_slice"
185 [email protected]
186 [email protected] "<"
187 [email protected]
188 [email protected]
189 [email protected] "F"
190 [email protected] ">"
191 [email protected]
192 [email protected] "("
193 [email protected] ")"
194 [email protected] "\n"
195 [email protected]
196 [email protected] "where"
197 [email protected] "\n "
198 [email protected]
199 [email protected] "for"
200 [email protected]
201 [email protected] "<"
202 [email protected]
203 [email protected] "\'a"
204 [email protected] ">"
205 [email protected] " "
206 [email protected]
207 [email protected] "["
208 [email protected]
209 [email protected] "&"
210 [email protected] "\'a"
211 [email protected] " "
212 [email protected]
213 [email protected]
214 [email protected]
215 [email protected]
216 [email protected] "F"
217 [email protected] "]"
218 [email protected] ":"
219 [email protected] " "
220 [email protected]
221 [email protected]
222 [email protected]
223 [email protected]
224 [email protected]
225 [email protected]
226 [email protected] "Eq"
227 [email protected] ","
228 [email protected] "\n"
229 [email protected]
230 [email protected] "{"
231 [email protected] "\n"
232 [email protected] "}"
233 [email protected] "\n"
234 [email protected]
235 [email protected] "fn"
236 [email protected] " "
237 [email protected]
238 [email protected] "for_qpath"
239 [email protected]
240 [email protected] "<"
241 [email protected]
242 [email protected]
243 [email protected] "T"
244 [email protected] ">"
245 [email protected]
246 [email protected] "("
247 [email protected]
248 [email protected]
249 [email protected]
250 [email protected] "_t"
251 [email protected] ":"
252 [email protected] " "
253 [email protected]
254 [email protected] "&"
255 [email protected]
256 [email protected]
257 [email protected]
258 [email protected]
259 [email protected] "T"
260 [email protected] ")"
261 [email protected] "\n"
262 [email protected]
263 [email protected] "where"
264 [email protected] "\n "
265 [email protected]
266 [email protected] "for"
267 [email protected]
268 [email protected] "<"
269 [email protected]
270 [email protected] "\'a"
271 [email protected] ">"
272 [email protected] " "
273 [email protected]
274 [email protected]
275 [email protected]
276 [email protected]
277 [email protected] "<"
278 [email protected]
279 [email protected] "&"
280 [email protected] "\'a"
281 [email protected] " "
282 [email protected]
283 [email protected]
284 [email protected]
285 [email protected]
286 [email protected] "T"
287 [email protected] " "
288 [email protected] "as"
289 [email protected] " "
290 [email protected]
291 [email protected]
292 [email protected]
293 [email protected]
294 [email protected] "Baz"
295 [email protected] ">"
296 [email protected] "::"
297 [email protected]
298 [email protected]
299 [email protected] "Foo"
300 [email protected] ":"
301 [email protected] " "
302 [email protected]
303 [email protected]
304 [email protected]
305 [email protected]
306 [email protected]
307 [email protected]
308 [email protected] "Iterator"
309 [email protected] ","
310 [email protected] "\n"
311 [email protected]
312 [email protected] "{"
313 [email protected] "\n"
314 [email protected] "}"
315 [email protected] "\n"
316 [email protected]
317 [email protected] "fn"
318 [email protected] " "
319 [email protected]
320 [email protected] "for_for_fn"
321 [email protected]
322 [email protected] "<"
323 [email protected]
324 [email protected]
325 [email protected] "T"
326 [email protected] ">"
327 [email protected]
328 [email protected] "("
329 [email protected] ")"
330 [email protected] "\n"
331 [email protected]
332 [email protected] "where"
333 [email protected] "\n "
334 [email protected]
335 [email protected] "for"
336 [email protected]
337 [email protected] "<"
338 [email protected]
339 [email protected] "\'a"
340 [email protected] ">"
341 [email protected] " "
342 [email protected]
343 [email protected] "for"
344 [email protected]
345 [email protected] "<"
346 [email protected]
347 [email protected] "\'b"
348 [email protected] ">"
349 [email protected] " "
350 [email protected]
351 [email protected] "fn"
352 [email protected]
353 [email protected] "("
354 [email protected]
355 [email protected]
356 [email protected] "&"
357 [email protected] "\'a"
358 [email protected] " "
359 [email protected]
360 [email protected]
361 [email protected]
362 [email protected]
363 [email protected] "T"
364 [email protected] ","
365 [email protected] " "
366 [email protected]
367 [email protected]
368 [email protected] "&"
369 [email protected] "\'b"
370 [email protected] " "
371 [email protected]
372 [email protected]
373 [email protected]
374 [email protected]
375 [email protected] "T"
376 [email protected] ")"
377 [email protected] ":"
378 [email protected] " "
379 [email protected]
380 [email protected]
381 [email protected]
382 [email protected]
383 [email protected]
384 [email protected]
385 [email protected] "Copy"
386 [email protected] ","
387 [email protected] "\n"
388 [email protected]
389 [email protected] "{"
390 [email protected] "\n"
391 [email protected] "}"
392 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs
new file mode 100644
index 000000000..9058c4619
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs
@@ -0,0 +1,30 @@
1fn for_trait<F>()
2where
3 for<'a> F: Fn(&'a str),
4{
5}
6fn for_ref<F>()
7where
8 for<'a> &'a F: Debug,
9{
10}
11fn for_parens<F>()
12where
13 for<'a> (&'a F): Fn(&'a str),
14{
15}
16fn for_slice<F>()
17where
18 for<'a> [&'a F]: Eq,
19{
20}
21fn for_qpath<T>(_t: &T)
22where
23 for<'a> <&'a T as Baz>::Foo: Iterator,
24{
25}
26fn for_for_fn<T>()
27where
28 for<'a> for<'b> fn(&'a T, &'b T): Copy,
29{
30}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 65b487db3..458089e53 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -10,7 +10,7 @@ doctest = false
10 10
11[[bin]] 11[[bin]]
12name = "rust-analyzer" 12name = "rust-analyzer"
13path = "./src/bin/main.rs" 13path = "src/bin/main.rs"
14 14
15[dependencies] 15[dependencies]
16anyhow = "1.0.26" 16anyhow = "1.0.26"
@@ -32,7 +32,7 @@ threadpool = "1.7.1"
32 32
33stdx = { path = "../stdx" } 33stdx = { path = "../stdx" }
34 34
35lsp-server = "0.3.1" 35lsp-server = "0.3.2"
36ra_flycheck = { path = "../ra_flycheck" } 36ra_flycheck = { path = "../ra_flycheck" }
37ra_ide = { path = "../ra_ide" } 37ra_ide = { path = "../ra_ide" }
38ra_prof = { path = "../ra_prof" } 38ra_prof = { path = "../ra_prof" }
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index e82fd57de..8d071ab1c 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -4,9 +4,14 @@
4mod args; 4mod args;
5 5
6use lsp_server::Connection; 6use lsp_server::Connection;
7use rust_analyzer::{cli, config::Config, from_json, Result}; 7use rust_analyzer::{
8 cli,
9 config::{Config, LinkedProject},
10 from_json, Result,
11};
8 12
9use crate::args::HelpPrinted; 13use crate::args::HelpPrinted;
14use ra_project_model::ProjectManifest;
10 15
11fn main() -> Result<()> { 16fn main() -> Result<()> {
12 setup_logging()?; 17 setup_logging()?;
@@ -97,17 +102,6 @@ fn run_server() -> Result<()> {
97 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); 102 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
98 } 103 }
99 104
100 let cwd = std::env::current_dir()?;
101 let root = initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
102
103 let workspace_roots = initialize_params
104 .workspace_folders
105 .map(|workspaces| {
106 workspaces.into_iter().filter_map(|it| it.uri.to_file_path().ok()).collect::<Vec<_>>()
107 })
108 .filter(|workspaces| !workspaces.is_empty())
109 .unwrap_or_else(|| vec![root]);
110
111 let config = { 105 let config = {
112 let mut config = Config::default(); 106 let mut config = Config::default();
113 if let Some(value) = &initialize_params.initialization_options { 107 if let Some(value) = &initialize_params.initialization_options {
@@ -115,10 +109,31 @@ fn run_server() -> Result<()> {
115 } 109 }
116 config.update_caps(&initialize_params.capabilities); 110 config.update_caps(&initialize_params.capabilities);
117 111
112 if config.linked_projects.is_empty() {
113 let cwd = std::env::current_dir()?;
114 let root =
115 initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
116 let workspace_roots = initialize_params
117 .workspace_folders
118 .map(|workspaces| {
119 workspaces
120 .into_iter()
121 .filter_map(|it| it.uri.to_file_path().ok())
122 .collect::<Vec<_>>()
123 })
124 .filter(|workspaces| !workspaces.is_empty())
125 .unwrap_or_else(|| vec![root]);
126
127 config.linked_projects = ProjectManifest::discover_all(&workspace_roots)
128 .into_iter()
129 .map(LinkedProject::from)
130 .collect();
131 }
132
118 config 133 config
119 }; 134 };
120 135
121 rust_analyzer::main_loop(workspace_roots, config, connection)?; 136 rust_analyzer::main_loop(config, connection)?;
122 137
123 log::info!("shutting down IO..."); 138 log::info!("shutting down IO...");
124 io_threads.join()?; 139 io_threads.join()?;
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 345693524..673795e78 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -87,6 +87,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
87 "ssr": true, 87 "ssr": true,
88 "onEnter": true, 88 "onEnter": true,
89 "parentModule": true, 89 "parentModule": true,
90 "runnables": {
91 "kinds": [ "cargo" ],
92 },
90 })), 93 })),
91 } 94 }
92} 95}
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 441fb61df..44f856f6b 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -1,10 +1,10 @@
1//! See `CargoTargetSpec` 1//! See `CargoTargetSpec`
2 2
3use ra_cfg::CfgExpr;
3use ra_ide::{FileId, RunnableKind, TestId}; 4use ra_ide::{FileId, RunnableKind, TestId};
4use ra_project_model::{self, ProjectWorkspace, TargetKind}; 5use ra_project_model::{self, ProjectWorkspace, TargetKind};
5 6
6use crate::{world::WorldSnapshot, Result}; 7use crate::{global_state::GlobalStateSnapshot, Result};
7use ra_syntax::SmolStr;
8 8
9/// Abstract representation of Cargo target. 9/// Abstract representation of Cargo target.
10/// 10///
@@ -21,7 +21,7 @@ impl CargoTargetSpec {
21 pub(crate) fn runnable_args( 21 pub(crate) fn runnable_args(
22 spec: Option<CargoTargetSpec>, 22 spec: Option<CargoTargetSpec>,
23 kind: &RunnableKind, 23 kind: &RunnableKind,
24 features_needed: &Vec<SmolStr>, 24 cfgs: &[CfgExpr],
25 ) -> Result<(Vec<String>, Vec<String>)> { 25 ) -> Result<(Vec<String>, Vec<String>)> {
26 let mut args = Vec::new(); 26 let mut args = Vec::new();
27 let mut extra_args = Vec::new(); 27 let mut extra_args = Vec::new();
@@ -76,16 +76,20 @@ impl CargoTargetSpec {
76 } 76 }
77 } 77 }
78 78
79 features_needed.iter().for_each(|feature| { 79 let mut features = Vec::new();
80 for cfg in cfgs {
81 required_features(cfg, &mut features);
82 }
83 for feature in features {
80 args.push("--features".to_string()); 84 args.push("--features".to_string());
81 args.push(feature.to_string()); 85 args.push(feature);
82 }); 86 }
83 87
84 Ok((args, extra_args)) 88 Ok((args, extra_args))
85 } 89 }
86 90
87 pub(crate) fn for_file( 91 pub(crate) fn for_file(
88 world: &WorldSnapshot, 92 world: &GlobalStateSnapshot,
89 file_id: FileId, 93 file_id: FileId,
90 ) -> Result<Option<CargoTargetSpec>> { 94 ) -> Result<Option<CargoTargetSpec>> {
91 let &crate_id = match world.analysis().crate_for(file_id)?.first() { 95 let &crate_id = match world.analysis().crate_for(file_id)?.first() {
@@ -140,3 +144,74 @@ impl CargoTargetSpec {
140 } 144 }
141 } 145 }
142} 146}
147
148/// Fill minimal features needed
149fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) {
150 match cfg_expr {
151 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.to_string()),
152 CfgExpr::All(preds) => {
153 preds.iter().for_each(|cfg| required_features(cfg, features));
154 }
155 CfgExpr::Any(preds) => {
156 for cfg in preds {
157 let len_features = features.len();
158 required_features(cfg, features);
159 if len_features != features.len() {
160 break;
161 }
162 }
163 }
164 _ => {}
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 use mbe::{ast_to_token_tree, TokenMap};
173 use ra_cfg::parse_cfg;
174 use ra_syntax::{
175 ast::{self, AstNode},
176 SmolStr,
177 };
178
179 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
180 let source_file = ast::SourceFile::parse(input).ok().unwrap();
181 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
182 ast_to_token_tree(&tt).unwrap()
183 }
184
185 #[test]
186 fn test_cfg_expr_minimal_features_needed() {
187 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
188 let cfg_expr = parse_cfg(&subtree);
189 let mut min_features = vec![];
190 required_features(&cfg_expr, &mut min_features);
191
192 assert_eq!(min_features, vec![SmolStr::new("baz")]);
193
194 let (subtree, _) =
195 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
196 let cfg_expr = parse_cfg(&subtree);
197
198 let mut min_features = vec![];
199 required_features(&cfg_expr, &mut min_features);
200 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
201
202 let (subtree, _) =
203 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
204 let cfg_expr = parse_cfg(&subtree);
205
206 let mut min_features = vec![];
207 required_features(&cfg_expr, &mut min_features);
208 assert_eq!(min_features, vec![SmolStr::new("baz")]);
209
210 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
211 let cfg_expr = parse_cfg(&subtree);
212
213 let mut min_features = vec![];
214 required_features(&cfg_expr, &mut min_features);
215 assert!(min_features.is_empty());
216 }
217}
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 8eaf75ff6..97367d7c6 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -8,7 +8,7 @@ use crossbeam_channel::{unbounded, Receiver};
8use ra_db::{ExternSourceId, FileId, SourceRootId}; 8use ra_db::{ExternSourceId, FileId, SourceRootId};
9use ra_ide::{AnalysisChange, AnalysisHost}; 9use ra_ide::{AnalysisChange, AnalysisHost};
10use ra_project_model::{ 10use ra_project_model::{
11 get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace, 11 CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, ProjectWorkspace,
12}; 12};
13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; 13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
14use rustc_hash::{FxHashMap, FxHashSet}; 14use rustc_hash::{FxHashMap, FxHashSet};
@@ -28,7 +28,7 @@ pub fn load_cargo(
28 with_proc_macro: bool, 28 with_proc_macro: bool,
29) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { 29) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
30 let root = std::env::current_dir()?.join(root); 30 let root = std::env::current_dir()?.join(root);
31 let root = ProjectRoot::discover_single(&root)?; 31 let root = ProjectManifest::discover_single(&root)?;
32 let ws = ProjectWorkspace::load( 32 let ws = ProjectWorkspace::load(
33 root, 33 root,
34 &CargoConfig { load_out_dirs_from_check, ..Default::default() }, 34 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
@@ -36,28 +36,28 @@ pub fn load_cargo(
36 )?; 36 )?;
37 37
38 let mut extern_dirs = FxHashSet::default(); 38 let mut extern_dirs = FxHashSet::default();
39 extern_dirs.extend(ws.out_dirs());
40
41 let mut project_roots = ws.to_roots();
42 project_roots.extend(extern_dirs.iter().cloned().map(PackageRoot::new_non_member));
43 39
44 let (sender, receiver) = unbounded(); 40 let (sender, receiver) = unbounded();
45 let sender = Box::new(move |t| sender.send(t).unwrap()); 41 let sender = Box::new(move |t| sender.send(t).unwrap());
46 let (mut vfs, roots) = Vfs::new( 42
47 project_roots 43 let mut roots = Vec::new();
48 .iter() 44 let project_roots = ws.to_roots();
49 .map(|pkg_root| { 45 for root in &project_roots {
50 RootEntry::new( 46 roots.push(RootEntry::new(
51 pkg_root.path().to_owned(), 47 root.path().to_owned(),
52 RustPackageFilterBuilder::default() 48 RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(),
53 .set_member(pkg_root.is_member()) 49 ));
54 .into_vfs_filter(), 50
55 ) 51 if let Some(out_dir) = root.out_dir() {
56 }) 52 extern_dirs.insert(out_dir.to_path_buf());
57 .collect(), 53 roots.push(RootEntry::new(
58 sender, 54 out_dir.to_owned(),
59 Watch(false), 55 RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(),
60 ); 56 ))
57 }
58 }
59
60 let (mut vfs, roots) = Vfs::new(roots, sender, Watch(false));
61 61
62 let source_roots = roots 62 let source_roots = roots
63 .into_iter() 63 .into_iter()
@@ -111,10 +111,6 @@ pub(crate) fn load(
111 vfs.root2path(root) 111 vfs.root2path(root)
112 ); 112 );
113 analysis_change.add_root(source_root_id, is_local); 113 analysis_change.add_root(source_root_id, is_local);
114 analysis_change.set_debug_root_path(
115 source_root_id,
116 source_roots[&source_root_id].path().display().to_string(),
117 );
118 114
119 let vfs_root_path = vfs.root2path(root); 115 let vfs_root_path = vfs.root2path(root);
120 if extern_dirs.contains(&vfs_root_path) { 116 if extern_dirs.contains(&vfs_root_path) {
@@ -147,26 +143,14 @@ pub(crate) fn load(
147 } 143 }
148 } 144 }
149 145
150 // FIXME: cfg options? 146 let crate_graph =
151 let default_cfg_options = { 147 ws.to_crate_graph(None, &extern_source_roots, proc_macro_client, &mut |path: &Path| {
152 let mut opts = get_rustc_cfg_options(None);
153 opts.insert_atom("test".into());
154 opts.insert_atom("debug_assertion".into());
155 opts
156 };
157
158 let crate_graph = ws.to_crate_graph(
159 &default_cfg_options,
160 &extern_source_roots,
161 proc_macro_client,
162 &mut |path: &Path| {
163 // Some path from metadata will be non canonicalized, e.g. /foo/../bar/lib.rs 148 // Some path from metadata will be non canonicalized, e.g. /foo/../bar/lib.rs
164 let path = path.canonicalize().ok()?; 149 let path = path.canonicalize().ok()?;
165 let vfs_file = vfs.load(&path); 150 let vfs_file = vfs.load(&path);
166 log::debug!("vfs file {:?} -> {:?}", path, vfs_file); 151 log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
167 vfs_file.map(vfs_file_to_id) 152 vfs_file.map(vfs_file_to_id)
168 }, 153 });
169 );
170 log::debug!("crate graph: {:?}", crate_graph); 154 log::debug!("crate graph: {:?}", crate_graph);
171 analysis_change.set_crate_graph(crate_graph); 155 analysis_change.set_crate_graph(crate_graph);
172 156
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index c0f7c2c0c..1253db836 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -11,15 +11,14 @@ use std::{ffi::OsString, path::PathBuf};
11 11
12use lsp_types::ClientCapabilities; 12use lsp_types::ClientCapabilities;
13use ra_flycheck::FlycheckConfig; 13use ra_flycheck::FlycheckConfig;
14use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; 14use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
15use ra_project_model::CargoConfig; 15use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
16use serde::Deserialize; 16use serde::Deserialize;
17 17
18#[derive(Debug, Clone)] 18#[derive(Debug, Clone)]
19pub struct Config { 19pub struct Config {
20 pub client_caps: ClientCapsConfig, 20 pub client_caps: ClientCapsConfig,
21 21
22 pub with_sysroot: bool,
23 pub publish_diagnostics: bool, 22 pub publish_diagnostics: bool,
24 pub lru_capacity: Option<usize>, 23 pub lru_capacity: Option<usize>,
25 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>, 24 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
@@ -35,6 +34,28 @@ pub struct Config {
35 pub assist: AssistConfig, 34 pub assist: AssistConfig,
36 pub call_info_full: bool, 35 pub call_info_full: bool,
37 pub lens: LensConfig, 36 pub lens: LensConfig,
37 pub hover: HoverConfig,
38
39 pub with_sysroot: bool,
40 pub linked_projects: Vec<LinkedProject>,
41}
42
43#[derive(Debug, Clone)]
44pub enum LinkedProject {
45 ProjectManifest(ProjectManifest),
46 JsonProject(JsonProject),
47}
48
49impl From<ProjectManifest> for LinkedProject {
50 fn from(v: ProjectManifest) -> Self {
51 LinkedProject::ProjectManifest(v)
52 }
53}
54
55impl From<JsonProject> for LinkedProject {
56 fn from(v: JsonProject) -> Self {
57 LinkedProject::JsonProject(v)
58 }
38} 59}
39 60
40#[derive(Clone, Debug, PartialEq, Eq)] 61#[derive(Clone, Debug, PartialEq, Eq)]
@@ -103,6 +124,8 @@ pub struct ClientCapsConfig {
103 pub code_action_literals: bool, 124 pub code_action_literals: bool,
104 pub work_done_progress: bool, 125 pub work_done_progress: bool,
105 pub code_action_group: bool, 126 pub code_action_group: bool,
127 pub resolve_code_action: bool,
128 pub hover_actions: bool,
106} 129}
107 130
108impl Default for Config { 131impl Default for Config {
@@ -122,8 +145,9 @@ impl Default for Config {
122 check: Some(FlycheckConfig::CargoCommand { 145 check: Some(FlycheckConfig::CargoCommand {
123 command: "check".to_string(), 146 command: "check".to_string(),
124 all_targets: true, 147 all_targets: true,
125 all_features: true, 148 all_features: false,
126 extra_args: Vec::new(), 149 extra_args: Vec::new(),
150 features: Vec::new(),
127 }), 151 }),
128 152
129 inlay_hints: InlayHintsConfig { 153 inlay_hints: InlayHintsConfig {
@@ -141,6 +165,8 @@ impl Default for Config {
141 assist: AssistConfig::default(), 165 assist: AssistConfig::default(),
142 call_info_full: true, 166 call_info_full: true,
143 lens: LensConfig::default(), 167 lens: LensConfig::default(),
168 hover: HoverConfig::default(),
169 linked_projects: Vec::new(),
144 } 170 }
145 } 171 }
146} 172}
@@ -209,13 +235,14 @@ impl Config {
209 } 235 }
210 // otherwise configure command customizations 236 // otherwise configure command customizations
211 _ => { 237 _ => {
212 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features }) 238 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features })
213 = &mut self.check 239 = &mut self.check
214 { 240 {
215 set(value, "/checkOnSave/extraArgs", extra_args); 241 set(value, "/checkOnSave/extraArgs", extra_args);
216 set(value, "/checkOnSave/command", command); 242 set(value, "/checkOnSave/command", command);
217 set(value, "/checkOnSave/allTargets", all_targets); 243 set(value, "/checkOnSave/allTargets", all_targets);
218 set(value, "/checkOnSave/allFeatures", all_features); 244 *all_features = get(value, "/checkOnSave/allFeatures").unwrap_or(self.cargo.all_features);
245 *features = get(value, "/checkOnSave/features").unwrap_or(self.cargo.features.clone());
219 } 246 }
220 } 247 }
221 }; 248 };
@@ -240,6 +267,32 @@ impl Config {
240 self.lens = LensConfig::NO_LENS; 267 self.lens = LensConfig::NO_LENS;
241 } 268 }
242 269
270 if let Some(linked_projects) = get::<Vec<ManifestOrJsonProject>>(value, "/linkedProjects") {
271 if !linked_projects.is_empty() {
272 self.linked_projects.clear();
273 for linked_project in linked_projects {
274 let linked_project = match linked_project {
275 ManifestOrJsonProject::Manifest(it) => match ProjectManifest::from_manifest_file(it) {
276 Ok(it) => it.into(),
277 Err(_) => continue,
278 }
279 ManifestOrJsonProject::JsonProject(it) => it.into(),
280 };
281 self.linked_projects.push(linked_project);
282 }
283 }
284 }
285
286 let mut use_hover_actions = false;
287 set(value, "/hoverActions/enable", &mut use_hover_actions);
288 if use_hover_actions {
289 set(value, "/hoverActions/implementations", &mut self.hover.implementations);
290 set(value, "/hoverActions/run", &mut self.hover.run);
291 set(value, "/hoverActions/debug", &mut self.hover.debug);
292 } else {
293 self.hover = HoverConfig::NO_ACTIONS;
294 }
295
243 log::info!("Config::update() = {:#?}", self); 296 log::info!("Config::update() = {:#?}", self);
244 297
245 fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { 298 fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
@@ -293,13 +346,22 @@ impl Config {
293 346
294 self.assist.allow_snippets(false); 347 self.assist.allow_snippets(false);
295 if let Some(experimental) = &caps.experimental { 348 if let Some(experimental) = &caps.experimental {
296 let snippet_text_edit = 349 let get_bool =
297 experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); 350 |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);
351
352 let snippet_text_edit = get_bool("snippetTextEdit");
298 self.assist.allow_snippets(snippet_text_edit); 353 self.assist.allow_snippets(snippet_text_edit);
299 354
300 let code_action_group = 355 self.client_caps.code_action_group = get_bool("codeActionGroup");
301 experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); 356 self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
302 self.client_caps.code_action_group = code_action_group 357 self.client_caps.hover_actions = get_bool("hoverActions");
303 } 358 }
304 } 359 }
305} 360}
361
362#[derive(Deserialize)]
363#[serde(untagged)]
364enum ManifestOrJsonProject {
365 Manifest(PathBuf),
366 JsonProject(JsonProject),
367}
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
index c40cfdcdc..272057b47 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
@@ -65,6 +65,7 @@ expression: diag
65 fixes: [ 65 fixes: [
66 CodeAction { 66 CodeAction {
67 title: "return the expression directly", 67 title: "return the expression directly",
68 id: None,
68 group: None, 69 group: None,
69 kind: Some( 70 kind: Some(
70 "quickfix", 71 "quickfix",
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
index 6dd3fcb2e..f0273315e 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
@@ -29,7 +29,7 @@ expression: diag
29 }, 29 },
30 }, 30 },
31 severity: Some( 31 severity: Some(
32 Warning, 32 Hint,
33 ), 33 ),
34 code: Some( 34 code: Some(
35 String( 35 String(
@@ -50,6 +50,7 @@ expression: diag
50 fixes: [ 50 fixes: [
51 CodeAction { 51 CodeAction {
52 title: "consider prefixing with an underscore", 52 title: "consider prefixing with an underscore",
53 id: None,
53 group: None, 54 group: None,
54 kind: Some( 55 kind: Some(
55 "quickfix", 56 "quickfix",
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index a500d670a..04e286780 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -145,6 +145,7 @@ fn map_rust_child_diagnostic(
145 } else { 145 } else {
146 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { 146 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
147 title: rd.message.clone(), 147 title: rd.message.clone(),
148 id: None,
148 group: None, 149 group: None,
149 kind: Some("quickfix".to_string()), 150 kind: Some("quickfix".to_string()),
150 edit: Some(lsp_ext::SnippetWorkspaceEdit { 151 edit: Some(lsp_ext::SnippetWorkspaceEdit {
@@ -183,7 +184,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
183 return Vec::new(); 184 return Vec::new();
184 } 185 }
185 186
186 let severity = map_level_to_severity(rd.level); 187 let mut severity = map_level_to_severity(rd.level);
187 188
188 let mut source = String::from("rustc"); 189 let mut source = String::from("rustc");
189 let mut code = rd.code.as_ref().map(|c| c.code.clone()); 190 let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -225,6 +226,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
225 } 226 }
226 227
227 if is_unused_or_unnecessary(rd) { 228 if is_unused_or_unnecessary(rd) {
229 severity = Some(DiagnosticSeverity::Hint);
228 tags.push(DiagnosticTag::Unnecessary); 230 tags.push(DiagnosticTag::Unnecessary);
229 } 231 }
230 232
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs
index 4bb16a496..206673829 100644
--- a/crates/rust-analyzer/src/from_proto.rs
+++ b/crates/rust-analyzer/src/from_proto.rs
@@ -3,7 +3,7 @@ use ra_db::{FileId, FilePosition, FileRange};
3use ra_ide::{LineCol, LineIndex}; 3use ra_ide::{LineCol, LineIndex};
4use ra_syntax::{TextRange, TextSize}; 4use ra_syntax::{TextRange, TextSize};
5 5
6use crate::{world::WorldSnapshot, Result}; 6use crate::{global_state::GlobalStateSnapshot, Result};
7 7
8pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { 8pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize {
9 let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; 9 let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 };
@@ -16,12 +16,12 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Tex
16 TextRange::new(start, end) 16 TextRange::new(start, end)
17} 17}
18 18
19pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result<FileId> { 19pub(crate) fn file_id(world: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> {
20 world.uri_to_file_id(url) 20 world.uri_to_file_id(url)
21} 21}
22 22
23pub(crate) fn file_position( 23pub(crate) fn file_position(
24 world: &WorldSnapshot, 24 world: &GlobalStateSnapshot,
25 tdpp: lsp_types::TextDocumentPositionParams, 25 tdpp: lsp_types::TextDocumentPositionParams,
26) -> Result<FilePosition> { 26) -> Result<FilePosition> {
27 let file_id = file_id(world, &tdpp.text_document.uri)?; 27 let file_id = file_id(world, &tdpp.text_document.uri)?;
@@ -31,7 +31,7 @@ pub(crate) fn file_position(
31} 31}
32 32
33pub(crate) fn file_range( 33pub(crate) fn file_range(
34 world: &WorldSnapshot, 34 world: &GlobalStateSnapshot,
35 text_document_identifier: lsp_types::TextDocumentIdentifier, 35 text_document_identifier: lsp_types::TextDocumentIdentifier,
36 range: lsp_types::Range, 36 range: lsp_types::Range,
37) -> Result<FileRange> { 37) -> Result<FileRange> {
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/global_state.rs
index 367272925..21116e165 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -15,7 +15,7 @@ use ra_flycheck::{Flycheck, FlycheckConfig};
15use ra_ide::{ 15use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, 16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
17}; 17};
18use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; 18use ra_project_model::{ProcMacroClient, ProjectWorkspace};
19use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; 19use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
20use relative_path::RelativePathBuf; 20use relative_path::RelativePathBuf;
21use stdx::format_to; 21use stdx::format_to;
@@ -50,15 +50,15 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) ->
50 }) 50 })
51} 51}
52 52
53/// `WorldState` is the primary mutable state of the language server 53/// `GlobalState` is the primary mutable state of the language server
54/// 54///
55/// The most interesting components are `vfs`, which stores a consistent 55/// The most interesting components are `vfs`, which stores a consistent
56/// snapshot of the file systems, and `analysis_host`, which stores our 56/// snapshot of the file systems, and `analysis_host`, which stores our
57/// incremental salsa database. 57/// incremental salsa database.
58#[derive(Debug)] 58#[derive(Debug)]
59pub struct WorldState { 59pub struct GlobalState {
60 pub config: Config, 60 pub config: Config,
61 pub roots: Vec<PathBuf>, 61 pub local_roots: Vec<PathBuf>,
62 pub workspaces: Arc<Vec<ProjectWorkspace>>, 62 pub workspaces: Arc<Vec<ProjectWorkspace>>,
63 pub analysis_host: AnalysisHost, 63 pub analysis_host: AnalysisHost,
64 pub vfs: Arc<RwLock<Vfs>>, 64 pub vfs: Arc<RwLock<Vfs>>,
@@ -70,7 +70,7 @@ pub struct WorldState {
70} 70}
71 71
72/// An immutable snapshot of the world's state at a point in time. 72/// An immutable snapshot of the world's state at a point in time.
73pub struct WorldSnapshot { 73pub struct GlobalStateSnapshot {
74 pub config: Config, 74 pub config: Config,
75 pub workspaces: Arc<Vec<ProjectWorkspace>>, 75 pub workspaces: Arc<Vec<ProjectWorkspace>>,
76 pub analysis: Analysis, 76 pub analysis: Analysis,
@@ -79,20 +79,19 @@ pub struct WorldSnapshot {
79 vfs: Arc<RwLock<Vfs>>, 79 vfs: Arc<RwLock<Vfs>>,
80} 80}
81 81
82impl WorldState { 82impl GlobalState {
83 pub fn new( 83 pub fn new(
84 folder_roots: Vec<PathBuf>,
85 workspaces: Vec<ProjectWorkspace>, 84 workspaces: Vec<ProjectWorkspace>,
86 lru_capacity: Option<usize>, 85 lru_capacity: Option<usize>,
87 exclude_globs: &[Glob], 86 exclude_globs: &[Glob],
88 watch: Watch, 87 watch: Watch,
89 config: Config, 88 config: Config,
90 ) -> WorldState { 89 ) -> GlobalState {
91 let mut change = AnalysisChange::new(); 90 let mut change = AnalysisChange::new();
92 91
93 let extern_dirs: FxHashSet<_> = 92 let mut extern_dirs: FxHashSet<PathBuf> = FxHashSet::default();
94 workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect();
95 93
94 let mut local_roots = Vec::new();
96 let roots: Vec<_> = { 95 let roots: Vec<_> = {
97 let create_filter = |is_member| { 96 let create_filter = |is_member| {
98 RustPackageFilterBuilder::default() 97 RustPackageFilterBuilder::default()
@@ -100,18 +99,22 @@ impl WorldState {
100 .exclude(exclude_globs.iter().cloned()) 99 .exclude(exclude_globs.iter().cloned())
101 .into_vfs_filter() 100 .into_vfs_filter()
102 }; 101 };
103 folder_roots 102 let mut roots = Vec::new();
104 .iter() 103 for root in workspaces.iter().flat_map(ProjectWorkspace::to_roots) {
105 .map(|path| RootEntry::new(path.clone(), create_filter(true))) 104 let path = root.path().to_owned();
106 .chain(workspaces.iter().flat_map(ProjectWorkspace::to_roots).map(|pkg_root| { 105 if root.is_member() {
107 RootEntry::new(pkg_root.path().to_owned(), create_filter(pkg_root.is_member())) 106 local_roots.push(path.clone());
108 })) 107 }
109 .chain( 108 roots.push(RootEntry::new(path, create_filter(root.is_member())));
110 extern_dirs 109 if let Some(out_dir) = root.out_dir() {
111 .iter() 110 extern_dirs.insert(out_dir.to_path_buf());
112 .map(|path| RootEntry::new(path.to_owned(), create_filter(false))), 111 roots.push(RootEntry::new(
113 ) 112 out_dir.to_path_buf(),
114 .collect() 113 create_filter(root.is_member()),
114 ))
115 }
116 }
117 roots
115 }; 118 };
116 119
117 let (task_sender, task_receiver) = unbounded(); 120 let (task_sender, task_receiver) = unbounded();
@@ -121,9 +124,8 @@ impl WorldState {
121 let mut extern_source_roots = FxHashMap::default(); 124 let mut extern_source_roots = FxHashMap::default();
122 for r in vfs_roots { 125 for r in vfs_roots {
123 let vfs_root_path = vfs.root2path(r); 126 let vfs_root_path = vfs.root2path(r);
124 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); 127 let is_local = local_roots.iter().any(|it| vfs_root_path.starts_with(it));
125 change.add_root(SourceRootId(r.0), is_local); 128 change.add_root(SourceRootId(r.0), is_local);
126 change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());
127 129
128 // FIXME: add path2root in vfs to simpily this logic 130 // FIXME: add path2root in vfs to simpily this logic
129 if extern_dirs.contains(&vfs_root_path) { 131 if extern_dirs.contains(&vfs_root_path) {
@@ -131,14 +133,6 @@ impl WorldState {
131 } 133 }
132 } 134 }
133 135
134 // FIXME: Read default cfgs from config
135 let default_cfg_options = {
136 let mut opts = get_rustc_cfg_options(config.cargo.target.as_ref());
137 opts.insert_atom("test".into());
138 opts.insert_atom("debug_assertion".into());
139 opts
140 };
141
142 let proc_macro_client = match &config.proc_macro_srv { 136 let proc_macro_client = match &config.proc_macro_srv {
143 None => ProcMacroClient::dummy(), 137 None => ProcMacroClient::dummy(),
144 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) { 138 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
@@ -164,7 +158,7 @@ impl WorldState {
164 }; 158 };
165 for ws in workspaces.iter() { 159 for ws in workspaces.iter() {
166 crate_graph.extend(ws.to_crate_graph( 160 crate_graph.extend(ws.to_crate_graph(
167 &default_cfg_options, 161 config.cargo.target.as_deref(),
168 &extern_source_roots, 162 &extern_source_roots,
169 &proc_macro_client, 163 &proc_macro_client,
170 &mut load, 164 &mut load,
@@ -176,9 +170,9 @@ impl WorldState {
176 170
177 let mut analysis_host = AnalysisHost::new(lru_capacity); 171 let mut analysis_host = AnalysisHost::new(lru_capacity);
178 analysis_host.apply_change(change); 172 analysis_host.apply_change(change);
179 WorldState { 173 GlobalState {
180 config, 174 config,
181 roots: folder_roots, 175 local_roots,
182 workspaces: Arc::new(workspaces), 176 workspaces: Arc::new(workspaces),
183 analysis_host, 177 analysis_host,
184 vfs: Arc::new(RwLock::new(vfs)), 178 vfs: Arc::new(RwLock::new(vfs)),
@@ -216,7 +210,7 @@ impl WorldState {
216 match c { 210 match c {
217 VfsChange::AddRoot { root, files } => { 211 VfsChange::AddRoot { root, files } => {
218 let root_path = self.vfs.read().root2path(root); 212 let root_path = self.vfs.read().root2path(root);
219 let is_local = self.roots.iter().any(|r| root_path.starts_with(r)); 213 let is_local = self.local_roots.iter().any(|r| root_path.starts_with(r));
220 if is_local { 214 if is_local {
221 *roots_scanned += 1; 215 *roots_scanned += 1;
222 for (file, path, text) in files { 216 for (file, path, text) in files {
@@ -251,8 +245,8 @@ impl WorldState {
251 self.analysis_host.apply_change(change); 245 self.analysis_host.apply_change(change);
252 } 246 }
253 247
254 pub fn snapshot(&self) -> WorldSnapshot { 248 pub fn snapshot(&self) -> GlobalStateSnapshot {
255 WorldSnapshot { 249 GlobalStateSnapshot {
256 config: self.config.clone(), 250 config: self.config.clone(),
257 workspaces: Arc::clone(&self.workspaces), 251 workspaces: Arc::clone(&self.workspaces),
258 analysis: self.analysis_host.analysis(), 252 analysis: self.analysis_host.analysis(),
@@ -275,7 +269,7 @@ impl WorldState {
275 } 269 }
276} 270}
277 271
278impl WorldSnapshot { 272impl GlobalStateSnapshot {
279 pub fn analysis(&self) -> &Analysis { 273 pub fn analysis(&self) -> &Analysis {
280 &self.analysis 274 &self.analysis
281 } 275 }
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 57d0e9218..609cb69d3 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -26,7 +26,7 @@ mod main_loop;
26mod markdown; 26mod markdown;
27pub mod lsp_ext; 27pub mod lsp_ext;
28pub mod config; 28pub mod config;
29mod world; 29mod global_state;
30mod diagnostics; 30mod diagnostics;
31mod semantic_tokens; 31mod semantic_tokens;
32 32
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index acb1dacb6..1371f6cb4 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -4,7 +4,6 @@ use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Position, Range, TextDocumentIdentifier}; 6use lsp_types::{Position, Range, TextDocumentIdentifier};
7use rustc_hash::FxHashMap;
8use serde::{Deserialize, Serialize}; 7use serde::{Deserialize, Serialize};
9 8
10pub enum AnalyzerStatus {} 9pub enum AnalyzerStatus {}
@@ -98,6 +97,22 @@ pub struct JoinLinesParams {
98 pub ranges: Vec<Range>, 97 pub ranges: Vec<Range>,
99} 98}
100 99
100pub enum ResolveCodeActionRequest {}
101
102impl Request for ResolveCodeActionRequest {
103 type Params = ResolveCodeActionParams;
104 type Result = Option<SnippetWorkspaceEdit>;
105 const METHOD: &'static str = "experimental/resolveCodeAction";
106}
107
108/// Params for the ResolveCodeActionRequest
109#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
110#[serde(rename_all = "camelCase")]
111pub struct ResolveCodeActionParams {
112 pub code_action_params: lsp_types::CodeActionParams,
113 pub id: String,
114}
115
101pub enum OnEnter {} 116pub enum OnEnter {}
102 117
103impl Request for OnEnter { 118impl Request for OnEnter {
@@ -111,7 +126,7 @@ pub enum Runnables {}
111impl Request for Runnables { 126impl Request for Runnables {
112 type Params = RunnablesParams; 127 type Params = RunnablesParams;
113 type Result = Vec<Runnable>; 128 type Result = Vec<Runnable>;
114 const METHOD: &'static str = "rust-analyzer/runnables"; 129 const METHOD: &'static str = "experimental/runnables";
115} 130}
116 131
117#[derive(Serialize, Deserialize, Debug)] 132#[derive(Serialize, Deserialize, Debug)]
@@ -124,13 +139,28 @@ pub struct RunnablesParams {
124#[derive(Deserialize, Serialize, Debug)] 139#[derive(Deserialize, Serialize, Debug)]
125#[serde(rename_all = "camelCase")] 140#[serde(rename_all = "camelCase")]
126pub struct Runnable { 141pub struct Runnable {
127 pub range: Range,
128 pub label: String, 142 pub label: String,
129 pub bin: String, 143 #[serde(skip_serializing_if = "Option::is_none")]
130 pub args: Vec<String>, 144 pub location: Option<lsp_types::LocationLink>,
131 pub extra_args: Vec<String>, 145 pub kind: RunnableKind,
132 pub env: FxHashMap<String, String>, 146 pub args: CargoRunnable,
133 pub cwd: Option<PathBuf>, 147}
148
149#[derive(Serialize, Deserialize, Debug)]
150#[serde(rename_all = "lowercase")]
151pub enum RunnableKind {
152 Cargo,
153}
154
155#[derive(Deserialize, Serialize, Debug)]
156#[serde(rename_all = "camelCase")]
157pub struct CargoRunnable {
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub workspace_root: Option<PathBuf>,
160 // command, --package and --lib stuff
161 pub cargo_args: Vec<String>,
162 // stuff after --
163 pub executable_args: Vec<String>,
134} 164}
135 165
136pub enum InlayHints {} 166pub enum InlayHints {}
@@ -188,6 +218,8 @@ impl Request for CodeActionRequest {
188pub struct CodeAction { 218pub struct CodeAction {
189 pub title: String, 219 pub title: String,
190 #[serde(skip_serializing_if = "Option::is_none")] 220 #[serde(skip_serializing_if = "Option::is_none")]
221 pub id: Option<String>,
222 #[serde(skip_serializing_if = "Option::is_none")]
191 pub group: Option<String>, 223 pub group: Option<String>,
192 #[serde(skip_serializing_if = "Option::is_none")] 224 #[serde(skip_serializing_if = "Option::is_none")]
193 pub kind: Option<String>, 225 pub kind: Option<String>,
@@ -228,3 +260,35 @@ pub struct SnippetTextEdit {
228 #[serde(skip_serializing_if = "Option::is_none")] 260 #[serde(skip_serializing_if = "Option::is_none")]
229 pub insert_text_format: Option<lsp_types::InsertTextFormat>, 261 pub insert_text_format: Option<lsp_types::InsertTextFormat>,
230} 262}
263
264pub enum HoverRequest {}
265
266impl Request for HoverRequest {
267 type Params = lsp_types::HoverParams;
268 type Result = Option<Hover>;
269 const METHOD: &'static str = "textDocument/hover";
270}
271
272#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
273pub struct Hover {
274 #[serde(flatten)]
275 pub hover: lsp_types::Hover,
276 #[serde(skip_serializing_if = "Vec::is_empty")]
277 pub actions: Vec<CommandLinkGroup>,
278}
279
280#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
281pub struct CommandLinkGroup {
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub title: Option<String>,
284 pub commands: Vec<CommandLink>,
285}
286
287// LSP v3.15 Command does not have a `tooltip` field, vscode supports one.
288#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
289pub struct CommandLink {
290 #[serde(flatten)]
291 pub command: lsp_types::Command,
292 #[serde(skip_serializing_if = "Option::is_none")]
293 pub tooltip: Option<String>,
294}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index f1287d52c..752dbf145 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -12,13 +12,11 @@ use std::{
12 fmt, 12 fmt,
13 ops::Range, 13 ops::Range,
14 panic, 14 panic,
15 path::PathBuf,
16 sync::Arc, 15 sync::Arc,
17 time::{Duration, Instant}, 16 time::{Duration, Instant},
18}; 17};
19 18
20use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 19use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
21use itertools::Itertools;
22use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 20use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
23use lsp_types::{ 21use lsp_types::{
24 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, 22 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress,
@@ -36,14 +34,15 @@ use serde::{de::DeserializeOwned, Serialize};
36use threadpool::ThreadPool; 34use threadpool::ThreadPool;
37 35
38use crate::{ 36use crate::{
39 config::{Config, FilesWatcher}, 37 config::{Config, FilesWatcher, LinkedProject},
40 diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask}, 38 diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask},
41 from_proto, lsp_ext, 39 from_proto,
40 global_state::{GlobalState, GlobalStateSnapshot},
41 lsp_ext,
42 main_loop::{ 42 main_loop::{
43 pending_requests::{PendingRequest, PendingRequests}, 43 pending_requests::{PendingRequest, PendingRequests},
44 subscriptions::Subscriptions, 44 subscriptions::Subscriptions,
45 }, 45 },
46 world::{WorldSnapshot, WorldState},
47 Result, 46 Result,
48}; 47};
49 48
@@ -69,7 +68,7 @@ impl fmt::Display for LspError {
69 68
70impl Error for LspError {} 69impl Error for LspError {}
71 70
72pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> { 71pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
73 log::info!("initial config: {:#?}", config); 72 log::info!("initial config: {:#?}", config);
74 73
75 // Windows scheduler implements priority boosts: if thread waits for an 74 // Windows scheduler implements priority boosts: if thread waits for an
@@ -92,43 +91,37 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
92 } 91 }
93 92
94 let mut loop_state = LoopState::default(); 93 let mut loop_state = LoopState::default();
95 let mut world_state = { 94 let mut global_state = {
96 let workspaces = { 95 let workspaces = {
97 // FIXME: support dynamic workspace loading. 96 if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found {
98 let project_roots: FxHashSet<_> = ws_roots
99 .iter()
100 .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok())
101 .flatten()
102 .collect();
103
104 if project_roots.is_empty() && config.notifications.cargo_toml_not_found {
105 show_message( 97 show_message(
106 lsp_types::MessageType::Error, 98 lsp_types::MessageType::Error,
107 format!( 99 "rust-analyzer failed to discover workspace".to_string(),
108 "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}",
109 ws_roots.iter().format_with(", ", |it, f| f(&it.display()))
110 ),
111 &connection.sender, 100 &connection.sender,
112 ); 101 );
113 }; 102 };
114 103
115 project_roots 104 config
116 .into_iter() 105 .linked_projects
117 .filter_map(|root| { 106 .iter()
118 ra_project_model::ProjectWorkspace::load( 107 .filter_map(|project| match project {
119 root, 108 LinkedProject::ProjectManifest(manifest) => {
120 &config.cargo, 109 ra_project_model::ProjectWorkspace::load(
121 config.with_sysroot, 110 manifest.clone(),
122 ) 111 &config.cargo,
123 .map_err(|err| { 112 config.with_sysroot,
124 log::error!("failed to load workspace: {:#}", err); 113 )
125 show_message( 114 .map_err(|err| {
126 lsp_types::MessageType::Error, 115 log::error!("failed to load workspace: {:#}", err);
127 format!("rust-analyzer failed to load workspace: {:#}", err), 116 show_message(
128 &connection.sender, 117 lsp_types::MessageType::Error,
129 ); 118 format!("rust-analyzer failed to load workspace: {:#}", err),
130 }) 119 &connection.sender,
131 .ok() 120 );
121 })
122 .ok()
123 }
124 LinkedProject::JsonProject(it) => Some(it.clone().into()),
132 }) 125 })
133 .collect::<Vec<_>>() 126 .collect::<Vec<_>>()
134 }; 127 };
@@ -163,8 +156,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
163 connection.sender.send(request.into()).unwrap(); 156 connection.sender.send(request.into()).unwrap();
164 } 157 }
165 158
166 WorldState::new( 159 GlobalState::new(
167 ws_roots,
168 workspaces, 160 workspaces,
169 config.lru_capacity, 161 config.lru_capacity,
170 &globs, 162 &globs,
@@ -173,7 +165,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
173 ) 165 )
174 }; 166 };
175 167
176 loop_state.roots_total = world_state.vfs.read().n_roots(); 168 loop_state.roots_total = global_state.vfs.read().n_roots();
177 169
178 let pool = ThreadPool::default(); 170 let pool = ThreadPool::default();
179 let (task_sender, task_receiver) = unbounded::<Task>(); 171 let (task_sender, task_receiver) = unbounded::<Task>();
@@ -191,12 +183,12 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
191 Err(RecvError) => return Err("client exited without shutdown".into()), 183 Err(RecvError) => return Err("client exited without shutdown".into()),
192 }, 184 },
193 recv(task_receiver) -> task => Event::Task(task.unwrap()), 185 recv(task_receiver) -> task => Event::Task(task.unwrap()),
194 recv(world_state.task_receiver) -> task => match task { 186 recv(global_state.task_receiver) -> task => match task {
195 Ok(task) => Event::Vfs(task), 187 Ok(task) => Event::Vfs(task),
196 Err(RecvError) => return Err("vfs died".into()), 188 Err(RecvError) => return Err("vfs died".into()),
197 }, 189 },
198 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()), 190 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()),
199 recv(world_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { 191 recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task {
200 Ok(task) => Event::CheckWatcher(task), 192 Ok(task) => Event::CheckWatcher(task),
201 Err(RecvError) => return Err("check watcher died".into()), 193 Err(RecvError) => return Err("check watcher died".into()),
202 } 194 }
@@ -211,16 +203,16 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
211 &task_sender, 203 &task_sender,
212 &libdata_sender, 204 &libdata_sender,
213 &connection, 205 &connection,
214 &mut world_state, 206 &mut global_state,
215 &mut loop_state, 207 &mut loop_state,
216 event, 208 event,
217 )?; 209 )?;
218 } 210 }
219 } 211 }
220 world_state.analysis_host.request_cancellation(); 212 global_state.analysis_host.request_cancellation();
221 log::info!("waiting for tasks to finish..."); 213 log::info!("waiting for tasks to finish...");
222 task_receiver.into_iter().for_each(|task| { 214 task_receiver.into_iter().for_each(|task| {
223 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut world_state) 215 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut global_state)
224 }); 216 });
225 libdata_receiver.into_iter().for_each(drop); 217 libdata_receiver.into_iter().for_each(drop);
226 log::info!("...tasks have finished"); 218 log::info!("...tasks have finished");
@@ -229,7 +221,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
229 drop(pool); 221 drop(pool);
230 log::info!("...threadpool has finished"); 222 log::info!("...threadpool has finished");
231 223
232 let vfs = Arc::try_unwrap(world_state.vfs).expect("all snapshots should be dead"); 224 let vfs = Arc::try_unwrap(global_state.vfs).expect("all snapshots should be dead");
233 drop(vfs); 225 drop(vfs);
234 226
235 Ok(()) 227 Ok(())
@@ -320,7 +312,7 @@ fn loop_turn(
320 task_sender: &Sender<Task>, 312 task_sender: &Sender<Task>,
321 libdata_sender: &Sender<LibraryData>, 313 libdata_sender: &Sender<LibraryData>,
322 connection: &Connection, 314 connection: &Connection,
323 world_state: &mut WorldState, 315 global_state: &mut GlobalState,
324 loop_state: &mut LoopState, 316 loop_state: &mut LoopState,
325 event: Event, 317 event: Event,
326) -> Result<()> { 318) -> Result<()> {
@@ -336,22 +328,22 @@ fn loop_turn(
336 328
337 match event { 329 match event {
338 Event::Task(task) => { 330 Event::Task(task) => {
339 on_task(task, &connection.sender, &mut loop_state.pending_requests, world_state); 331 on_task(task, &connection.sender, &mut loop_state.pending_requests, global_state);
340 world_state.maybe_collect_garbage(); 332 global_state.maybe_collect_garbage();
341 } 333 }
342 Event::Vfs(task) => { 334 Event::Vfs(task) => {
343 world_state.vfs.write().handle_task(task); 335 global_state.vfs.write().handle_task(task);
344 } 336 }
345 Event::Lib(lib) => { 337 Event::Lib(lib) => {
346 world_state.add_lib(lib); 338 global_state.add_lib(lib);
347 world_state.maybe_collect_garbage(); 339 global_state.maybe_collect_garbage();
348 loop_state.in_flight_libraries -= 1; 340 loop_state.in_flight_libraries -= 1;
349 loop_state.roots_scanned += 1; 341 loop_state.roots_scanned += 1;
350 } 342 }
351 Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?, 343 Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?,
352 Event::Msg(msg) => match msg { 344 Event::Msg(msg) => match msg {
353 Message::Request(req) => on_request( 345 Message::Request(req) => on_request(
354 world_state, 346 global_state,
355 &mut loop_state.pending_requests, 347 &mut loop_state.pending_requests,
356 pool, 348 pool,
357 task_sender, 349 task_sender,
@@ -360,7 +352,7 @@ fn loop_turn(
360 req, 352 req,
361 )?, 353 )?,
362 Message::Notification(not) => { 354 Message::Notification(not) => {
363 on_notification(&connection.sender, world_state, loop_state, not)?; 355 on_notification(&connection.sender, global_state, loop_state, not)?;
364 } 356 }
365 Message::Response(resp) => { 357 Message::Response(resp) => {
366 let removed = loop_state.pending_responses.remove(&resp.id); 358 let removed = loop_state.pending_responses.remove(&resp.id);
@@ -379,9 +371,9 @@ fn loop_turn(
379 } 371 }
380 (None, Some(configs)) => { 372 (None, Some(configs)) => {
381 if let Some(new_config) = configs.get(0) { 373 if let Some(new_config) = configs.get(0) {
382 let mut config = world_state.config.clone(); 374 let mut config = global_state.config.clone();
383 config.update(&new_config); 375 config.update(&new_config);
384 world_state.update_configuration(config); 376 global_state.update_configuration(config);
385 } 377 }
386 } 378 }
387 (None, None) => { 379 (None, None) => {
@@ -394,7 +386,7 @@ fn loop_turn(
394 }; 386 };
395 387
396 let mut state_changed = false; 388 let mut state_changed = false;
397 if let Some(changes) = world_state.process_changes(&mut loop_state.roots_scanned) { 389 if let Some(changes) = global_state.process_changes(&mut loop_state.roots_scanned) {
398 state_changed = true; 390 state_changed = true;
399 loop_state.pending_libraries.extend(changes); 391 loop_state.pending_libraries.extend(changes);
400 } 392 }
@@ -416,7 +408,7 @@ fn loop_turn(
416 } 408 }
417 409
418 let show_progress = 410 let show_progress =
419 !loop_state.workspace_loaded && world_state.config.client_caps.work_done_progress; 411 !loop_state.workspace_loaded && global_state.config.client_caps.work_done_progress;
420 412
421 if !loop_state.workspace_loaded 413 if !loop_state.workspace_loaded
422 && loop_state.roots_scanned == loop_state.roots_total 414 && loop_state.roots_scanned == loop_state.roots_total
@@ -425,7 +417,7 @@ fn loop_turn(
425 { 417 {
426 state_changed = true; 418 state_changed = true;
427 loop_state.workspace_loaded = true; 419 loop_state.workspace_loaded = true;
428 if let Some(flycheck) = &world_state.flycheck { 420 if let Some(flycheck) = &global_state.flycheck {
429 flycheck.update(); 421 flycheck.update();
430 } 422 }
431 } 423 }
@@ -437,13 +429,13 @@ fn loop_turn(
437 if state_changed && loop_state.workspace_loaded { 429 if state_changed && loop_state.workspace_loaded {
438 update_file_notifications_on_threadpool( 430 update_file_notifications_on_threadpool(
439 pool, 431 pool,
440 world_state.snapshot(), 432 global_state.snapshot(),
441 task_sender.clone(), 433 task_sender.clone(),
442 loop_state.subscriptions.subscriptions(), 434 loop_state.subscriptions.subscriptions(),
443 ); 435 );
444 pool.execute({ 436 pool.execute({
445 let subs = loop_state.subscriptions.subscriptions(); 437 let subs = loop_state.subscriptions.subscriptions();
446 let snap = world_state.snapshot(); 438 let snap = global_state.snapshot();
447 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) 439 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ())
448 }); 440 });
449 } 441 }
@@ -467,7 +459,7 @@ fn on_task(
467 task: Task, 459 task: Task,
468 msg_sender: &Sender<Message>, 460 msg_sender: &Sender<Message>,
469 pending_requests: &mut PendingRequests, 461 pending_requests: &mut PendingRequests,
470 state: &mut WorldState, 462 state: &mut GlobalState,
471) { 463) {
472 match task { 464 match task {
473 Task::Respond(response) => { 465 Task::Respond(response) => {
@@ -485,7 +477,7 @@ fn on_task(
485} 477}
486 478
487fn on_request( 479fn on_request(
488 world: &mut WorldState, 480 global_state: &mut GlobalState,
489 pending_requests: &mut PendingRequests, 481 pending_requests: &mut PendingRequests,
490 pool: &ThreadPool, 482 pool: &ThreadPool,
491 task_sender: &Sender<Task>, 483 task_sender: &Sender<Task>,
@@ -496,7 +488,7 @@ fn on_request(
496 let mut pool_dispatcher = PoolDispatcher { 488 let mut pool_dispatcher = PoolDispatcher {
497 req: Some(req), 489 req: Some(req),
498 pool, 490 pool,
499 world, 491 global_state,
500 task_sender, 492 task_sender,
501 msg_sender, 493 msg_sender,
502 pending_requests, 494 pending_requests,
@@ -517,6 +509,8 @@ fn on_request(
517 .on::<lsp_ext::Runnables>(handlers::handle_runnables)? 509 .on::<lsp_ext::Runnables>(handlers::handle_runnables)?
518 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? 510 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
519 .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)? 511 .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)?
512 .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)?
513 .on::<lsp_ext::HoverRequest>(handlers::handle_hover)?
520 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? 514 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
521 .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? 515 .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
522 .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? 516 .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
@@ -528,7 +522,6 @@ fn on_request(
528 .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? 522 .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
529 .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? 523 .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?
530 .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)? 524 .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)?
531 .on::<lsp_types::request::HoverRequest>(handlers::handle_hover)?
532 .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)? 525 .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)?
533 .on::<lsp_types::request::Rename>(handlers::handle_rename)? 526 .on::<lsp_types::request::Rename>(handlers::handle_rename)?
534 .on::<lsp_types::request::References>(handlers::handle_references)? 527 .on::<lsp_types::request::References>(handlers::handle_references)?
@@ -552,7 +545,7 @@ fn on_request(
552 545
553fn on_notification( 546fn on_notification(
554 msg_sender: &Sender<Message>, 547 msg_sender: &Sender<Message>,
555 state: &mut WorldState, 548 state: &mut GlobalState,
556 loop_state: &mut LoopState, 549 loop_state: &mut LoopState,
557 not: Notification, 550 not: Notification,
558) -> Result<()> { 551) -> Result<()> {
@@ -726,7 +719,7 @@ fn apply_document_changes(
726 719
727fn on_check_task( 720fn on_check_task(
728 task: CheckTask, 721 task: CheckTask,
729 world_state: &mut WorldState, 722 global_state: &mut GlobalState,
730 task_sender: &Sender<Task>, 723 task_sender: &Sender<Task>,
731) -> Result<()> { 724) -> Result<()> {
732 match task { 725 match task {
@@ -745,7 +738,7 @@ fn on_check_task(
745 .uri 738 .uri
746 .to_file_path() 739 .to_file_path()
747 .map_err(|()| format!("invalid uri: {}", diag.location.uri))?; 740 .map_err(|()| format!("invalid uri: {}", diag.location.uri))?;
748 let file_id = match world_state.vfs.read().path2file(&path) { 741 let file_id = match global_state.vfs.read().path2file(&path) {
749 Some(file) => FileId(file.0), 742 Some(file) => FileId(file.0),
750 None => { 743 None => {
751 log::error!( 744 log::error!(
@@ -765,7 +758,7 @@ fn on_check_task(
765 } 758 }
766 759
767 CheckTask::Status(status) => { 760 CheckTask::Status(status) => {
768 if world_state.config.client_caps.work_done_progress { 761 if global_state.config.client_caps.work_done_progress {
769 let progress = match status { 762 let progress = match status {
770 Status::Being => { 763 Status::Being => {
771 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { 764 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
@@ -804,7 +797,7 @@ fn on_check_task(
804 Ok(()) 797 Ok(())
805} 798}
806 799
807fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut WorldState) { 800fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut GlobalState) {
808 let subscriptions = state.diagnostics.handle_task(task); 801 let subscriptions = state.diagnostics.handle_task(task);
809 802
810 for file_id in subscriptions { 803 for file_id in subscriptions {
@@ -879,7 +872,7 @@ fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) {
879struct PoolDispatcher<'a> { 872struct PoolDispatcher<'a> {
880 req: Option<Request>, 873 req: Option<Request>,
881 pool: &'a ThreadPool, 874 pool: &'a ThreadPool,
882 world: &'a mut WorldState, 875 global_state: &'a mut GlobalState,
883 pending_requests: &'a mut PendingRequests, 876 pending_requests: &'a mut PendingRequests,
884 msg_sender: &'a Sender<Message>, 877 msg_sender: &'a Sender<Message>,
885 task_sender: &'a Sender<Task>, 878 task_sender: &'a Sender<Task>,
@@ -890,7 +883,7 @@ impl<'a> PoolDispatcher<'a> {
890 /// Dispatches the request onto the current thread 883 /// Dispatches the request onto the current thread
891 fn on_sync<R>( 884 fn on_sync<R>(
892 &mut self, 885 &mut self,
893 f: fn(&mut WorldState, R::Params) -> Result<R::Result>, 886 f: fn(&mut GlobalState, R::Params) -> Result<R::Result>,
894 ) -> Result<&mut Self> 887 ) -> Result<&mut Self>
895 where 888 where
896 R: lsp_types::request::Request + 'static, 889 R: lsp_types::request::Request + 'static,
@@ -903,18 +896,21 @@ impl<'a> PoolDispatcher<'a> {
903 return Ok(self); 896 return Ok(self);
904 } 897 }
905 }; 898 };
906 let world = panic::AssertUnwindSafe(&mut *self.world); 899 let world = panic::AssertUnwindSafe(&mut *self.global_state);
907 let task = panic::catch_unwind(move || { 900 let task = panic::catch_unwind(move || {
908 let result = f(world.0, params); 901 let result = f(world.0, params);
909 result_to_task::<R>(id, result) 902 result_to_task::<R>(id, result)
910 }) 903 })
911 .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?; 904 .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?;
912 on_task(task, self.msg_sender, self.pending_requests, self.world); 905 on_task(task, self.msg_sender, self.pending_requests, self.global_state);
913 Ok(self) 906 Ok(self)
914 } 907 }
915 908
916 /// Dispatches the request onto thread pool 909 /// Dispatches the request onto thread pool
917 fn on<R>(&mut self, f: fn(WorldSnapshot, R::Params) -> Result<R::Result>) -> Result<&mut Self> 910 fn on<R>(
911 &mut self,
912 f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
913 ) -> Result<&mut Self>
918 where 914 where
919 R: lsp_types::request::Request + 'static, 915 R: lsp_types::request::Request + 'static,
920 R::Params: DeserializeOwned + Send + 'static, 916 R::Params: DeserializeOwned + Send + 'static,
@@ -928,7 +924,7 @@ impl<'a> PoolDispatcher<'a> {
928 }; 924 };
929 925
930 self.pool.execute({ 926 self.pool.execute({
931 let world = self.world.snapshot(); 927 let world = self.global_state.snapshot();
932 let sender = self.task_sender.clone(); 928 let sender = self.task_sender.clone();
933 move || { 929 move || {
934 let result = f(world, params); 930 let result = f(world, params);
@@ -1012,7 +1008,7 @@ where
1012 1008
1013fn update_file_notifications_on_threadpool( 1009fn update_file_notifications_on_threadpool(
1014 pool: &ThreadPool, 1010 pool: &ThreadPool,
1015 world: WorldSnapshot, 1011 world: GlobalStateSnapshot,
1016 task_sender: Sender<Task>, 1012 task_sender: Sender<Task>,
1017 subscriptions: Vec<FileId>, 1013 subscriptions: Vec<FileId>,
1018) { 1014) {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 1f910ff82..a41adf8b0 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -12,40 +12,37 @@ use lsp_types::{
12 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 12 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
14 CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, 14 CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight,
15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, 15 DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, MarkupContent,
16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, 16 MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams,
17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, 17 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, 18 TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_cfg::CfgExpr;
21use ra_ide::{ 20use ra_ide::{
22 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind,
23 TextEdit, 22 SearchScope, TextEdit,
24}; 23};
25use ra_prof::profile; 24use ra_prof::profile;
26use ra_project_model::TargetKind; 25use ra_project_model::TargetKind;
27use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize}; 26use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
28use rustc_hash::FxHashMap;
29use serde::{Deserialize, Serialize}; 27use serde::{Deserialize, Serialize};
30use serde_json::to_value; 28use serde_json::to_value;
31use stdx::format_to; 29use stdx::{format_to, split1};
32 30
33use crate::{ 31use crate::{
34 cargo_target_spec::CargoTargetSpec, 32 cargo_target_spec::CargoTargetSpec,
35 config::RustfmtConfig, 33 config::RustfmtConfig,
36 diagnostics::DiagnosticTask, 34 diagnostics::DiagnosticTask,
37 from_json, from_proto, 35 from_json, from_proto,
36 global_state::GlobalStateSnapshot,
38 lsp_ext::{self, InlayHint, InlayHintsParams}, 37 lsp_ext::{self, InlayHint, InlayHintsParams},
39 to_proto, 38 to_proto, LspError, Result,
40 world::WorldSnapshot,
41 LspError, Result,
42}; 39};
43 40
44pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { 41pub fn handle_analyzer_status(snap: GlobalStateSnapshot, _: ()) -> Result<String> {
45 let _p = profile("handle_analyzer_status"); 42 let _p = profile("handle_analyzer_status");
46 let mut buf = world.status(); 43 let mut buf = snap.status();
47 format_to!(buf, "\n\nrequests:\n"); 44 format_to!(buf, "\n\nrequests:\n");
48 let requests = world.latest_requests.read(); 45 let requests = snap.latest_requests.read();
49 for (is_last, r) in requests.iter() { 46 for (is_last, r) in requests.iter() {
50 let mark = if is_last { "*" } else { " " }; 47 let mark = if is_last { "*" } else { " " };
51 format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis()); 48 format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis());
@@ -54,37 +51,37 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> {
54} 51}
55 52
56pub fn handle_syntax_tree( 53pub fn handle_syntax_tree(
57 world: WorldSnapshot, 54 snap: GlobalStateSnapshot,
58 params: lsp_ext::SyntaxTreeParams, 55 params: lsp_ext::SyntaxTreeParams,
59) -> Result<String> { 56) -> Result<String> {
60 let _p = profile("handle_syntax_tree"); 57 let _p = profile("handle_syntax_tree");
61 let id = from_proto::file_id(&world, &params.text_document.uri)?; 58 let id = from_proto::file_id(&snap, &params.text_document.uri)?;
62 let line_index = world.analysis().file_line_index(id)?; 59 let line_index = snap.analysis().file_line_index(id)?;
63 let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); 60 let text_range = params.range.map(|r| from_proto::text_range(&line_index, r));
64 let res = world.analysis().syntax_tree(id, text_range)?; 61 let res = snap.analysis().syntax_tree(id, text_range)?;
65 Ok(res) 62 Ok(res)
66} 63}
67 64
68pub fn handle_expand_macro( 65pub fn handle_expand_macro(
69 world: WorldSnapshot, 66 snap: GlobalStateSnapshot,
70 params: lsp_ext::ExpandMacroParams, 67 params: lsp_ext::ExpandMacroParams,
71) -> Result<Option<lsp_ext::ExpandedMacro>> { 68) -> Result<Option<lsp_ext::ExpandedMacro>> {
72 let _p = profile("handle_expand_macro"); 69 let _p = profile("handle_expand_macro");
73 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 70 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
74 let line_index = world.analysis().file_line_index(file_id)?; 71 let line_index = snap.analysis().file_line_index(file_id)?;
75 let offset = from_proto::offset(&line_index, params.position); 72 let offset = from_proto::offset(&line_index, params.position);
76 73
77 let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; 74 let res = snap.analysis().expand_macro(FilePosition { file_id, offset })?;
78 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion })) 75 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
79} 76}
80 77
81pub fn handle_selection_range( 78pub fn handle_selection_range(
82 world: WorldSnapshot, 79 snap: GlobalStateSnapshot,
83 params: lsp_types::SelectionRangeParams, 80 params: lsp_types::SelectionRangeParams,
84) -> Result<Option<Vec<lsp_types::SelectionRange>>> { 81) -> Result<Option<Vec<lsp_types::SelectionRange>>> {
85 let _p = profile("handle_selection_range"); 82 let _p = profile("handle_selection_range");
86 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 83 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
87 let line_index = world.analysis().file_line_index(file_id)?; 84 let line_index = snap.analysis().file_line_index(file_id)?;
88 let res: Result<Vec<lsp_types::SelectionRange>> = params 85 let res: Result<Vec<lsp_types::SelectionRange>> = params
89 .positions 86 .positions
90 .into_iter() 87 .into_iter()
@@ -96,7 +93,7 @@ pub fn handle_selection_range(
96 loop { 93 loop {
97 ranges.push(range); 94 ranges.push(range);
98 let frange = FileRange { file_id, range }; 95 let frange = FileRange { file_id, range };
99 let next = world.analysis().extend_selection(frange)?; 96 let next = snap.analysis().extend_selection(frange)?;
100 if next == range { 97 if next == range {
101 break; 98 break;
102 } else { 99 } else {
@@ -122,18 +119,18 @@ pub fn handle_selection_range(
122} 119}
123 120
124pub fn handle_matching_brace( 121pub fn handle_matching_brace(
125 world: WorldSnapshot, 122 snap: GlobalStateSnapshot,
126 params: lsp_ext::MatchingBraceParams, 123 params: lsp_ext::MatchingBraceParams,
127) -> Result<Vec<Position>> { 124) -> Result<Vec<Position>> {
128 let _p = profile("handle_matching_brace"); 125 let _p = profile("handle_matching_brace");
129 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 126 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
130 let line_index = world.analysis().file_line_index(file_id)?; 127 let line_index = snap.analysis().file_line_index(file_id)?;
131 let res = params 128 let res = params
132 .positions 129 .positions
133 .into_iter() 130 .into_iter()
134 .map(|position| { 131 .map(|position| {
135 let offset = from_proto::offset(&line_index, position); 132 let offset = from_proto::offset(&line_index, position);
136 let offset = match world.analysis().matching_brace(FilePosition { file_id, offset }) { 133 let offset = match snap.analysis().matching_brace(FilePosition { file_id, offset }) {
137 Ok(Some(matching_brace_offset)) => matching_brace_offset, 134 Ok(Some(matching_brace_offset)) => matching_brace_offset,
138 Err(_) | Ok(None) => offset, 135 Err(_) | Ok(None) => offset,
139 }; 136 };
@@ -144,17 +141,17 @@ pub fn handle_matching_brace(
144} 141}
145 142
146pub fn handle_join_lines( 143pub fn handle_join_lines(
147 world: WorldSnapshot, 144 snap: GlobalStateSnapshot,
148 params: lsp_ext::JoinLinesParams, 145 params: lsp_ext::JoinLinesParams,
149) -> Result<Vec<lsp_types::TextEdit>> { 146) -> Result<Vec<lsp_types::TextEdit>> {
150 let _p = profile("handle_join_lines"); 147 let _p = profile("handle_join_lines");
151 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 148 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
152 let line_index = world.analysis().file_line_index(file_id)?; 149 let line_index = snap.analysis().file_line_index(file_id)?;
153 let line_endings = world.file_line_endings(file_id); 150 let line_endings = snap.file_line_endings(file_id);
154 let mut res = TextEdit::default(); 151 let mut res = TextEdit::default();
155 for range in params.ranges { 152 for range in params.ranges {
156 let range = from_proto::text_range(&line_index, range); 153 let range = from_proto::text_range(&line_index, range);
157 let edit = world.analysis().join_lines(FileRange { file_id, range })?; 154 let edit = snap.analysis().join_lines(FileRange { file_id, range })?;
158 match res.union(edit) { 155 match res.union(edit) {
159 Ok(()) => (), 156 Ok(()) => (),
160 Err(_edit) => { 157 Err(_edit) => {
@@ -167,37 +164,37 @@ pub fn handle_join_lines(
167} 164}
168 165
169pub fn handle_on_enter( 166pub fn handle_on_enter(
170 world: WorldSnapshot, 167 snap: GlobalStateSnapshot,
171 params: lsp_types::TextDocumentPositionParams, 168 params: lsp_types::TextDocumentPositionParams,
172) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> { 169) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
173 let _p = profile("handle_on_enter"); 170 let _p = profile("handle_on_enter");
174 let position = from_proto::file_position(&world, params)?; 171 let position = from_proto::file_position(&snap, params)?;
175 let edit = match world.analysis().on_enter(position)? { 172 let edit = match snap.analysis().on_enter(position)? {
176 None => return Ok(None), 173 None => return Ok(None),
177 Some(it) => it, 174 Some(it) => it,
178 }; 175 };
179 let line_index = world.analysis().file_line_index(position.file_id)?; 176 let line_index = snap.analysis().file_line_index(position.file_id)?;
180 let line_endings = world.file_line_endings(position.file_id); 177 let line_endings = snap.file_line_endings(position.file_id);
181 let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit); 178 let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit);
182 Ok(Some(edit)) 179 Ok(Some(edit))
183} 180}
184 181
185// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. 182// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
186pub fn handle_on_type_formatting( 183pub fn handle_on_type_formatting(
187 world: WorldSnapshot, 184 snap: GlobalStateSnapshot,
188 params: lsp_types::DocumentOnTypeFormattingParams, 185 params: lsp_types::DocumentOnTypeFormattingParams,
189) -> Result<Option<Vec<lsp_types::TextEdit>>> { 186) -> Result<Option<Vec<lsp_types::TextEdit>>> {
190 let _p = profile("handle_on_type_formatting"); 187 let _p = profile("handle_on_type_formatting");
191 let mut position = from_proto::file_position(&world, params.text_document_position)?; 188 let mut position = from_proto::file_position(&snap, params.text_document_position)?;
192 let line_index = world.analysis().file_line_index(position.file_id)?; 189 let line_index = snap.analysis().file_line_index(position.file_id)?;
193 let line_endings = world.file_line_endings(position.file_id); 190 let line_endings = snap.file_line_endings(position.file_id);
194 191
195 // in `ra_ide`, the `on_type` invariant is that 192 // in `ra_ide`, the `on_type` invariant is that
196 // `text.char_at(position) == typed_char`. 193 // `text.char_at(position) == typed_char`.
197 position.offset -= TextSize::of('.'); 194 position.offset -= TextSize::of('.');
198 let char_typed = params.ch.chars().next().unwrap_or('\0'); 195 let char_typed = params.ch.chars().next().unwrap_or('\0');
199 assert!({ 196 assert!({
200 let text = world.analysis().file_text(position.file_id)?; 197 let text = snap.analysis().file_text(position.file_id)?;
201 text[usize::from(position.offset)..].starts_with(char_typed) 198 text[usize::from(position.offset)..].starts_with(char_typed)
202 }); 199 });
203 200
@@ -209,7 +206,7 @@ pub fn handle_on_type_formatting(
209 return Ok(None); 206 return Ok(None);
210 } 207 }
211 208
212 let edit = world.analysis().on_char_typed(position, char_typed)?; 209 let edit = snap.analysis().on_char_typed(position, char_typed)?;
213 let mut edit = match edit { 210 let mut edit = match edit {
214 Some(it) => it, 211 Some(it) => it,
215 None => return Ok(None), 212 None => return Ok(None),
@@ -223,16 +220,16 @@ pub fn handle_on_type_formatting(
223} 220}
224 221
225pub fn handle_document_symbol( 222pub fn handle_document_symbol(
226 world: WorldSnapshot, 223 snap: GlobalStateSnapshot,
227 params: lsp_types::DocumentSymbolParams, 224 params: lsp_types::DocumentSymbolParams,
228) -> Result<Option<lsp_types::DocumentSymbolResponse>> { 225) -> Result<Option<lsp_types::DocumentSymbolResponse>> {
229 let _p = profile("handle_document_symbol"); 226 let _p = profile("handle_document_symbol");
230 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 227 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
231 let line_index = world.analysis().file_line_index(file_id)?; 228 let line_index = snap.analysis().file_line_index(file_id)?;
232 229
233 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); 230 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
234 231
235 for symbol in world.analysis().file_structure(file_id)? { 232 for symbol in snap.analysis().file_structure(file_id)? {
236 let doc_symbol = DocumentSymbol { 233 let doc_symbol = DocumentSymbol {
237 name: symbol.label, 234 name: symbol.label,
238 detail: symbol.detail, 235 detail: symbol.detail,
@@ -258,10 +255,10 @@ pub fn handle_document_symbol(
258 } 255 }
259 } 256 }
260 257
261 let res = if world.config.client_caps.hierarchical_symbols { 258 let res = if snap.config.client_caps.hierarchical_symbols {
262 document_symbols.into() 259 document_symbols.into()
263 } else { 260 } else {
264 let url = to_proto::url(&world, file_id)?; 261 let url = to_proto::url(&snap, file_id)?;
265 let mut symbol_information = Vec::<SymbolInformation>::new(); 262 let mut symbol_information = Vec::<SymbolInformation>::new();
266 for symbol in document_symbols { 263 for symbol in document_symbols {
267 flatten_document_symbol(&symbol, None, &url, &mut symbol_information); 264 flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
@@ -291,7 +288,7 @@ pub fn handle_document_symbol(
291} 288}
292 289
293pub fn handle_workspace_symbol( 290pub fn handle_workspace_symbol(
294 world: WorldSnapshot, 291 snap: GlobalStateSnapshot,
295 params: lsp_types::WorkspaceSymbolParams, 292 params: lsp_types::WorkspaceSymbolParams,
296) -> Result<Option<Vec<SymbolInformation>>> { 293) -> Result<Option<Vec<SymbolInformation>>> {
297 let _p = profile("handle_workspace_symbol"); 294 let _p = profile("handle_workspace_symbol");
@@ -309,22 +306,22 @@ pub fn handle_workspace_symbol(
309 q.limit(128); 306 q.limit(128);
310 q 307 q
311 }; 308 };
312 let mut res = exec_query(&world, query)?; 309 let mut res = exec_query(&snap, query)?;
313 if res.is_empty() && !all_symbols { 310 if res.is_empty() && !all_symbols {
314 let mut query = Query::new(params.query); 311 let mut query = Query::new(params.query);
315 query.limit(128); 312 query.limit(128);
316 res = exec_query(&world, query)?; 313 res = exec_query(&snap, query)?;
317 } 314 }
318 315
319 return Ok(Some(res)); 316 return Ok(Some(res));
320 317
321 fn exec_query(world: &WorldSnapshot, query: Query) -> Result<Vec<SymbolInformation>> { 318 fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
322 let mut res = Vec::new(); 319 let mut res = Vec::new();
323 for nav in world.analysis().symbol_search(query)? { 320 for nav in snap.analysis().symbol_search(query)? {
324 let info = SymbolInformation { 321 let info = SymbolInformation {
325 name: nav.name().to_string(), 322 name: nav.name().to_string(),
326 kind: to_proto::symbol_kind(nav.kind()), 323 kind: to_proto::symbol_kind(nav.kind()),
327 location: to_proto::location(world, nav.file_range())?, 324 location: to_proto::location(snap, nav.file_range())?,
328 container_name: nav.container_name().map(|v| v.to_string()), 325 container_name: nav.container_name().map(|v| v.to_string()),
329 deprecated: None, 326 deprecated: None,
330 }; 327 };
@@ -335,88 +332,83 @@ pub fn handle_workspace_symbol(
335} 332}
336 333
337pub fn handle_goto_definition( 334pub fn handle_goto_definition(
338 world: WorldSnapshot, 335 snap: GlobalStateSnapshot,
339 params: lsp_types::GotoDefinitionParams, 336 params: lsp_types::GotoDefinitionParams,
340) -> Result<Option<lsp_types::GotoDefinitionResponse>> { 337) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
341 let _p = profile("handle_goto_definition"); 338 let _p = profile("handle_goto_definition");
342 let position = from_proto::file_position(&world, params.text_document_position_params)?; 339 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
343 let nav_info = match world.analysis().goto_definition(position)? { 340 let nav_info = match snap.analysis().goto_definition(position)? {
344 None => return Ok(None), 341 None => return Ok(None),
345 Some(it) => it, 342 Some(it) => it,
346 }; 343 };
347 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 344 let src = FileRange { file_id: position.file_id, range: nav_info.range };
348 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 345 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
349 Ok(Some(res)) 346 Ok(Some(res))
350} 347}
351 348
352pub fn handle_goto_implementation( 349pub fn handle_goto_implementation(
353 world: WorldSnapshot, 350 snap: GlobalStateSnapshot,
354 params: lsp_types::request::GotoImplementationParams, 351 params: lsp_types::request::GotoImplementationParams,
355) -> Result<Option<lsp_types::request::GotoImplementationResponse>> { 352) -> Result<Option<lsp_types::request::GotoImplementationResponse>> {
356 let _p = profile("handle_goto_implementation"); 353 let _p = profile("handle_goto_implementation");
357 let position = from_proto::file_position(&world, params.text_document_position_params)?; 354 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
358 let nav_info = match world.analysis().goto_implementation(position)? { 355 let nav_info = match snap.analysis().goto_implementation(position)? {
359 None => return Ok(None), 356 None => return Ok(None),
360 Some(it) => it, 357 Some(it) => it,
361 }; 358 };
362 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 359 let src = FileRange { file_id: position.file_id, range: nav_info.range };
363 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 360 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
364 Ok(Some(res)) 361 Ok(Some(res))
365} 362}
366 363
367pub fn handle_goto_type_definition( 364pub fn handle_goto_type_definition(
368 world: WorldSnapshot, 365 snap: GlobalStateSnapshot,
369 params: lsp_types::request::GotoTypeDefinitionParams, 366 params: lsp_types::request::GotoTypeDefinitionParams,
370) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> { 367) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
371 let _p = profile("handle_goto_type_definition"); 368 let _p = profile("handle_goto_type_definition");
372 let position = from_proto::file_position(&world, params.text_document_position_params)?; 369 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
373 let nav_info = match world.analysis().goto_type_definition(position)? { 370 let nav_info = match snap.analysis().goto_type_definition(position)? {
374 None => return Ok(None), 371 None => return Ok(None),
375 Some(it) => it, 372 Some(it) => it,
376 }; 373 };
377 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 374 let src = FileRange { file_id: position.file_id, range: nav_info.range };
378 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 375 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
379 Ok(Some(res)) 376 Ok(Some(res))
380} 377}
381 378
382pub fn handle_parent_module( 379pub fn handle_parent_module(
383 world: WorldSnapshot, 380 snap: GlobalStateSnapshot,
384 params: lsp_types::TextDocumentPositionParams, 381 params: lsp_types::TextDocumentPositionParams,
385) -> Result<Option<lsp_types::GotoDefinitionResponse>> { 382) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
386 let _p = profile("handle_parent_module"); 383 let _p = profile("handle_parent_module");
387 let position = from_proto::file_position(&world, params)?; 384 let position = from_proto::file_position(&snap, params)?;
388 let navs = world.analysis().parent_module(position)?; 385 let navs = snap.analysis().parent_module(position)?;
389 let res = to_proto::goto_definition_response(&world, None, navs)?; 386 let res = to_proto::goto_definition_response(&snap, None, navs)?;
390 Ok(Some(res)) 387 Ok(Some(res))
391} 388}
392 389
393pub fn handle_runnables( 390pub fn handle_runnables(
394 world: WorldSnapshot, 391 snap: GlobalStateSnapshot,
395 params: lsp_ext::RunnablesParams, 392 params: lsp_ext::RunnablesParams,
396) -> Result<Vec<lsp_ext::Runnable>> { 393) -> Result<Vec<lsp_ext::Runnable>> {
397 let _p = profile("handle_runnables"); 394 let _p = profile("handle_runnables");
398 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 395 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
399 let line_index = world.analysis().file_line_index(file_id)?; 396 let line_index = snap.analysis().file_line_index(file_id)?;
400 let offset = params.position.map(|it| from_proto::offset(&line_index, it)); 397 let offset = params.position.map(|it| from_proto::offset(&line_index, it));
401 let mut res = Vec::new(); 398 let mut res = Vec::new();
402 let workspace_root = world.workspace_root_for(file_id); 399 let workspace_root = snap.workspace_root_for(file_id);
403 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; 400 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
404 for runnable in world.analysis().runnables(file_id)? { 401 for runnable in snap.analysis().runnables(file_id)? {
405 if let Some(offset) = offset { 402 if let Some(offset) = offset {
406 if !runnable.range.contains_inclusive(offset) { 403 if !runnable.nav.full_range().contains_inclusive(offset) {
407 continue; 404 continue;
408 } 405 }
409 } 406 }
410 // Do not suggest binary run on other target than binary 407 if should_skip_target(&runnable, cargo_spec.as_ref()) {
411 if let RunnableKind::Bin = runnable.kind { 408 continue;
412 if let Some(spec) = &cargo_spec {
413 match spec.target_kind {
414 TargetKind::Bin => {}
415 _ => continue,
416 }
417 }
418 } 409 }
419 res.push(to_lsp_runnable(&world, file_id, runnable)?); 410
411 res.push(to_proto::runnable(&snap, file_id, runnable)?);
420 } 412 }
421 413
422 // Add `cargo check` and `cargo test` for the whole package 414 // Add `cargo check` and `cargo test` for the whole package
@@ -424,25 +416,31 @@ pub fn handle_runnables(
424 Some(spec) => { 416 Some(spec) => {
425 for &cmd in ["check", "test"].iter() { 417 for &cmd in ["check", "test"].iter() {
426 res.push(lsp_ext::Runnable { 418 res.push(lsp_ext::Runnable {
427 range: Default::default(),
428 label: format!("cargo {} -p {}", cmd, spec.package), 419 label: format!("cargo {} -p {}", cmd, spec.package),
429 bin: "cargo".to_string(), 420 location: None,
430 args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], 421 kind: lsp_ext::RunnableKind::Cargo,
431 extra_args: Vec::new(), 422 args: lsp_ext::CargoRunnable {
432 env: FxHashMap::default(), 423 workspace_root: workspace_root.map(|root| root.to_owned()),
433 cwd: workspace_root.map(|root| root.to_owned()), 424 cargo_args: vec![
425 cmd.to_string(),
426 "--package".to_string(),
427 spec.package.clone(),
428 ],
429 executable_args: Vec::new(),
430 },
434 }) 431 })
435 } 432 }
436 } 433 }
437 None => { 434 None => {
438 res.push(lsp_ext::Runnable { 435 res.push(lsp_ext::Runnable {
439 range: Default::default(),
440 label: "cargo check --workspace".to_string(), 436 label: "cargo check --workspace".to_string(),
441 bin: "cargo".to_string(), 437 location: None,
442 args: vec!["check".to_string(), "--workspace".to_string()], 438 kind: lsp_ext::RunnableKind::Cargo,
443 extra_args: Vec::new(), 439 args: lsp_ext::CargoRunnable {
444 env: FxHashMap::default(), 440 workspace_root: workspace_root.map(|root| root.to_owned()),
445 cwd: workspace_root.map(|root| root.to_owned()), 441 cargo_args: vec!["check".to_string(), "--workspace".to_string()],
442 executable_args: Vec::new(),
443 },
446 }); 444 });
447 } 445 }
448 } 446 }
@@ -450,16 +448,16 @@ pub fn handle_runnables(
450} 448}
451 449
452pub fn handle_completion( 450pub fn handle_completion(
453 world: WorldSnapshot, 451 snap: GlobalStateSnapshot,
454 params: lsp_types::CompletionParams, 452 params: lsp_types::CompletionParams,
455) -> Result<Option<lsp_types::CompletionResponse>> { 453) -> Result<Option<lsp_types::CompletionResponse>> {
456 let _p = profile("handle_completion"); 454 let _p = profile("handle_completion");
457 let position = from_proto::file_position(&world, params.text_document_position)?; 455 let position = from_proto::file_position(&snap, params.text_document_position)?;
458 let completion_triggered_after_single_colon = { 456 let completion_triggered_after_single_colon = {
459 let mut res = false; 457 let mut res = false;
460 if let Some(ctx) = params.context { 458 if let Some(ctx) = params.context {
461 if ctx.trigger_character.unwrap_or_default() == ":" { 459 if ctx.trigger_character.unwrap_or_default() == ":" {
462 let source_file = world.analysis().parse(position.file_id)?; 460 let source_file = snap.analysis().parse(position.file_id)?;
463 let syntax = source_file.syntax(); 461 let syntax = source_file.syntax();
464 let text = syntax.text(); 462 let text = syntax.text();
465 if let Some(next_char) = text.char_at(position.offset) { 463 if let Some(next_char) = text.char_at(position.offset) {
@@ -477,12 +475,12 @@ pub fn handle_completion(
477 return Ok(None); 475 return Ok(None);
478 } 476 }
479 477
480 let items = match world.analysis().completions(&world.config.completion, position)? { 478 let items = match snap.analysis().completions(&snap.config.completion, position)? {
481 None => return Ok(None), 479 None => return Ok(None),
482 Some(items) => items, 480 Some(items) => items,
483 }; 481 };
484 let line_index = world.analysis().file_line_index(position.file_id)?; 482 let line_index = snap.analysis().file_line_index(position.file_id)?;
485 let line_endings = world.file_line_endings(position.file_id); 483 let line_endings = snap.file_line_endings(position.file_id);
486 let items: Vec<CompletionItem> = items 484 let items: Vec<CompletionItem> = items
487 .into_iter() 485 .into_iter()
488 .map(|item| to_proto::completion_item(&line_index, line_endings, item)) 486 .map(|item| to_proto::completion_item(&line_index, line_endings, item))
@@ -492,15 +490,15 @@ pub fn handle_completion(
492} 490}
493 491
494pub fn handle_folding_range( 492pub fn handle_folding_range(
495 world: WorldSnapshot, 493 snap: GlobalStateSnapshot,
496 params: FoldingRangeParams, 494 params: FoldingRangeParams,
497) -> Result<Option<Vec<FoldingRange>>> { 495) -> Result<Option<Vec<FoldingRange>>> {
498 let _p = profile("handle_folding_range"); 496 let _p = profile("handle_folding_range");
499 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 497 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
500 let folds = world.analysis().folding_ranges(file_id)?; 498 let folds = snap.analysis().folding_ranges(file_id)?;
501 let text = world.analysis().file_text(file_id)?; 499 let text = snap.analysis().file_text(file_id)?;
502 let line_index = world.analysis().file_line_index(file_id)?; 500 let line_index = snap.analysis().file_line_index(file_id)?;
503 let line_folding_only = world.config.client_caps.line_folding_only; 501 let line_folding_only = snap.config.client_caps.line_folding_only;
504 let res = folds 502 let res = folds
505 .into_iter() 503 .into_iter()
506 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) 504 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
@@ -509,16 +507,16 @@ pub fn handle_folding_range(
509} 507}
510 508
511pub fn handle_signature_help( 509pub fn handle_signature_help(
512 world: WorldSnapshot, 510 snap: GlobalStateSnapshot,
513 params: lsp_types::SignatureHelpParams, 511 params: lsp_types::SignatureHelpParams,
514) -> Result<Option<lsp_types::SignatureHelp>> { 512) -> Result<Option<lsp_types::SignatureHelp>> {
515 let _p = profile("handle_signature_help"); 513 let _p = profile("handle_signature_help");
516 let position = from_proto::file_position(&world, params.text_document_position_params)?; 514 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
517 let call_info = match world.analysis().call_info(position)? { 515 let call_info = match snap.analysis().call_info(position)? {
518 None => return Ok(None), 516 None => return Ok(None),
519 Some(it) => it, 517 Some(it) => it,
520 }; 518 };
521 let concise = !world.config.call_info_full; 519 let concise = !snap.config.call_info_full;
522 let mut active_parameter = call_info.active_parameter.map(|it| it as i64); 520 let mut active_parameter = call_info.active_parameter.map(|it| it as i64);
523 if concise && call_info.signature.has_self_param { 521 if concise && call_info.signature.has_self_param {
524 active_parameter = active_parameter.map(|it| it.saturating_sub(1)); 522 active_parameter = active_parameter.map(|it| it.saturating_sub(1));
@@ -532,46 +530,56 @@ pub fn handle_signature_help(
532 })) 530 }))
533} 531}
534 532
535pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result<Option<Hover>> { 533pub fn handle_hover(
534 snap: GlobalStateSnapshot,
535 params: lsp_types::HoverParams,
536) -> Result<Option<lsp_ext::Hover>> {
536 let _p = profile("handle_hover"); 537 let _p = profile("handle_hover");
537 let position = from_proto::file_position(&world, params.text_document_position_params)?; 538 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
538 let info = match world.analysis().hover(position)? { 539 let info = match snap.analysis().hover(position)? {
539 None => return Ok(None), 540 None => return Ok(None),
540 Some(info) => info, 541 Some(info) => info,
541 }; 542 };
542 let line_index = world.analysis.file_line_index(position.file_id)?; 543 let line_index = snap.analysis.file_line_index(position.file_id)?;
543 let range = to_proto::range(&line_index, info.range); 544 let range = to_proto::range(&line_index, info.range);
544 let res = Hover { 545 let hover = lsp_ext::Hover {
545 contents: HoverContents::Markup(MarkupContent { 546 hover: lsp_types::Hover {
546 kind: MarkupKind::Markdown, 547 contents: HoverContents::Markup(MarkupContent {
547 value: crate::markdown::format_docs(&info.info.to_markup()), 548 kind: MarkupKind::Markdown,
548 }), 549 value: crate::markdown::format_docs(&info.info.to_markup()),
549 range: Some(range), 550 }),
551 range: Some(range),
552 },
553 actions: prepare_hover_actions(&snap, position.file_id, info.info.actions()),
550 }; 554 };
551 Ok(Some(res)) 555
556 Ok(Some(hover))
552} 557}
553 558
554pub fn handle_prepare_rename( 559pub fn handle_prepare_rename(
555 world: WorldSnapshot, 560 snap: GlobalStateSnapshot,
556 params: lsp_types::TextDocumentPositionParams, 561 params: lsp_types::TextDocumentPositionParams,
557) -> Result<Option<PrepareRenameResponse>> { 562) -> Result<Option<PrepareRenameResponse>> {
558 let _p = profile("handle_prepare_rename"); 563 let _p = profile("handle_prepare_rename");
559 let position = from_proto::file_position(&world, params)?; 564 let position = from_proto::file_position(&snap, params)?;
560 565
561 let optional_change = world.analysis().rename(position, "dummy")?; 566 let optional_change = snap.analysis().rename(position, "dummy")?;
562 let range = match optional_change { 567 let range = match optional_change {
563 None => return Ok(None), 568 None => return Ok(None),
564 Some(it) => it.range, 569 Some(it) => it.range,
565 }; 570 };
566 571
567 let line_index = world.analysis().file_line_index(position.file_id)?; 572 let line_index = snap.analysis().file_line_index(position.file_id)?;
568 let range = to_proto::range(&line_index, range); 573 let range = to_proto::range(&line_index, range);
569 Ok(Some(PrepareRenameResponse::Range(range))) 574 Ok(Some(PrepareRenameResponse::Range(range)))
570} 575}
571 576
572pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { 577pub fn handle_rename(
578 snap: GlobalStateSnapshot,
579 params: RenameParams,
580) -> Result<Option<WorkspaceEdit>> {
573 let _p = profile("handle_rename"); 581 let _p = profile("handle_rename");
574 let position = from_proto::file_position(&world, params.text_document_position)?; 582 let position = from_proto::file_position(&snap, params.text_document_position)?;
575 583
576 if params.new_name.is_empty() { 584 if params.new_name.is_empty() {
577 return Err(LspError::new( 585 return Err(LspError::new(
@@ -581,36 +589,36 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio
581 .into()); 589 .into());
582 } 590 }
583 591
584 let optional_change = world.analysis().rename(position, &*params.new_name)?; 592 let optional_change = snap.analysis().rename(position, &*params.new_name)?;
585 let source_change = match optional_change { 593 let source_change = match optional_change {
586 None => return Ok(None), 594 None => return Ok(None),
587 Some(it) => it.info, 595 Some(it) => it.info,
588 }; 596 };
589 let workspace_edit = to_proto::workspace_edit(&world, source_change)?; 597 let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
590 Ok(Some(workspace_edit)) 598 Ok(Some(workspace_edit))
591} 599}
592 600
593pub fn handle_references( 601pub fn handle_references(
594 world: WorldSnapshot, 602 snap: GlobalStateSnapshot,
595 params: lsp_types::ReferenceParams, 603 params: lsp_types::ReferenceParams,
596) -> Result<Option<Vec<Location>>> { 604) -> Result<Option<Vec<Location>>> {
597 let _p = profile("handle_references"); 605 let _p = profile("handle_references");
598 let position = from_proto::file_position(&world, params.text_document_position)?; 606 let position = from_proto::file_position(&snap, params.text_document_position)?;
599 607
600 let refs = match world.analysis().find_all_refs(position, None)? { 608 let refs = match snap.analysis().find_all_refs(position, None)? {
601 None => return Ok(None), 609 None => return Ok(None),
602 Some(refs) => refs, 610 Some(refs) => refs,
603 }; 611 };
604 612
605 let locations = if params.context.include_declaration { 613 let locations = if params.context.include_declaration {
606 refs.into_iter() 614 refs.into_iter()
607 .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) 615 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok())
608 .collect() 616 .collect()
609 } else { 617 } else {
610 // Only iterate over the references if include_declaration was false 618 // Only iterate over the references if include_declaration was false
611 refs.references() 619 refs.references()
612 .iter() 620 .iter()
613 .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) 621 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok())
614 .collect() 622 .collect()
615 }; 623 };
616 624
@@ -618,24 +626,24 @@ pub fn handle_references(
618} 626}
619 627
620pub fn handle_formatting( 628pub fn handle_formatting(
621 world: WorldSnapshot, 629 snap: GlobalStateSnapshot,
622 params: DocumentFormattingParams, 630 params: DocumentFormattingParams,
623) -> Result<Option<Vec<lsp_types::TextEdit>>> { 631) -> Result<Option<Vec<lsp_types::TextEdit>>> {
624 let _p = profile("handle_formatting"); 632 let _p = profile("handle_formatting");
625 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 633 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
626 let file = world.analysis().file_text(file_id)?; 634 let file = snap.analysis().file_text(file_id)?;
627 let crate_ids = world.analysis().crate_for(file_id)?; 635 let crate_ids = snap.analysis().crate_for(file_id)?;
628 636
629 let file_line_index = world.analysis().file_line_index(file_id)?; 637 let file_line_index = snap.analysis().file_line_index(file_id)?;
630 let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); 638 let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str()));
631 639
632 let mut rustfmt = match &world.config.rustfmt { 640 let mut rustfmt = match &snap.config.rustfmt {
633 RustfmtConfig::Rustfmt { extra_args } => { 641 RustfmtConfig::Rustfmt { extra_args } => {
634 let mut cmd = process::Command::new("rustfmt"); 642 let mut cmd = process::Command::new("rustfmt");
635 cmd.args(extra_args); 643 cmd.args(extra_args);
636 if let Some(&crate_id) = crate_ids.first() { 644 if let Some(&crate_id) = crate_ids.first() {
637 // Assume all crates are in the same edition 645 // Assume all crates are in the same edition
638 let edition = world.analysis().crate_edition(crate_id)?; 646 let edition = snap.analysis().crate_edition(crate_id)?;
639 cmd.arg("--edition"); 647 cmd.arg("--edition");
640 cmd.arg(edition.to_string()); 648 cmd.arg(edition.to_string());
641 } 649 }
@@ -693,136 +701,145 @@ pub fn handle_formatting(
693 }])) 701 }]))
694} 702}
695 703
696pub fn handle_code_action( 704fn handle_fixes(
697 world: WorldSnapshot, 705 snap: &GlobalStateSnapshot,
698 params: lsp_types::CodeActionParams, 706 params: &lsp_types::CodeActionParams,
699) -> Result<Option<Vec<lsp_ext::CodeAction>>> { 707 res: &mut Vec<lsp_ext::CodeAction>,
700 let _p = profile("handle_code_action"); 708) -> Result<()> {
701 // We intentionally don't support command-based actions, as those either 709 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
702 // requires custom client-code anyway, or requires server-initiated edits. 710 let line_index = snap.analysis().file_line_index(file_id)?;
703 // Server initiated edits break causality, so we avoid those as well.
704 if !world.config.client_caps.code_action_literals {
705 return Ok(None);
706 }
707
708 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
709 let line_index = world.analysis().file_line_index(file_id)?;
710 let range = from_proto::text_range(&line_index, params.range); 711 let range = from_proto::text_range(&line_index, params.range);
711 let frange = FileRange { file_id, range }; 712 let diagnostics = snap.analysis().diagnostics(file_id)?;
712
713 let diagnostics = world.analysis().diagnostics(file_id)?;
714 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
715 713
716 let fixes_from_diagnostics = diagnostics 714 let fixes_from_diagnostics = diagnostics
717 .into_iter() 715 .into_iter()
718 .filter_map(|d| Some((d.range, d.fix?))) 716 .filter_map(|d| Some((d.range, d.fix?)))
719 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) 717 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some())
720 .map(|(_range, fix)| fix); 718 .map(|(_range, fix)| fix);
721
722 for fix in fixes_from_diagnostics { 719 for fix in fixes_from_diagnostics {
723 let title = fix.label; 720 let title = fix.label;
724 let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; 721 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
725 let action = 722 let action = lsp_ext::CodeAction {
726 lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None }; 723 title,
724 id: None,
725 group: None,
726 kind: Some(lsp_types::code_action_kind::QUICKFIX.into()),
727 edit: Some(edit),
728 command: None,
729 };
727 res.push(action); 730 res.push(action);
728 } 731 }
729 732
730 for fix in world.check_fixes.get(&file_id).into_iter().flatten() { 733 for fix in snap.check_fixes.get(&file_id).into_iter().flatten() {
731 let fix_range = from_proto::text_range(&line_index, fix.range); 734 let fix_range = from_proto::text_range(&line_index, fix.range);
732 if fix_range.intersect(range).is_none() { 735 if fix_range.intersect(range).is_none() {
733 continue; 736 continue;
734 } 737 }
735 res.push(fix.action.clone()); 738 res.push(fix.action.clone());
736 } 739 }
740 Ok(())
741}
737 742
738 for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() { 743pub fn handle_code_action(
739 res.push(to_proto::code_action(&world, assist)?.into()); 744 snap: GlobalStateSnapshot,
745 params: lsp_types::CodeActionParams,
746) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
747 let _p = profile("handle_code_action");
748 // We intentionally don't support command-based actions, as those either
749 // requires custom client-code anyway, or requires server-initiated edits.
750 // Server initiated edits break causality, so we avoid those as well.
751 if !snap.config.client_caps.code_action_literals {
752 return Ok(None);
753 }
754
755 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
756 let line_index = snap.analysis().file_line_index(file_id)?;
757 let range = from_proto::text_range(&line_index, params.range);
758 let frange = FileRange { file_id, range };
759 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
760
761 handle_fixes(&snap, &params, &mut res)?;
762
763 if snap.config.client_caps.resolve_code_action {
764 for (index, assist) in
765 snap.analysis().unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate()
766 {
767 res.push(to_proto::unresolved_code_action(&snap, assist, index)?);
768 }
769 } else {
770 for assist in snap.analysis().resolved_assists(&snap.config.assist, frange)?.into_iter() {
771 res.push(to_proto::resolved_code_action(&snap, assist)?);
772 }
740 } 773 }
774
741 Ok(Some(res)) 775 Ok(Some(res))
742} 776}
743 777
778pub fn handle_resolve_code_action(
779 snap: GlobalStateSnapshot,
780 params: lsp_ext::ResolveCodeActionParams,
781) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> {
782 let _p = profile("handle_resolve_code_action");
783 let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?;
784 let line_index = snap.analysis().file_line_index(file_id)?;
785 let range = from_proto::text_range(&line_index, params.code_action_params.range);
786 let frange = FileRange { file_id, range };
787
788 let assists = snap.analysis().resolved_assists(&snap.config.assist, frange)?;
789 let (id_string, index) = split1(&params.id, ':').unwrap();
790 let index = index.parse::<usize>().unwrap();
791 let assist = &assists[index];
792 assert!(assist.assist.id.0 == id_string);
793 Ok(to_proto::resolved_code_action(&snap, assist.clone())?.edit)
794}
795
744pub fn handle_code_lens( 796pub fn handle_code_lens(
745 world: WorldSnapshot, 797 snap: GlobalStateSnapshot,
746 params: lsp_types::CodeLensParams, 798 params: lsp_types::CodeLensParams,
747) -> Result<Option<Vec<CodeLens>>> { 799) -> Result<Option<Vec<CodeLens>>> {
748 let _p = profile("handle_code_lens"); 800 let _p = profile("handle_code_lens");
749 let mut lenses: Vec<CodeLens> = Default::default(); 801 let mut lenses: Vec<CodeLens> = Default::default();
750 802
751 if world.config.lens.none() { 803 if snap.config.lens.none() {
752 // early return before any db query! 804 // early return before any db query!
753 return Ok(Some(lenses)); 805 return Ok(Some(lenses));
754 } 806 }
755 807
756 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 808 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
757 let line_index = world.analysis().file_line_index(file_id)?; 809 let line_index = snap.analysis().file_line_index(file_id)?;
758 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; 810 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
759 811
760 if world.config.lens.runnable() { 812 if snap.config.lens.runnable() {
761 // Gather runnables 813 // Gather runnables
762 for runnable in world.analysis().runnables(file_id)? { 814 for runnable in snap.analysis().runnables(file_id)? {
763 let (run_title, debugee) = match &runnable.kind { 815 if should_skip_target(&runnable, cargo_spec.as_ref()) {
764 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { 816 continue;
765 ("▶\u{fe0e} Run Test", true) 817 }
766 }
767 RunnableKind::DocTest { .. } => {
768 // cargo does not support -no-run for doctests
769 ("▶\u{fe0e} Run Doctest", false)
770 }
771 RunnableKind::Bench { .. } => {
772 // Nothing wrong with bench debugging
773 ("Run Bench", true)
774 }
775 RunnableKind::Bin => {
776 // Do not suggest binary run on other target than binary
777 match &cargo_spec {
778 Some(spec) => match spec.target_kind {
779 TargetKind::Bin => ("Run", true),
780 _ => continue,
781 },
782 None => continue,
783 }
784 }
785 };
786 818
787 let mut r = to_lsp_runnable(&world, file_id, runnable)?; 819 let action = runnable.action();
788 if world.config.lens.run { 820 let range = to_proto::range(&line_index, runnable.nav.range());
821 let r = to_proto::runnable(&snap, file_id, runnable)?;
822 if snap.config.lens.run {
789 let lens = CodeLens { 823 let lens = CodeLens {
790 range: r.range, 824 range,
791 command: Some(Command { 825 command: Some(run_single_command(&r, action.run_title)),
792 title: run_title.to_string(),
793 command: "rust-analyzer.runSingle".into(),
794 arguments: Some(vec![to_value(&r).unwrap()]),
795 }),
796 data: None, 826 data: None,
797 }; 827 };
798 lenses.push(lens); 828 lenses.push(lens);
799 } 829 }
800 830
801 if debugee && world.config.lens.debug { 831 if action.debugee && snap.config.lens.debug {
802 if r.args[0] == "run" { 832 let debug_lens =
803 r.args[0] = "build".into(); 833 CodeLens { range, command: Some(debug_single_command(&r)), data: None };
804 } else {
805 r.args.push("--no-run".into());
806 }
807 let debug_lens = CodeLens {
808 range: r.range,
809 command: Some(Command {
810 title: "Debug".into(),
811 command: "rust-analyzer.debugSingle".into(),
812 arguments: Some(vec![to_value(r).unwrap()]),
813 }),
814 data: None,
815 };
816 lenses.push(debug_lens); 834 lenses.push(debug_lens);
817 } 835 }
818 } 836 }
819 } 837 }
820 838
821 if world.config.lens.impementations { 839 if snap.config.lens.impementations {
822 // Handle impls 840 // Handle impls
823 lenses.extend( 841 lenses.extend(
824 world 842 snap.analysis()
825 .analysis()
826 .file_structure(file_id)? 843 .file_structure(file_id)?
827 .into_iter() 844 .into_iter()
828 .filter(|it| match it.kind { 845 .filter(|it| match it.kind {
@@ -857,14 +874,17 @@ enum CodeLensResolveData {
857 Impls(lsp_types::request::GotoImplementationParams), 874 Impls(lsp_types::request::GotoImplementationParams),
858} 875}
859 876
860pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { 877pub fn handle_code_lens_resolve(
878 snap: GlobalStateSnapshot,
879 code_lens: CodeLens,
880) -> Result<CodeLens> {
861 let _p = profile("handle_code_lens_resolve"); 881 let _p = profile("handle_code_lens_resolve");
862 let data = code_lens.data.unwrap(); 882 let data = code_lens.data.unwrap();
863 let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?; 883 let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?;
864 match resolve { 884 match resolve {
865 Some(CodeLensResolveData::Impls(lens_params)) => { 885 Some(CodeLensResolveData::Impls(lens_params)) => {
866 let locations: Vec<Location> = 886 let locations: Vec<Location> =
867 match handle_goto_implementation(world, lens_params.clone())? { 887 match handle_goto_implementation(snap, lens_params.clone())? {
868 Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], 888 Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc],
869 Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, 889 Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs,
870 Some(lsp_types::GotoDefinitionResponse::Link(links)) => links 890 Some(lsp_types::GotoDefinitionResponse::Link(links)) => links
@@ -874,24 +894,13 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re
874 _ => vec![], 894 _ => vec![],
875 }; 895 };
876 896
877 let title = if locations.len() == 1 { 897 let title = implementation_title(locations.len());
878 "1 implementation".into() 898 let cmd = show_references_command(
879 } else {
880 format!("{} implementations", locations.len())
881 };
882
883 // We cannot use the 'editor.action.showReferences' command directly
884 // because that command requires vscode types which we convert in the handler
885 // on the client side.
886 let cmd = Command {
887 title, 899 title,
888 command: "rust-analyzer.showReferences".into(), 900 &lens_params.text_document_position_params.text_document.uri,
889 arguments: Some(vec![ 901 code_lens.range.start,
890 to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(), 902 locations,
891 to_value(code_lens.range.start).unwrap(), 903 );
892 to_value(locations).unwrap(),
893 ]),
894 };
895 Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) 904 Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
896 } 905 }
897 None => Ok(CodeLens { 906 None => Ok(CodeLens {
@@ -903,14 +912,14 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re
903} 912}
904 913
905pub fn handle_document_highlight( 914pub fn handle_document_highlight(
906 world: WorldSnapshot, 915 snap: GlobalStateSnapshot,
907 params: lsp_types::DocumentHighlightParams, 916 params: lsp_types::DocumentHighlightParams,
908) -> Result<Option<Vec<DocumentHighlight>>> { 917) -> Result<Option<Vec<DocumentHighlight>>> {
909 let _p = profile("handle_document_highlight"); 918 let _p = profile("handle_document_highlight");
910 let position = from_proto::file_position(&world, params.text_document_position_params)?; 919 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
911 let line_index = world.analysis().file_line_index(position.file_id)?; 920 let line_index = snap.analysis().file_line_index(position.file_id)?;
912 921
913 let refs = match world 922 let refs = match snap
914 .analysis() 923 .analysis()
915 .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? 924 .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))?
916 { 925 {
@@ -930,19 +939,19 @@ pub fn handle_document_highlight(
930} 939}
931 940
932pub fn handle_ssr( 941pub fn handle_ssr(
933 world: WorldSnapshot, 942 snap: GlobalStateSnapshot,
934 params: lsp_ext::SsrParams, 943 params: lsp_ext::SsrParams,
935) -> Result<lsp_types::WorkspaceEdit> { 944) -> Result<lsp_types::WorkspaceEdit> {
936 let _p = profile("handle_ssr"); 945 let _p = profile("handle_ssr");
937 let source_change = 946 let source_change =
938 world.analysis().structural_search_replace(&params.query, params.parse_only)??; 947 snap.analysis().structural_search_replace(&params.query, params.parse_only)??;
939 to_proto::workspace_edit(&world, source_change) 948 to_proto::workspace_edit(&snap, source_change)
940} 949}
941 950
942pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 951pub fn publish_diagnostics(snap: &GlobalStateSnapshot, file_id: FileId) -> Result<DiagnosticTask> {
943 let _p = profile("publish_diagnostics"); 952 let _p = profile("publish_diagnostics");
944 let line_index = world.analysis().file_line_index(file_id)?; 953 let line_index = snap.analysis().file_line_index(file_id)?;
945 let diagnostics: Vec<Diagnostic> = world 954 let diagnostics: Vec<Diagnostic> = snap
946 .analysis() 955 .analysis()
947 .diagnostics(file_id)? 956 .diagnostics(file_id)?
948 .into_iter() 957 .into_iter()
@@ -959,87 +968,29 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
959 Ok(DiagnosticTask::SetNative(file_id, diagnostics)) 968 Ok(DiagnosticTask::SetNative(file_id, diagnostics))
960} 969}
961 970
962fn to_lsp_runnable(
963 world: &WorldSnapshot,
964 file_id: FileId,
965 runnable: Runnable,
966) -> Result<lsp_ext::Runnable> {
967 let spec = CargoTargetSpec::for_file(world, file_id)?;
968 let target = spec.as_ref().map(|s| s.target.clone());
969 let mut features_needed = vec![];
970 for cfg_expr in &runnable.cfg_exprs {
971 collect_minimal_features_needed(cfg_expr, &mut features_needed);
972 }
973 let (args, extra_args) =
974 CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
975 let line_index = world.analysis().file_line_index(file_id)?;
976 let label = match &runnable.kind {
977 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
978 RunnableKind::TestMod { path } => format!("test-mod {}", path),
979 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
980 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
981 RunnableKind::Bin => {
982 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
983 }
984 };
985 Ok(lsp_ext::Runnable {
986 range: to_proto::range(&line_index, runnable.range),
987 label,
988 bin: "cargo".to_string(),
989 args,
990 extra_args,
991 env: {
992 let mut m = FxHashMap::default();
993 m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
994 m
995 },
996 cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()),
997 })
998}
999
1000/// Fill minimal features needed
1001fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) {
1002 match cfg_expr {
1003 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
1004 CfgExpr::All(preds) => {
1005 preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
1006 }
1007 CfgExpr::Any(preds) => {
1008 for cfg in preds {
1009 let len_features = features.len();
1010 collect_minimal_features_needed(cfg, features);
1011 if len_features != features.len() {
1012 break;
1013 }
1014 }
1015 }
1016 _ => {}
1017 }
1018}
1019
1020pub fn handle_inlay_hints( 971pub fn handle_inlay_hints(
1021 world: WorldSnapshot, 972 snap: GlobalStateSnapshot,
1022 params: InlayHintsParams, 973 params: InlayHintsParams,
1023) -> Result<Vec<InlayHint>> { 974) -> Result<Vec<InlayHint>> {
1024 let _p = profile("handle_inlay_hints"); 975 let _p = profile("handle_inlay_hints");
1025 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 976 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1026 let analysis = world.analysis(); 977 let analysis = snap.analysis();
1027 let line_index = analysis.file_line_index(file_id)?; 978 let line_index = analysis.file_line_index(file_id)?;
1028 Ok(analysis 979 Ok(analysis
1029 .inlay_hints(file_id, &world.config.inlay_hints)? 980 .inlay_hints(file_id, &snap.config.inlay_hints)?
1030 .into_iter() 981 .into_iter()
1031 .map(|it| to_proto::inlay_int(&line_index, it)) 982 .map(|it| to_proto::inlay_int(&line_index, it))
1032 .collect()) 983 .collect())
1033} 984}
1034 985
1035pub fn handle_call_hierarchy_prepare( 986pub fn handle_call_hierarchy_prepare(
1036 world: WorldSnapshot, 987 snap: GlobalStateSnapshot,
1037 params: CallHierarchyPrepareParams, 988 params: CallHierarchyPrepareParams,
1038) -> Result<Option<Vec<CallHierarchyItem>>> { 989) -> Result<Option<Vec<CallHierarchyItem>>> {
1039 let _p = profile("handle_call_hierarchy_prepare"); 990 let _p = profile("handle_call_hierarchy_prepare");
1040 let position = from_proto::file_position(&world, params.text_document_position_params)?; 991 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
1041 992
1042 let nav_info = match world.analysis().call_hierarchy(position)? { 993 let nav_info = match snap.analysis().call_hierarchy(position)? {
1043 None => return Ok(None), 994 None => return Ok(None),
1044 Some(it) => it, 995 Some(it) => it,
1045 }; 996 };
@@ -1048,24 +999,24 @@ pub fn handle_call_hierarchy_prepare(
1048 let res = navs 999 let res = navs
1049 .into_iter() 1000 .into_iter()
1050 .filter(|it| it.kind() == SyntaxKind::FN_DEF) 1001 .filter(|it| it.kind() == SyntaxKind::FN_DEF)
1051 .map(|it| to_proto::call_hierarchy_item(&world, it)) 1002 .map(|it| to_proto::call_hierarchy_item(&snap, it))
1052 .collect::<Result<Vec<_>>>()?; 1003 .collect::<Result<Vec<_>>>()?;
1053 1004
1054 Ok(Some(res)) 1005 Ok(Some(res))
1055} 1006}
1056 1007
1057pub fn handle_call_hierarchy_incoming( 1008pub fn handle_call_hierarchy_incoming(
1058 world: WorldSnapshot, 1009 snap: GlobalStateSnapshot,
1059 params: CallHierarchyIncomingCallsParams, 1010 params: CallHierarchyIncomingCallsParams,
1060) -> Result<Option<Vec<CallHierarchyIncomingCall>>> { 1011) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
1061 let _p = profile("handle_call_hierarchy_incoming"); 1012 let _p = profile("handle_call_hierarchy_incoming");
1062 let item = params.item; 1013 let item = params.item;
1063 1014
1064 let doc = TextDocumentIdentifier::new(item.uri); 1015 let doc = TextDocumentIdentifier::new(item.uri);
1065 let frange = from_proto::file_range(&world, doc, item.range)?; 1016 let frange = from_proto::file_range(&snap, doc, item.range)?;
1066 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 1017 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1067 1018
1068 let call_items = match world.analysis().incoming_calls(fpos)? { 1019 let call_items = match snap.analysis().incoming_calls(fpos)? {
1069 None => return Ok(None), 1020 None => return Ok(None),
1070 Some(it) => it, 1021 Some(it) => it,
1071 }; 1022 };
@@ -1074,8 +1025,8 @@ pub fn handle_call_hierarchy_incoming(
1074 1025
1075 for call_item in call_items.into_iter() { 1026 for call_item in call_items.into_iter() {
1076 let file_id = call_item.target.file_id(); 1027 let file_id = call_item.target.file_id();
1077 let line_index = world.analysis().file_line_index(file_id)?; 1028 let line_index = snap.analysis().file_line_index(file_id)?;
1078 let item = to_proto::call_hierarchy_item(&world, call_item.target)?; 1029 let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1079 res.push(CallHierarchyIncomingCall { 1030 res.push(CallHierarchyIncomingCall {
1080 from: item, 1031 from: item,
1081 from_ranges: call_item 1032 from_ranges: call_item
@@ -1090,17 +1041,17 @@ pub fn handle_call_hierarchy_incoming(
1090} 1041}
1091 1042
1092pub fn handle_call_hierarchy_outgoing( 1043pub fn handle_call_hierarchy_outgoing(
1093 world: WorldSnapshot, 1044 snap: GlobalStateSnapshot,
1094 params: CallHierarchyOutgoingCallsParams, 1045 params: CallHierarchyOutgoingCallsParams,
1095) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> { 1046) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
1096 let _p = profile("handle_call_hierarchy_outgoing"); 1047 let _p = profile("handle_call_hierarchy_outgoing");
1097 let item = params.item; 1048 let item = params.item;
1098 1049
1099 let doc = TextDocumentIdentifier::new(item.uri); 1050 let doc = TextDocumentIdentifier::new(item.uri);
1100 let frange = from_proto::file_range(&world, doc, item.range)?; 1051 let frange = from_proto::file_range(&snap, doc, item.range)?;
1101 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 1052 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1102 1053
1103 let call_items = match world.analysis().outgoing_calls(fpos)? { 1054 let call_items = match snap.analysis().outgoing_calls(fpos)? {
1104 None => return Ok(None), 1055 None => return Ok(None),
1105 Some(it) => it, 1056 Some(it) => it,
1106 }; 1057 };
@@ -1109,8 +1060,8 @@ pub fn handle_call_hierarchy_outgoing(
1109 1060
1110 for call_item in call_items.into_iter() { 1061 for call_item in call_items.into_iter() {
1111 let file_id = call_item.target.file_id(); 1062 let file_id = call_item.target.file_id();
1112 let line_index = world.analysis().file_line_index(file_id)?; 1063 let line_index = snap.analysis().file_line_index(file_id)?;
1113 let item = to_proto::call_hierarchy_item(&world, call_item.target)?; 1064 let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1114 res.push(CallHierarchyOutgoingCall { 1065 res.push(CallHierarchyOutgoingCall {
1115 to: item, 1066 to: item,
1116 from_ranges: call_item 1067 from_ranges: call_item
@@ -1125,82 +1076,165 @@ pub fn handle_call_hierarchy_outgoing(
1125} 1076}
1126 1077
1127pub fn handle_semantic_tokens( 1078pub fn handle_semantic_tokens(
1128 world: WorldSnapshot, 1079 snap: GlobalStateSnapshot,
1129 params: SemanticTokensParams, 1080 params: SemanticTokensParams,
1130) -> Result<Option<SemanticTokensResult>> { 1081) -> Result<Option<SemanticTokensResult>> {
1131 let _p = profile("handle_semantic_tokens"); 1082 let _p = profile("handle_semantic_tokens");
1132 1083
1133 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 1084 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1134 let text = world.analysis().file_text(file_id)?; 1085 let text = snap.analysis().file_text(file_id)?;
1135 let line_index = world.analysis().file_line_index(file_id)?; 1086 let line_index = snap.analysis().file_line_index(file_id)?;
1136 1087
1137 let highlights = world.analysis().highlight(file_id)?; 1088 let highlights = snap.analysis().highlight(file_id)?;
1138 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1089 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1139 Ok(Some(semantic_tokens.into())) 1090 Ok(Some(semantic_tokens.into()))
1140} 1091}
1141 1092
1142pub fn handle_semantic_tokens_range( 1093pub fn handle_semantic_tokens_range(
1143 world: WorldSnapshot, 1094 snap: GlobalStateSnapshot,
1144 params: SemanticTokensRangeParams, 1095 params: SemanticTokensRangeParams,
1145) -> Result<Option<SemanticTokensRangeResult>> { 1096) -> Result<Option<SemanticTokensRangeResult>> {
1146 let _p = profile("handle_semantic_tokens_range"); 1097 let _p = profile("handle_semantic_tokens_range");
1147 1098
1148 let frange = from_proto::file_range(&world, params.text_document, params.range)?; 1099 let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
1149 let text = world.analysis().file_text(frange.file_id)?; 1100 let text = snap.analysis().file_text(frange.file_id)?;
1150 let line_index = world.analysis().file_line_index(frange.file_id)?; 1101 let line_index = snap.analysis().file_line_index(frange.file_id)?;
1151 1102
1152 let highlights = world.analysis().highlight_range(frange)?; 1103 let highlights = snap.analysis().highlight_range(frange)?;
1153 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1104 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1154 Ok(Some(semantic_tokens.into())) 1105 Ok(Some(semantic_tokens.into()))
1155} 1106}
1156 1107
1157#[cfg(test)] 1108fn implementation_title(count: usize) -> String {
1158mod tests { 1109 if count == 1 {
1159 use super::*; 1110 "1 implementation".into()
1111 } else {
1112 format!("{} implementations", count)
1113 }
1114}
1160 1115
1161 use mbe::{ast_to_token_tree, TokenMap}; 1116fn show_references_command(
1162 use ra_cfg::parse_cfg; 1117 title: String,
1163 use ra_syntax::{ 1118 uri: &lsp_types::Url,
1164 ast::{self, AstNode}, 1119 position: lsp_types::Position,
1165 SmolStr, 1120 locations: Vec<lsp_types::Location>,
1166 }; 1121) -> Command {
1122 // We cannot use the 'editor.action.showReferences' command directly
1123 // because that command requires vscode types which we convert in the handler
1124 // on the client side.
1125
1126 Command {
1127 title,
1128 command: "rust-analyzer.showReferences".into(),
1129 arguments: Some(vec![
1130 to_value(uri).unwrap(),
1131 to_value(position).unwrap(),
1132 to_value(locations).unwrap(),
1133 ]),
1134 }
1135}
1167 1136
1168 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { 1137fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command {
1169 let source_file = ast::SourceFile::parse(input).ok().unwrap(); 1138 Command {
1170 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); 1139 title: title.to_string(),
1171 ast_to_token_tree(&tt).unwrap() 1140 command: "rust-analyzer.runSingle".into(),
1141 arguments: Some(vec![to_value(runnable).unwrap()]),
1172 } 1142 }
1143}
1173 1144
1174 #[test] 1145fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command {
1175 fn test_cfg_expr_minimal_features_needed() { 1146 Command {
1176 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); 1147 title: "Debug".into(),
1177 let cfg_expr = parse_cfg(&subtree); 1148 command: "rust-analyzer.debugSingle".into(),
1178 let mut min_features = vec![]; 1149 arguments: Some(vec![to_value(runnable).unwrap()]),
1179 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1150 }
1151}
1180 1152
1181 assert_eq!(min_features, vec![SmolStr::new("baz")]); 1153fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink {
1154 lsp_ext::CommandLink { tooltip: Some(tooltip), command }
1155}
1182 1156
1183 let (subtree, _) = 1157fn show_impl_command_link(
1184 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); 1158 snap: &GlobalStateSnapshot,
1185 let cfg_expr = parse_cfg(&subtree); 1159 position: &FilePosition,
1160) -> Option<lsp_ext::CommandLinkGroup> {
1161 if snap.config.hover.implementations {
1162 if let Some(nav_data) = snap.analysis().goto_implementation(*position).unwrap_or(None) {
1163 let uri = to_proto::url(snap, position.file_id).ok()?;
1164 let line_index = snap.analysis().file_line_index(position.file_id).ok()?;
1165 let position = to_proto::position(&line_index, position.offset);
1166 let locations: Vec<_> = nav_data
1167 .info
1168 .iter()
1169 .filter_map(|it| to_proto::location(snap, it.file_range()).ok())
1170 .collect();
1171 let title = implementation_title(locations.len());
1172 let command = show_references_command(title, &uri, position, locations);
1173
1174 return Some(lsp_ext::CommandLinkGroup {
1175 commands: vec![to_command_link(command, "Go to implementations".into())],
1176 ..Default::default()
1177 });
1178 }
1179 }
1180 None
1181}
1186 1182
1187 let mut min_features = vec![]; 1183fn to_runnable_action(
1188 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1184 snap: &GlobalStateSnapshot,
1189 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]); 1185 file_id: FileId,
1186 runnable: Runnable,
1187) -> Option<lsp_ext::CommandLinkGroup> {
1188 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?;
1189 if should_skip_target(&runnable, cargo_spec.as_ref()) {
1190 return None;
1191 }
1190 1192
1191 let (subtree, _) = 1193 let action: &'static _ = runnable.action();
1192 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); 1194 to_proto::runnable(snap, file_id, runnable).ok().map(|r| {
1193 let cfg_expr = parse_cfg(&subtree); 1195 let mut group = lsp_ext::CommandLinkGroup::default();
1194 1196
1195 let mut min_features = vec![]; 1197 if snap.config.hover.run {
1196 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1198 let run_command = run_single_command(&r, action.run_title);
1197 assert_eq!(min_features, vec![SmolStr::new("baz")]); 1199 group.commands.push(to_command_link(run_command, r.label.clone()));
1200 }
1198 1201
1199 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); 1202 if snap.config.hover.debug {
1200 let cfg_expr = parse_cfg(&subtree); 1203 let dbg_command = debug_single_command(&r);
1204 group.commands.push(to_command_link(dbg_command, r.label));
1205 }
1206
1207 group
1208 })
1209}
1210
1211fn prepare_hover_actions(
1212 snap: &GlobalStateSnapshot,
1213 file_id: FileId,
1214 actions: &[HoverAction],
1215) -> Vec<lsp_ext::CommandLinkGroup> {
1216 if snap.config.hover.none() || !snap.config.client_caps.hover_actions {
1217 return Vec::new();
1218 }
1201 1219
1202 let mut min_features = vec![]; 1220 actions
1203 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1221 .iter()
1204 assert!(min_features.is_empty()); 1222 .filter_map(|it| match it {
1223 HoverAction::Implementaion(position) => show_impl_command_link(snap, position),
1224 HoverAction::Runnable(r) => to_runnable_action(snap, file_id, r.clone()),
1225 })
1226 .collect()
1227}
1228
1229fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool {
1230 match runnable.kind {
1231 RunnableKind::Bin => {
1232 // Do not suggest binary run on other target than binary
1233 match &cargo_spec {
1234 Some(spec) => spec.target_kind != TargetKind::Bin,
1235 None => true,
1236 }
1237 }
1238 _ => false,
1205 } 1239 }
1206} 1240}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2fbbb4e63..710df1fbd 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -3,13 +3,16 @@ use ra_db::{FileId, FileRange};
3use ra_ide::{ 3use ra_ide::{
4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, 4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, 5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel,
6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, 6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess,
7 SourceChange, SourceFileEdit, TextEdit, 7 ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit,
8}; 8};
9use ra_syntax::{SyntaxKind, TextRange, TextSize}; 9use ra_syntax::{SyntaxKind, TextRange, TextSize};
10use ra_vfs::LineEndings; 10use ra_vfs::LineEndings;
11 11
12use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; 12use crate::{
13 cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot, lsp_ext,
14 semantic_tokens, Result,
15};
13 16
14pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { 17pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
15 let line_col = line_index.line_col(offset); 18 let line_col = line_index.line_col(offset);
@@ -382,41 +385,44 @@ pub(crate) fn folding_range(
382 } 385 }
383} 386}
384 387
385pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::Url> { 388pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> Result<lsp_types::Url> {
386 world.file_id_to_uri(file_id) 389 snap.file_id_to_uri(file_id)
387} 390}
388 391
389pub(crate) fn versioned_text_document_identifier( 392pub(crate) fn versioned_text_document_identifier(
390 world: &WorldSnapshot, 393 snap: &GlobalStateSnapshot,
391 file_id: FileId, 394 file_id: FileId,
392 version: Option<i64>, 395 version: Option<i64>,
393) -> Result<lsp_types::VersionedTextDocumentIdentifier> { 396) -> Result<lsp_types::VersionedTextDocumentIdentifier> {
394 let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(world, file_id)?, version }; 397 let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(snap, file_id)?, version };
395 Ok(res) 398 Ok(res)
396} 399}
397 400
398pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_types::Location> { 401pub(crate) fn location(
399 let url = url(world, frange.file_id)?; 402 snap: &GlobalStateSnapshot,
400 let line_index = world.analysis().file_line_index(frange.file_id)?; 403 frange: FileRange,
404) -> Result<lsp_types::Location> {
405 let url = url(snap, frange.file_id)?;
406 let line_index = snap.analysis().file_line_index(frange.file_id)?;
401 let range = range(&line_index, frange.range); 407 let range = range(&line_index, frange.range);
402 let loc = lsp_types::Location::new(url, range); 408 let loc = lsp_types::Location::new(url, range);
403 Ok(loc) 409 Ok(loc)
404} 410}
405 411
406pub(crate) fn location_link( 412pub(crate) fn location_link(
407 world: &WorldSnapshot, 413 snap: &GlobalStateSnapshot,
408 src: Option<FileRange>, 414 src: Option<FileRange>,
409 target: NavigationTarget, 415 target: NavigationTarget,
410) -> Result<lsp_types::LocationLink> { 416) -> Result<lsp_types::LocationLink> {
411 let origin_selection_range = match src { 417 let origin_selection_range = match src {
412 Some(src) => { 418 Some(src) => {
413 let line_index = world.analysis().file_line_index(src.file_id)?; 419 let line_index = snap.analysis().file_line_index(src.file_id)?;
414 let range = range(&line_index, src.range); 420 let range = range(&line_index, src.range);
415 Some(range) 421 Some(range)
416 } 422 }
417 None => None, 423 None => None,
418 }; 424 };
419 let (target_uri, target_range, target_selection_range) = location_info(world, target)?; 425 let (target_uri, target_range, target_selection_range) = location_info(snap, target)?;
420 let res = lsp_types::LocationLink { 426 let res = lsp_types::LocationLink {
421 origin_selection_range, 427 origin_selection_range,
422 target_uri, 428 target_uri,
@@ -427,12 +433,12 @@ pub(crate) fn location_link(
427} 433}
428 434
429fn location_info( 435fn location_info(
430 world: &WorldSnapshot, 436 snap: &GlobalStateSnapshot,
431 target: NavigationTarget, 437 target: NavigationTarget,
432) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { 438) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> {
433 let line_index = world.analysis().file_line_index(target.file_id())?; 439 let line_index = snap.analysis().file_line_index(target.file_id())?;
434 440
435 let target_uri = url(world, target.file_id())?; 441 let target_uri = url(snap, target.file_id())?;
436 let target_range = range(&line_index, target.full_range()); 442 let target_range = range(&line_index, target.full_range());
437 let target_selection_range = 443 let target_selection_range =
438 target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); 444 target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range);
@@ -440,14 +446,14 @@ fn location_info(
440} 446}
441 447
442pub(crate) fn goto_definition_response( 448pub(crate) fn goto_definition_response(
443 world: &WorldSnapshot, 449 snap: &GlobalStateSnapshot,
444 src: Option<FileRange>, 450 src: Option<FileRange>,
445 targets: Vec<NavigationTarget>, 451 targets: Vec<NavigationTarget>,
446) -> Result<lsp_types::GotoDefinitionResponse> { 452) -> Result<lsp_types::GotoDefinitionResponse> {
447 if world.config.client_caps.location_link { 453 if snap.config.client_caps.location_link {
448 let links = targets 454 let links = targets
449 .into_iter() 455 .into_iter()
450 .map(|nav| location_link(world, src, nav)) 456 .map(|nav| location_link(snap, src, nav))
451 .collect::<Result<Vec<_>>>()?; 457 .collect::<Result<Vec<_>>>()?;
452 Ok(links.into()) 458 Ok(links.into())
453 } else { 459 } else {
@@ -455,7 +461,7 @@ pub(crate) fn goto_definition_response(
455 .into_iter() 461 .into_iter()
456 .map(|nav| { 462 .map(|nav| {
457 location( 463 location(
458 world, 464 snap,
459 FileRange { 465 FileRange {
460 file_id: nav.file_id(), 466 file_id: nav.file_id(),
461 range: nav.focus_range().unwrap_or(nav.range()), 467 range: nav.focus_range().unwrap_or(nav.range()),
@@ -468,13 +474,13 @@ pub(crate) fn goto_definition_response(
468} 474}
469 475
470pub(crate) fn snippet_text_document_edit( 476pub(crate) fn snippet_text_document_edit(
471 world: &WorldSnapshot, 477 snap: &GlobalStateSnapshot,
472 is_snippet: bool, 478 is_snippet: bool,
473 source_file_edit: SourceFileEdit, 479 source_file_edit: SourceFileEdit,
474) -> Result<lsp_ext::SnippetTextDocumentEdit> { 480) -> Result<lsp_ext::SnippetTextDocumentEdit> {
475 let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; 481 let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id, None)?;
476 let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; 482 let line_index = snap.analysis().file_line_index(source_file_edit.file_id)?;
477 let line_endings = world.file_line_endings(source_file_edit.file_id); 483 let line_endings = snap.file_line_endings(source_file_edit.file_id);
478 let edits = source_file_edit 484 let edits = source_file_edit
479 .edit 485 .edit
480 .into_iter() 486 .into_iter()
@@ -484,17 +490,17 @@ pub(crate) fn snippet_text_document_edit(
484} 490}
485 491
486pub(crate) fn resource_op( 492pub(crate) fn resource_op(
487 world: &WorldSnapshot, 493 snap: &GlobalStateSnapshot,
488 file_system_edit: FileSystemEdit, 494 file_system_edit: FileSystemEdit,
489) -> Result<lsp_types::ResourceOp> { 495) -> Result<lsp_types::ResourceOp> {
490 let res = match file_system_edit { 496 let res = match file_system_edit {
491 FileSystemEdit::CreateFile { source_root, path } => { 497 FileSystemEdit::CreateFile { source_root, path } => {
492 let uri = world.path_to_uri(source_root, &path)?; 498 let uri = snap.path_to_uri(source_root, &path)?;
493 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) 499 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None })
494 } 500 }
495 FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { 501 FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => {
496 let old_uri = world.file_id_to_uri(src)?; 502 let old_uri = snap.file_id_to_uri(src)?;
497 let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; 503 let new_uri = snap.path_to_uri(dst_source_root, &dst_path)?;
498 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) 504 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None })
499 } 505 }
500 }; 506 };
@@ -502,16 +508,16 @@ pub(crate) fn resource_op(
502} 508}
503 509
504pub(crate) fn snippet_workspace_edit( 510pub(crate) fn snippet_workspace_edit(
505 world: &WorldSnapshot, 511 snap: &GlobalStateSnapshot,
506 source_change: SourceChange, 512 source_change: SourceChange,
507) -> Result<lsp_ext::SnippetWorkspaceEdit> { 513) -> Result<lsp_ext::SnippetWorkspaceEdit> {
508 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); 514 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
509 for op in source_change.file_system_edits { 515 for op in source_change.file_system_edits {
510 let op = resource_op(&world, op)?; 516 let op = resource_op(&snap, op)?;
511 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); 517 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op));
512 } 518 }
513 for edit in source_change.source_file_edits { 519 for edit in source_change.source_file_edits {
514 let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?; 520 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?;
515 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); 521 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
516 } 522 }
517 let workspace_edit = 523 let workspace_edit =
@@ -520,11 +526,11 @@ pub(crate) fn snippet_workspace_edit(
520} 526}
521 527
522pub(crate) fn workspace_edit( 528pub(crate) fn workspace_edit(
523 world: &WorldSnapshot, 529 snap: &GlobalStateSnapshot,
524 source_change: SourceChange, 530 source_change: SourceChange,
525) -> Result<lsp_types::WorkspaceEdit> { 531) -> Result<lsp_types::WorkspaceEdit> {
526 assert!(!source_change.is_snippet); 532 assert!(!source_change.is_snippet);
527 snippet_workspace_edit(world, source_change).map(|it| it.into()) 533 snippet_workspace_edit(snap, source_change).map(|it| it.into())
528} 534}
529 535
530impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { 536impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
@@ -563,13 +569,13 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
563} 569}
564 570
565pub fn call_hierarchy_item( 571pub fn call_hierarchy_item(
566 world: &WorldSnapshot, 572 snap: &GlobalStateSnapshot,
567 target: NavigationTarget, 573 target: NavigationTarget,
568) -> Result<lsp_types::CallHierarchyItem> { 574) -> Result<lsp_types::CallHierarchyItem> {
569 let name = target.name().to_string(); 575 let name = target.name().to_string();
570 let detail = target.description().map(|it| it.to_string()); 576 let detail = target.description().map(|it| it.to_string());
571 let kind = symbol_kind(target.kind()); 577 let kind = symbol_kind(target.kind());
572 let (uri, range, selection_range) = location_info(world, target)?; 578 let (uri, range, selection_range) = location_info(snap, target)?;
573 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) 579 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range })
574} 580}
575 581
@@ -617,13 +623,56 @@ fn main() <fold>{
617 } 623 }
618} 624}
619 625
620pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { 626pub(crate) fn unresolved_code_action(
627 snap: &GlobalStateSnapshot,
628 assist: Assist,
629 index: usize,
630) -> Result<lsp_ext::CodeAction> {
621 let res = lsp_ext::CodeAction { 631 let res = lsp_ext::CodeAction {
622 title: assist.label, 632 title: assist.label,
623 group: if world.config.client_caps.code_action_group { assist.group_label } else { None }, 633 id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())),
634 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
624 kind: Some(String::new()), 635 kind: Some(String::new()),
625 edit: Some(snippet_workspace_edit(world, assist.source_change)?), 636 edit: None,
626 command: None, 637 command: None,
627 }; 638 };
628 Ok(res) 639 Ok(res)
629} 640}
641
642pub(crate) fn resolved_code_action(
643 snap: &GlobalStateSnapshot,
644 assist: ResolvedAssist,
645) -> Result<lsp_ext::CodeAction> {
646 let change = assist.source_change;
647 unresolved_code_action(snap, assist.assist, 0).and_then(|it| {
648 Ok(lsp_ext::CodeAction {
649 id: None,
650 edit: Some(snippet_workspace_edit(snap, change)?),
651 ..it
652 })
653 })
654}
655
656pub(crate) fn runnable(
657 snap: &GlobalStateSnapshot,
658 file_id: FileId,
659 runnable: Runnable,
660) -> Result<lsp_ext::Runnable> {
661 let spec = CargoTargetSpec::for_file(snap, file_id)?;
662 let target = spec.as_ref().map(|s| s.target.clone());
663 let (cargo_args, executable_args) =
664 CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?;
665 let label = runnable.label(target);
666 let location = location_link(snap, None, runnable.nav)?;
667
668 Ok(lsp_ext::Runnable {
669 label,
670 location: Some(location),
671 kind: lsp_ext::RunnableKind::Cargo,
672 args: lsp_ext::CargoRunnable {
673 workspace_root: snap.workspace_root_for(file_id).map(|root| root.to_owned()),
674 cargo_args,
675 executable_args,
676 },
677 })
678}
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 405ddb362..0e2a83c6a 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -4,9 +4,7 @@ use std::{collections::HashMap, path::PathBuf, time::Instant};
4 4
5use lsp_types::{ 5use lsp_types::{
6 notification::DidOpenTextDocument, 6 notification::DidOpenTextDocument,
7 request::{ 7 request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest},
8 CodeActionRequest, Completion, Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest,
9 },
10 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, 8 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
11 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, 9 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams,
12 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, 10 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
@@ -59,52 +57,6 @@ use std::collections::Spam;
59} 57}
60 58
61#[test] 59#[test]
62fn test_runnables_no_project() {
63 if skip_slow_tests() {
64 return;
65 }
66
67 let server = project(
68 r"
69//- lib.rs
70#[test]
71fn foo() {
72}
73",
74 );
75 server.wait_until_workspace_is_loaded();
76 server.request::<Runnables>(
77 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
78 json!([
79 {
80 "args": [ "test" ],
81 "extraArgs": [ "foo", "--nocapture" ],
82 "bin": "cargo",
83 "env": { "RUST_BACKTRACE": "short" },
84 "cwd": null,
85 "label": "test foo",
86 "range": {
87 "end": { "character": 1, "line": 2 },
88 "start": { "character": 0, "line": 0 }
89 }
90 },
91 {
92 "args": ["check", "--workspace"],
93 "extraArgs": [],
94 "bin": "cargo",
95 "env": {},
96 "cwd": null,
97 "label": "cargo check --workspace",
98 "range": {
99 "end": { "character": 0, "line": 0 },
100 "start": { "character": 0, "line": 0 }
101 }
102 }
103 ]),
104 );
105}
106
107#[test]
108fn test_runnables_project() { 60fn test_runnables_project() {
109 if skip_slow_tests() { 61 if skip_slow_tests() {
110 return; 62 return;
@@ -138,42 +90,44 @@ fn main() {}
138 server.request::<Runnables>( 90 server.request::<Runnables>(
139 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, 91 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
140 json!([ 92 json!([
141 { 93 {
142 "args": [ "test", "--package", "foo", "--test", "spam" ], 94 "args": {
143 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], 95 "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
144 "bin": "cargo", 96 "executableArgs": ["test_eggs", "--exact", "--nocapture"],
145 "env": { "RUST_BACKTRACE": "short" }, 97 "workspaceRoot": server.path().join("foo")
146 "label": "test test_eggs", 98 },
147 "range": { 99 "kind": "cargo",
100 "label": "test test_eggs",
101 "location": {
102 "targetRange": {
148 "end": { "character": 17, "line": 1 }, 103 "end": { "character": 17, "line": 1 },
149 "start": { "character": 0, "line": 0 } 104 "start": { "character": 0, "line": 0 }
150 }, 105 },
151 "cwd": server.path().join("foo") 106 "targetSelectionRange": {
152 }, 107 "end": { "character": 12, "line": 1 },
153 { 108 "start": { "character": 3, "line": 1 }
154 "args": [ "check", "--package", "foo" ],
155 "extraArgs": [],
156 "bin": "cargo",
157 "env": {},
158 "label": "cargo check -p foo",
159 "range": {
160 "end": { "character": 0, "line": 0 },
161 "start": { "character": 0, "line": 0 }
162 }, 109 },
163 "cwd": server.path().join("foo") 110 "targetUri": "file:///[..]/tests/spam.rs"
164 },
165 {
166 "args": [ "test", "--package", "foo" ],
167 "extraArgs": [],
168 "bin": "cargo",
169 "env": {},
170 "label": "cargo test -p foo",
171 "range": {
172 "end": { "character": 0, "line": 0 },
173 "start": { "character": 0, "line": 0 }
174 },
175 "cwd": server.path().join("foo")
176 } 111 }
112 },
113 {
114 "args": {
115 "cargoArgs": ["check", "--package", "foo"],
116 "executableArgs": [],
117 "workspaceRoot": server.path().join("foo")
118 },
119 "kind": "cargo",
120 "label": "cargo check -p foo"
121 },
122 {
123 "args": {
124 "cargoArgs": ["test", "--package", "foo"],
125 "executableArgs": [],
126 "workspaceRoot": server.path().join("foo")
127 },
128 "kind": "cargo",
129 "label": "cargo test -p foo"
130 }
177 ]), 131 ]),
178 ); 132 );
179} 133}
@@ -342,6 +296,7 @@ fn main() {}
342 } 296 }
343 ] 297 ]
344 }, 298 },
299 "kind": "quickfix",
345 "title": "Create module" 300 "title": "Create module"
346 }]), 301 }]),
347 ); 302 );
@@ -374,8 +329,7 @@ fn test_missing_module_code_action_in_json_project() {
374 "root_module": path.join("src/lib.rs"), 329 "root_module": path.join("src/lib.rs"),
375 "deps": [], 330 "deps": [],
376 "edition": "2015", 331 "edition": "2015",
377 "atom_cfgs": [], 332 "cfg": [ "cfg_atom_1", "feature=cfg_1"],
378 "key_value_cfgs": {}
379 } ] 333 } ]
380 }); 334 });
381 335
@@ -413,6 +367,7 @@ fn main() {{}}
413 } 367 }
414 ] 368 ]
415 }, 369 },
370 "kind": "quickfix",
416 "title": "Create module" 371 "title": "Create module"
417 }]), 372 }]),
418 ); 373 );
@@ -550,6 +505,10 @@ fn main() {
550 println!("cargo:rerun-if-changed=build.rs"); 505 println!("cargo:rerun-if-changed=build.rs");
551} 506}
552//- src/main.rs 507//- src/main.rs
508#[rustc_builtin_macro] macro_rules! include {}
509#[rustc_builtin_macro] macro_rules! concat {}
510#[rustc_builtin_macro] macro_rules! env {}
511
553include!(concat!(env!("OUT_DIR"), "/hello.rs")); 512include!(concat!(env!("OUT_DIR"), "/hello.rs"));
554 513
555#[cfg(atom_cfg)] 514#[cfg(atom_cfg)]
@@ -564,10 +523,8 @@ struct B;
564fn main() { 523fn main() {
565 let va = A; 524 let va = A;
566 let vb = B; 525 let vb = B;
567 message(); 526 let should_be_str = message();
568} 527}
569
570fn main() { message(); }
571"###, 528"###,
572 ) 529 )
573 .with_config(|config| { 530 .with_config(|config| {
@@ -575,54 +532,35 @@ fn main() { message(); }
575 }) 532 })
576 .server(); 533 .server();
577 server.wait_until_workspace_is_loaded(); 534 server.wait_until_workspace_is_loaded();
578 let res = server.send_request::<GotoDefinition>(GotoDefinitionParams { 535 let res = server.send_request::<HoverRequest>(HoverParams {
579 text_document_position_params: TextDocumentPositionParams::new( 536 text_document_position_params: TextDocumentPositionParams::new(
580 server.doc_id("src/main.rs"), 537 server.doc_id("src/main.rs"),
581 Position::new(14, 8), 538 Position::new(18, 10),
582 ), 539 ),
583 work_done_progress_params: Default::default(), 540 work_done_progress_params: Default::default(),
584 partial_result_params: Default::default(),
585 }); 541 });
586 assert!(format!("{}", res).contains("hello.rs")); 542 assert!(res.to_string().contains("&str"));
587 server.request::<GotoTypeDefinition>( 543 server.request::<GotoTypeDefinition>(
588 GotoDefinitionParams { 544 GotoDefinitionParams {
589 text_document_position_params: TextDocumentPositionParams::new( 545 text_document_position_params: TextDocumentPositionParams::new(
590 server.doc_id("src/main.rs"), 546 server.doc_id("src/main.rs"),
591 Position::new(12, 9), 547 Position::new(16, 9),
592 ), 548 ),
593 work_done_progress_params: Default::default(), 549 work_done_progress_params: Default::default(),
594 partial_result_params: Default::default(), 550 partial_result_params: Default::default(),
595 }, 551 },
596 json!([{ 552 json!([{
597 "originSelectionRange": { 553 "originSelectionRange": {
598 "end": { 554 "end": { "character": 10, "line": 16 },
599 "character": 10, 555 "start": { "character": 8, "line": 16 }
600 "line": 12
601 },
602 "start": {
603 "character": 8,
604 "line": 12
605 }
606 }, 556 },
607 "targetRange": { 557 "targetRange": {
608 "end": { 558 "end": { "character": 9, "line": 7 },
609 "character": 9, 559 "start": { "character": 0, "line": 6 }
610 "line": 3
611 },
612 "start": {
613 "character": 0,
614 "line": 2
615 }
616 }, 560 },
617 "targetSelectionRange": { 561 "targetSelectionRange": {
618 "end": { 562 "end": { "character": 8, "line": 7 },
619 "character": 8, 563 "start": { "character": 7, "line": 7 }
620 "line": 3
621 },
622 "start": {
623 "character": 7,
624 "line": 3
625 }
626 }, 564 },
627 "targetUri": "file:///[..]src/main.rs" 565 "targetUri": "file:///[..]src/main.rs"
628 }]), 566 }]),
@@ -631,41 +569,23 @@ fn main() { message(); }
631 GotoDefinitionParams { 569 GotoDefinitionParams {
632 text_document_position_params: TextDocumentPositionParams::new( 570 text_document_position_params: TextDocumentPositionParams::new(
633 server.doc_id("src/main.rs"), 571 server.doc_id("src/main.rs"),
634 Position::new(13, 9), 572 Position::new(17, 9),
635 ), 573 ),
636 work_done_progress_params: Default::default(), 574 work_done_progress_params: Default::default(),
637 partial_result_params: Default::default(), 575 partial_result_params: Default::default(),
638 }, 576 },
639 json!([{ 577 json!([{
640 "originSelectionRange": { 578 "originSelectionRange": {
641 "end": { 579 "end": { "character": 10, "line": 17 },
642 "character": 10, 580 "start": { "character": 8, "line": 17 }
643 "line": 13
644 },
645 "start": {
646 "character": 8,
647 "line":13
648 }
649 }, 581 },
650 "targetRange": { 582 "targetRange": {
651 "end": { 583 "end": { "character": 9, "line": 11 },
652 "character": 9, 584 "start": { "character": 0, "line":10 }
653 "line": 7
654 },
655 "start": {
656 "character": 0,
657 "line":6
658 }
659 }, 585 },
660 "targetSelectionRange": { 586 "targetSelectionRange": {
661 "end": { 587 "end": { "character": 8, "line": 11 },
662 "character": 8, 588 "start": { "character": 7, "line": 11 }
663 "line": 7
664 },
665 "start": {
666 "character": 7,
667 "line": 7
668 }
669 }, 589 },
670 "targetUri": "file:///[..]src/main.rs" 590 "targetUri": "file:///[..]src/main.rs"
671 }]), 591 }]),
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index 66a6f4d54..30d03b622 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -19,8 +19,9 @@ use serde_json::{to_string_pretty, Value};
19use tempfile::TempDir; 19use tempfile::TempDir;
20use test_utils::{find_mismatch, parse_fixture}; 20use test_utils::{find_mismatch, parse_fixture};
21 21
22use ra_project_model::ProjectManifest;
22use rust_analyzer::{ 23use rust_analyzer::{
23 config::{ClientCapsConfig, Config}, 24 config::{ClientCapsConfig, Config, LinkedProject},
24 main_loop, 25 main_loop,
25}; 26};
26 27
@@ -42,7 +43,7 @@ impl<'a> Project<'a> {
42 self 43 self
43 } 44 }
44 45
45 pub fn root(mut self, path: &str) -> Project<'a> { 46 pub(crate) fn root(mut self, path: &str) -> Project<'a> {
46 self.roots.push(path.into()); 47 self.roots.push(path.into());
47 self 48 self
48 } 49 }
@@ -74,7 +75,16 @@ impl<'a> Project<'a> {
74 paths.push((path, entry.text)); 75 paths.push((path, entry.text));
75 } 76 }
76 77
77 let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); 78 let mut roots =
79 self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect::<Vec<_>>();
80 if roots.is_empty() {
81 roots.push(tmp_dir.path().to_path_buf());
82 }
83 let linked_projects = roots
84 .into_iter()
85 .map(|it| ProjectManifest::discover_single(&it).unwrap())
86 .map(LinkedProject::from)
87 .collect::<Vec<_>>();
78 88
79 let mut config = Config { 89 let mut config = Config {
80 client_caps: ClientCapsConfig { 90 client_caps: ClientCapsConfig {
@@ -84,6 +94,7 @@ impl<'a> Project<'a> {
84 ..Default::default() 94 ..Default::default()
85 }, 95 },
86 with_sysroot: self.with_sysroot, 96 with_sysroot: self.with_sysroot,
97 linked_projects,
87 ..Config::default() 98 ..Config::default()
88 }; 99 };
89 100
@@ -91,7 +102,7 @@ impl<'a> Project<'a> {
91 f(&mut config) 102 f(&mut config)
92 } 103 }
93 104
94 Server::new(tmp_dir, config, roots, paths) 105 Server::new(tmp_dir, config, paths)
95 } 106 }
96} 107}
97 108
@@ -109,20 +120,12 @@ pub struct Server {
109} 120}
110 121
111impl Server { 122impl Server {
112 fn new( 123 fn new(dir: TempDir, config: Config, files: Vec<(PathBuf, String)>) -> Server {
113 dir: TempDir,
114 config: Config,
115 roots: Vec<PathBuf>,
116 files: Vec<(PathBuf, String)>,
117 ) -> Server {
118 let path = dir.path().to_path_buf();
119
120 let roots = if roots.is_empty() { vec![path] } else { roots };
121 let (connection, client) = Connection::memory(); 124 let (connection, client) = Connection::memory();
122 125
123 let _thread = jod_thread::Builder::new() 126 let _thread = jod_thread::Builder::new()
124 .name("test server".to_string()) 127 .name("test server".to_string())
125 .spawn(move || main_loop(roots, config, connection).unwrap()) 128 .spawn(move || main_loop(config, connection).unwrap())
126 .expect("failed to spawn a thread"); 129 .expect("failed to spawn a thread");
127 130
128 let res = 131 let res =
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 71a57fba2..c0356344c 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -124,3 +124,8 @@ pub fn replace(buf: &mut String, from: char, to: &str) {
124 // FIXME: do this in place. 124 // FIXME: do this in place.
125 *buf = buf.replace(from, to) 125 *buf = buf.replace(from, to)
126} 126}
127
128pub fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
129 let idx = haystack.find(delim)?;
130 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
131}
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 4d185b01c..8840bf36a 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -14,4 +14,5 @@ serde_json = "1.0.48"
14relative-path = "1.0.0" 14relative-path = "1.0.0"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16 16
17ra_cfg = { path = "../ra_cfg" } \ No newline at end of file 17ra_cfg = { path = "../ra_cfg" }
18stdx = { path = "../stdx" } \ No newline at end of file
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 1bd97215c..2141bfc20 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -15,6 +15,7 @@ use std::{
15}; 15};
16 16
17pub use ra_cfg::CfgOptions; 17pub use ra_cfg::CfgOptions;
18use stdx::split1;
18 19
19pub use relative_path::{RelativePath, RelativePathBuf}; 20pub use relative_path::{RelativePath, RelativePathBuf};
20pub use rustc_hash::FxHashMap; 21pub use rustc_hash::FxHashMap;
@@ -332,11 +333,6 @@ fn parse_meta(meta: &str) -> FixtureMeta {
332 FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env }) 333 FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env })
333} 334}
334 335
335fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
336 let idx = haystack.find(delim)?;
337 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
338}
339
340/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. 336/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines.
341/// This allows fixtures to start off in a different indentation, e.g. to align the first line with 337/// This allows fixtures to start off in a different indentation, e.g. to align the first line with
342/// the other lines visually: 338/// the other lines visually: