diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 588 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs | 29 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 70 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 22 | ||||
-rw-r--r-- | crates/ra_ide/src/goto_type_definition.rs | 22 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 2 | ||||
-rw-r--r-- | crates/ra_parser/src/grammar/params.rs | 5 | ||||
-rw-r--r-- | crates/ra_project_model/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 11 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 51 | ||||
-rw-r--r-- | crates/ra_project_model/src/sysroot.rs | 14 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 9 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/extensions.rs | 18 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.txt | 26 |
16 files changed, 773 insertions, 105 deletions
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 1fb701da5..c4aea2a06 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -1,10 +1,18 @@ | |||
1 | use ra_ide_db::imports_locator::ImportsLocator; | ||
2 | use ra_syntax::ast::{self, AstNode}; | ||
3 | |||
4 | use crate::{ | 1 | use crate::{ |
5 | assist_ctx::{Assist, AssistCtx}, | 2 | assist_ctx::{Assist, AssistCtx}, |
6 | insert_use_statement, AssistId, | 3 | insert_use_statement, AssistId, |
7 | }; | 4 | }; |
5 | use hir::{ | ||
6 | db::HirDatabase, AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, | ||
7 | SourceAnalyzer, Trait, Type, | ||
8 | }; | ||
9 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; | ||
10 | use ra_prof::profile; | ||
11 | use ra_syntax::{ | ||
12 | ast::{self, AstNode}, | ||
13 | SyntaxNode, | ||
14 | }; | ||
15 | use rustc_hash::FxHashSet; | ||
8 | use std::collections::BTreeSet; | 16 | use std::collections::BTreeSet; |
9 | 17 | ||
10 | // Assist: auto_import | 18 | // Assist: auto_import |
@@ -27,52 +35,24 @@ use std::collections::BTreeSet; | |||
27 | // # pub mod std { pub mod collections { pub struct HashMap { } } } | 35 | // # pub mod std { pub mod collections { pub struct HashMap { } } } |
28 | // ``` | 36 | // ``` |
29 | pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { | 37 | pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { |
30 | let path_under_caret: ast::Path = ctx.find_node_at_offset()?; | 38 | let auto_import_assets = AutoImportAssets::new(&ctx)?; |
31 | if path_under_caret.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { | 39 | let proposed_imports = auto_import_assets.search_for_imports(ctx.db); |
32 | return None; | ||
33 | } | ||
34 | |||
35 | let module = path_under_caret.syntax().ancestors().find_map(ast::Module::cast); | ||
36 | let position = match module.and_then(|it| it.item_list()) { | ||
37 | Some(item_list) => item_list.syntax().clone(), | ||
38 | None => { | ||
39 | let current_file = | ||
40 | path_under_caret.syntax().ancestors().find_map(ast::SourceFile::cast)?; | ||
41 | current_file.syntax().clone() | ||
42 | } | ||
43 | }; | ||
44 | let source_analyzer = ctx.source_analyzer(&position, None); | ||
45 | let module_with_name_to_import = source_analyzer.module()?; | ||
46 | |||
47 | let name_ref_to_import = | ||
48 | path_under_caret.syntax().descendants().find_map(ast::NameRef::cast)?; | ||
49 | if source_analyzer | ||
50 | .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?) | ||
51 | .is_some() | ||
52 | { | ||
53 | return None; | ||
54 | } | ||
55 | |||
56 | let name_to_import = name_ref_to_import.syntax().to_string(); | ||
57 | let proposed_imports = ImportsLocator::new(ctx.db) | ||
58 | .find_imports(&name_to_import) | ||
59 | .into_iter() | ||
60 | .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) | ||
61 | .filter(|use_path| !use_path.segments.is_empty()) | ||
62 | .take(20) | ||
63 | .collect::<BTreeSet<_>>(); | ||
64 | |||
65 | if proposed_imports.is_empty() { | 40 | if proposed_imports.is_empty() { |
66 | return None; | 41 | return None; |
67 | } | 42 | } |
68 | 43 | ||
69 | let mut group = ctx.add_assist_group(format!("Import {}", name_to_import)); | 44 | let assist_group_name = if proposed_imports.len() == 1 { |
45 | format!("Import `{}`", proposed_imports.iter().next().unwrap()) | ||
46 | } else { | ||
47 | auto_import_assets.get_import_group_message() | ||
48 | }; | ||
49 | let mut group = ctx.add_assist_group(assist_group_name); | ||
70 | for import in proposed_imports { | 50 | for import in proposed_imports { |
71 | group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { | 51 | group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { |
72 | edit.target(path_under_caret.syntax().text_range()); | 52 | edit.target(auto_import_assets.syntax_under_caret.text_range()); |
73 | insert_use_statement( | 53 | insert_use_statement( |
74 | &position, | 54 | &auto_import_assets.syntax_under_caret, |
75 | path_under_caret.syntax(), | 55 | &auto_import_assets.syntax_under_caret, |
76 | &import, | 56 | &import, |
77 | edit.text_edit_builder(), | 57 | edit.text_edit_builder(), |
78 | ); | 58 | ); |
@@ -81,11 +61,232 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { | |||
81 | group.finish() | 61 | group.finish() |
82 | } | 62 | } |
83 | 63 | ||
64 | struct AutoImportAssets { | ||
65 | import_candidate: ImportCandidate, | ||
66 | module_with_name_to_import: Module, | ||
67 | syntax_under_caret: SyntaxNode, | ||
68 | } | ||
69 | |||
70 | impl AutoImportAssets { | ||
71 | fn new(ctx: &AssistCtx) -> Option<Self> { | ||
72 | if let Some(path_under_caret) = ctx.find_node_at_offset::<ast::Path>() { | ||
73 | Self::for_regular_path(path_under_caret, &ctx) | ||
74 | } else { | ||
75 | Self::for_method_call(ctx.find_node_at_offset()?, &ctx) | ||
76 | } | ||
77 | } | ||
78 | |||
79 | fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> { | ||
80 | let syntax_under_caret = method_call.syntax().to_owned(); | ||
81 | let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); | ||
82 | let module_with_name_to_import = source_analyzer.module()?; | ||
83 | Some(Self { | ||
84 | import_candidate: ImportCandidate::for_method_call( | ||
85 | &method_call, | ||
86 | &source_analyzer, | ||
87 | ctx.db, | ||
88 | )?, | ||
89 | module_with_name_to_import, | ||
90 | syntax_under_caret, | ||
91 | }) | ||
92 | } | ||
93 | |||
94 | fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option<Self> { | ||
95 | let syntax_under_caret = path_under_caret.syntax().to_owned(); | ||
96 | if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() { | ||
97 | return None; | ||
98 | } | ||
99 | |||
100 | let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); | ||
101 | let module_with_name_to_import = source_analyzer.module()?; | ||
102 | Some(Self { | ||
103 | import_candidate: ImportCandidate::for_regular_path( | ||
104 | &path_under_caret, | ||
105 | &source_analyzer, | ||
106 | ctx.db, | ||
107 | )?, | ||
108 | module_with_name_to_import, | ||
109 | syntax_under_caret, | ||
110 | }) | ||
111 | } | ||
112 | |||
113 | fn get_search_query(&self) -> &str { | ||
114 | match &self.import_candidate { | ||
115 | ImportCandidate::UnqualifiedName(name) => name, | ||
116 | ImportCandidate::QualifierStart(qualifier_start) => qualifier_start, | ||
117 | ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => trait_assoc_item_name, | ||
118 | ImportCandidate::TraitMethod(_, trait_method_name) => trait_method_name, | ||
119 | } | ||
120 | } | ||
121 | |||
122 | fn get_import_group_message(&self) -> String { | ||
123 | match &self.import_candidate { | ||
124 | ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), | ||
125 | ImportCandidate::QualifierStart(qualifier_start) => { | ||
126 | format!("Import {}", qualifier_start) | ||
127 | } | ||
128 | ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => { | ||
129 | format!("Import a trait for item {}", trait_assoc_item_name) | ||
130 | } | ||
131 | ImportCandidate::TraitMethod(_, trait_method_name) => { | ||
132 | format!("Import a trait for method {}", trait_method_name) | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { | ||
138 | let _p = profile("auto_import::search_for_imports"); | ||
139 | let current_crate = self.module_with_name_to_import.krate(); | ||
140 | ImportsLocator::new(db) | ||
141 | .find_imports(&self.get_search_query()) | ||
142 | .into_iter() | ||
143 | .filter_map(|module_def| match &self.import_candidate { | ||
144 | ImportCandidate::TraitAssocItem(assoc_item_type, _) => { | ||
145 | let located_assoc_item = match module_def { | ||
146 | ModuleDef::Function(located_function) => located_function | ||
147 | .as_assoc_item(db) | ||
148 | .map(|assoc| assoc.container(db)) | ||
149 | .and_then(Self::assoc_to_trait), | ||
150 | ModuleDef::Const(located_const) => located_const | ||
151 | .as_assoc_item(db) | ||
152 | .map(|assoc| assoc.container(db)) | ||
153 | .and_then(Self::assoc_to_trait), | ||
154 | _ => None, | ||
155 | }?; | ||
156 | |||
157 | let mut trait_candidates = FxHashSet::default(); | ||
158 | trait_candidates.insert(located_assoc_item.into()); | ||
159 | |||
160 | assoc_item_type | ||
161 | .iterate_path_candidates( | ||
162 | db, | ||
163 | current_crate, | ||
164 | &trait_candidates, | ||
165 | None, | ||
166 | |_, assoc| Self::assoc_to_trait(assoc.container(db)), | ||
167 | ) | ||
168 | .map(ModuleDef::from) | ||
169 | } | ||
170 | ImportCandidate::TraitMethod(function_callee, _) => { | ||
171 | let located_assoc_item = | ||
172 | if let ModuleDef::Function(located_function) = module_def { | ||
173 | located_function | ||
174 | .as_assoc_item(db) | ||
175 | .map(|assoc| assoc.container(db)) | ||
176 | .and_then(Self::assoc_to_trait) | ||
177 | } else { | ||
178 | None | ||
179 | }?; | ||
180 | |||
181 | let mut trait_candidates = FxHashSet::default(); | ||
182 | trait_candidates.insert(located_assoc_item.into()); | ||
183 | |||
184 | function_callee | ||
185 | .iterate_method_candidates( | ||
186 | db, | ||
187 | current_crate, | ||
188 | &trait_candidates, | ||
189 | None, | ||
190 | |_, function| { | ||
191 | Self::assoc_to_trait(function.as_assoc_item(db)?.container(db)) | ||
192 | }, | ||
193 | ) | ||
194 | .map(ModuleDef::from) | ||
195 | } | ||
196 | _ => Some(module_def), | ||
197 | }) | ||
198 | .filter_map(|module_def| self.module_with_name_to_import.find_use_path(db, module_def)) | ||
199 | .filter(|use_path| !use_path.segments.is_empty()) | ||
200 | .take(20) | ||
201 | .collect::<BTreeSet<_>>() | ||
202 | } | ||
203 | |||
204 | fn assoc_to_trait(assoc: AssocItemContainer) -> Option<Trait> { | ||
205 | if let AssocItemContainer::Trait(extracted_trait) = assoc { | ||
206 | Some(extracted_trait) | ||
207 | } else { | ||
208 | None | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | #[derive(Debug)] | ||
214 | enum ImportCandidate { | ||
215 | /// Simple name like 'HashMap' | ||
216 | UnqualifiedName(String), | ||
217 | /// First part of the qualified name. | ||
218 | /// For 'std::collections::HashMap', that will be 'std'. | ||
219 | QualifierStart(String), | ||
220 | /// A trait associated function (with no self parameter) or associated constant. | ||
221 | /// For 'test_mod::TestEnum::test_function', `Type` is the `test_mod::TestEnum` expression type | ||
222 | /// and `String` is the `test_function` | ||
223 | TraitAssocItem(Type, String), | ||
224 | /// A trait method with self parameter. | ||
225 | /// For 'test_enum.test_method()', `Type` is the `test_enum` expression type | ||
226 | /// and `String` is the `test_method` | ||
227 | TraitMethod(Type, String), | ||
228 | } | ||
229 | |||
230 | impl ImportCandidate { | ||
231 | fn for_method_call( | ||
232 | method_call: &ast::MethodCallExpr, | ||
233 | source_analyzer: &SourceAnalyzer, | ||
234 | db: &impl HirDatabase, | ||
235 | ) -> Option<Self> { | ||
236 | if source_analyzer.resolve_method_call(method_call).is_some() { | ||
237 | return None; | ||
238 | } | ||
239 | Some(Self::TraitMethod( | ||
240 | source_analyzer.type_of(db, &method_call.expr()?)?, | ||
241 | method_call.name_ref()?.syntax().to_string(), | ||
242 | )) | ||
243 | } | ||
244 | |||
245 | fn for_regular_path( | ||
246 | path_under_caret: &ast::Path, | ||
247 | source_analyzer: &SourceAnalyzer, | ||
248 | db: &impl HirDatabase, | ||
249 | ) -> Option<Self> { | ||
250 | if source_analyzer.resolve_path(db, path_under_caret).is_some() { | ||
251 | return None; | ||
252 | } | ||
253 | |||
254 | let segment = path_under_caret.segment()?; | ||
255 | if let Some(qualifier) = path_under_caret.qualifier() { | ||
256 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | ||
257 | let qualifier_start_path = | ||
258 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | ||
259 | if let Some(qualifier_start_resolution) = | ||
260 | source_analyzer.resolve_path(db, &qualifier_start_path) | ||
261 | { | ||
262 | let qualifier_resolution = if qualifier_start_path == qualifier { | ||
263 | qualifier_start_resolution | ||
264 | } else { | ||
265 | source_analyzer.resolve_path(db, &qualifier)? | ||
266 | }; | ||
267 | if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution { | ||
268 | Some(ImportCandidate::TraitAssocItem( | ||
269 | assoc_item_path.ty(db), | ||
270 | segment.syntax().to_string(), | ||
271 | )) | ||
272 | } else { | ||
273 | None | ||
274 | } | ||
275 | } else { | ||
276 | Some(ImportCandidate::QualifierStart(qualifier_start.syntax().to_string())) | ||
277 | } | ||
278 | } else { | ||
279 | Some(ImportCandidate::UnqualifiedName( | ||
280 | segment.syntax().descendants().find_map(ast::NameRef::cast)?.syntax().to_string(), | ||
281 | )) | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
84 | #[cfg(test)] | 286 | #[cfg(test)] |
85 | mod tests { | 287 | mod tests { |
86 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; | ||
87 | |||
88 | use super::*; | 288 | use super::*; |
289 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; | ||
89 | 290 | ||
90 | #[test] | 291 | #[test] |
91 | fn applicable_when_found_an_import() { | 292 | fn applicable_when_found_an_import() { |
@@ -290,4 +491,303 @@ mod tests { | |||
290 | ", | 491 | ", |
291 | ); | 492 | ); |
292 | } | 493 | } |
494 | |||
495 | #[test] | ||
496 | fn not_applicable_for_imported_function() { | ||
497 | check_assist_not_applicable( | ||
498 | auto_import, | ||
499 | r" | ||
500 | pub mod test_mod { | ||
501 | pub fn test_function() {} | ||
502 | } | ||
503 | |||
504 | use test_mod::test_function; | ||
505 | fn main() { | ||
506 | test_function<|> | ||
507 | } | ||
508 | ", | ||
509 | ); | ||
510 | } | ||
511 | |||
512 | #[test] | ||
513 | fn associated_struct_function() { | ||
514 | check_assist( | ||
515 | auto_import, | ||
516 | r" | ||
517 | mod test_mod { | ||
518 | pub struct TestStruct {} | ||
519 | impl TestStruct { | ||
520 | pub fn test_function() {} | ||
521 | } | ||
522 | } | ||
523 | |||
524 | fn main() { | ||
525 | TestStruct::test_function<|> | ||
526 | } | ||
527 | ", | ||
528 | r" | ||
529 | use test_mod::TestStruct; | ||
530 | |||
531 | mod test_mod { | ||
532 | pub struct TestStruct {} | ||
533 | impl TestStruct { | ||
534 | pub fn test_function() {} | ||
535 | } | ||
536 | } | ||
537 | |||
538 | fn main() { | ||
539 | TestStruct::test_function<|> | ||
540 | } | ||
541 | ", | ||
542 | ); | ||
543 | } | ||
544 | |||
545 | #[test] | ||
546 | fn associated_struct_const() { | ||
547 | check_assist( | ||
548 | auto_import, | ||
549 | r" | ||
550 | mod test_mod { | ||
551 | pub struct TestStruct {} | ||
552 | impl TestStruct { | ||
553 | const TEST_CONST: u8 = 42; | ||
554 | } | ||
555 | } | ||
556 | |||
557 | fn main() { | ||
558 | TestStruct::TEST_CONST<|> | ||
559 | } | ||
560 | ", | ||
561 | r" | ||
562 | use test_mod::TestStruct; | ||
563 | |||
564 | mod test_mod { | ||
565 | pub struct TestStruct {} | ||
566 | impl TestStruct { | ||
567 | const TEST_CONST: u8 = 42; | ||
568 | } | ||
569 | } | ||
570 | |||
571 | fn main() { | ||
572 | TestStruct::TEST_CONST<|> | ||
573 | } | ||
574 | ", | ||
575 | ); | ||
576 | } | ||
577 | |||
578 | #[test] | ||
579 | fn associated_trait_function() { | ||
580 | check_assist( | ||
581 | auto_import, | ||
582 | r" | ||
583 | mod test_mod { | ||
584 | pub trait TestTrait { | ||
585 | fn test_function(); | ||
586 | } | ||
587 | pub struct TestStruct {} | ||
588 | impl TestTrait for TestStruct { | ||
589 | fn test_function() {} | ||
590 | } | ||
591 | } | ||
592 | |||
593 | fn main() { | ||
594 | test_mod::TestStruct::test_function<|> | ||
595 | } | ||
596 | ", | ||
597 | r" | ||
598 | use test_mod::TestTrait; | ||
599 | |||
600 | mod test_mod { | ||
601 | pub trait TestTrait { | ||
602 | fn test_function(); | ||
603 | } | ||
604 | pub struct TestStruct {} | ||
605 | impl TestTrait for TestStruct { | ||
606 | fn test_function() {} | ||
607 | } | ||
608 | } | ||
609 | |||
610 | fn main() { | ||
611 | test_mod::TestStruct::test_function<|> | ||
612 | } | ||
613 | ", | ||
614 | ); | ||
615 | } | ||
616 | |||
617 | #[test] | ||
618 | fn not_applicable_for_imported_trait_for_function() { | ||
619 | check_assist_not_applicable( | ||
620 | auto_import, | ||
621 | r" | ||
622 | mod test_mod { | ||
623 | pub trait TestTrait { | ||
624 | fn test_function(); | ||
625 | } | ||
626 | pub trait TestTrait2 { | ||
627 | fn test_function(); | ||
628 | } | ||
629 | pub enum TestEnum { | ||
630 | One, | ||
631 | Two, | ||
632 | } | ||
633 | impl TestTrait2 for TestEnum { | ||
634 | fn test_function() {} | ||
635 | } | ||
636 | impl TestTrait for TestEnum { | ||
637 | fn test_function() {} | ||
638 | } | ||
639 | } | ||
640 | |||
641 | use test_mod::TestTrait2; | ||
642 | fn main() { | ||
643 | test_mod::TestEnum::test_function<|>; | ||
644 | } | ||
645 | ", | ||
646 | ) | ||
647 | } | ||
648 | |||
649 | #[test] | ||
650 | fn associated_trait_const() { | ||
651 | check_assist( | ||
652 | auto_import, | ||
653 | r" | ||
654 | mod test_mod { | ||
655 | pub trait TestTrait { | ||
656 | const TEST_CONST: u8; | ||
657 | } | ||
658 | pub struct TestStruct {} | ||
659 | impl TestTrait for TestStruct { | ||
660 | const TEST_CONST: u8 = 42; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | fn main() { | ||
665 | test_mod::TestStruct::TEST_CONST<|> | ||
666 | } | ||
667 | ", | ||
668 | r" | ||
669 | use test_mod::TestTrait; | ||
670 | |||
671 | mod test_mod { | ||
672 | pub trait TestTrait { | ||
673 | const TEST_CONST: u8; | ||
674 | } | ||
675 | pub struct TestStruct {} | ||
676 | impl TestTrait for TestStruct { | ||
677 | const TEST_CONST: u8 = 42; | ||
678 | } | ||
679 | } | ||
680 | |||
681 | fn main() { | ||
682 | test_mod::TestStruct::TEST_CONST<|> | ||
683 | } | ||
684 | ", | ||
685 | ); | ||
686 | } | ||
687 | |||
688 | #[test] | ||
689 | fn not_applicable_for_imported_trait_for_const() { | ||
690 | check_assist_not_applicable( | ||
691 | auto_import, | ||
692 | r" | ||
693 | mod test_mod { | ||
694 | pub trait TestTrait { | ||
695 | const TEST_CONST: u8; | ||
696 | } | ||
697 | pub trait TestTrait2 { | ||
698 | const TEST_CONST: f64; | ||
699 | } | ||
700 | pub enum TestEnum { | ||
701 | One, | ||
702 | Two, | ||
703 | } | ||
704 | impl TestTrait2 for TestEnum { | ||
705 | const TEST_CONST: f64 = 42.0; | ||
706 | } | ||
707 | impl TestTrait for TestEnum { | ||
708 | const TEST_CONST: u8 = 42; | ||
709 | } | ||
710 | } | ||
711 | |||
712 | use test_mod::TestTrait2; | ||
713 | fn main() { | ||
714 | test_mod::TestEnum::TEST_CONST<|>; | ||
715 | } | ||
716 | ", | ||
717 | ) | ||
718 | } | ||
719 | |||
720 | #[test] | ||
721 | fn trait_method() { | ||
722 | check_assist( | ||
723 | auto_import, | ||
724 | r" | ||
725 | mod test_mod { | ||
726 | pub trait TestTrait { | ||
727 | fn test_method(&self); | ||
728 | } | ||
729 | pub struct TestStruct {} | ||
730 | impl TestTrait for TestStruct { | ||
731 | fn test_method(&self) {} | ||
732 | } | ||
733 | } | ||
734 | |||
735 | fn main() { | ||
736 | let test_struct = test_mod::TestStruct {}; | ||
737 | test_struct.test_meth<|>od() | ||
738 | } | ||
739 | ", | ||
740 | r" | ||
741 | use test_mod::TestTrait; | ||
742 | |||
743 | mod test_mod { | ||
744 | pub trait TestTrait { | ||
745 | fn test_method(&self); | ||
746 | } | ||
747 | pub struct TestStruct {} | ||
748 | impl TestTrait for TestStruct { | ||
749 | fn test_method(&self) {} | ||
750 | } | ||
751 | } | ||
752 | |||
753 | fn main() { | ||
754 | let test_struct = test_mod::TestStruct {}; | ||
755 | test_struct.test_meth<|>od() | ||
756 | } | ||
757 | ", | ||
758 | ); | ||
759 | } | ||
760 | |||
761 | #[test] | ||
762 | fn not_applicable_for_imported_trait_for_method() { | ||
763 | check_assist_not_applicable( | ||
764 | auto_import, | ||
765 | r" | ||
766 | mod test_mod { | ||
767 | pub trait TestTrait { | ||
768 | fn test_method(&self); | ||
769 | } | ||
770 | pub trait TestTrait2 { | ||
771 | fn test_method(&self); | ||
772 | } | ||
773 | pub enum TestEnum { | ||
774 | One, | ||
775 | Two, | ||
776 | } | ||
777 | impl TestTrait2 for TestEnum { | ||
778 | fn test_method(&self) {} | ||
779 | } | ||
780 | impl TestTrait for TestEnum { | ||
781 | fn test_method(&self) {} | ||
782 | } | ||
783 | } | ||
784 | |||
785 | use test_mod::TestTrait2; | ||
786 | fn main() { | ||
787 | let one = test_mod::TestEnum::One; | ||
788 | one.test<|>_method(); | ||
789 | } | ||
790 | ", | ||
791 | ) | ||
792 | } | ||
293 | } | 793 | } |
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index b70c88ec2..eac452413 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -431,7 +431,12 @@ fn best_action_for_target( | |||
431 | .find(|n| n.text_range().start() < anchor.text_range().start()) | 431 | .find(|n| n.text_range().start() < anchor.text_range().start()) |
432 | .or_else(|| Some(anchor)); | 432 | .or_else(|| Some(anchor)); |
433 | 433 | ||
434 | ImportAction::add_new_use(anchor, false) | 434 | let add_after_anchor = anchor |
435 | .clone() | ||
436 | .and_then(ast::Attr::cast) | ||
437 | .map(|attr| attr.kind() == ast::AttrKind::Inner) | ||
438 | .unwrap_or(false); | ||
439 | ImportAction::add_new_use(anchor, add_after_anchor) | ||
435 | } | 440 | } |
436 | } | 441 | } |
437 | } | 442 | } |
@@ -962,4 +967,26 @@ mod foo { | |||
962 | ", | 967 | ", |
963 | ); | 968 | ); |
964 | } | 969 | } |
970 | |||
971 | #[test] | ||
972 | fn inserts_imports_after_inner_attributes() { | ||
973 | check_assist( | ||
974 | replace_qualified_name_with_use, | ||
975 | " | ||
976 | #![allow(dead_code)] | ||
977 | |||
978 | fn main() { | ||
979 | std::fmt::Debug<|> | ||
980 | } | ||
981 | ", | ||
982 | " | ||
983 | #![allow(dead_code)] | ||
984 | use std::fmt::Debug; | ||
985 | |||
986 | fn main() { | ||
987 | Debug<|> | ||
988 | } | ||
989 | ", | ||
990 | ); | ||
991 | } | ||
965 | } | 992 | } |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 4d9641728..a56b8ab04 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -10,9 +10,9 @@ use hir_def::{ | |||
10 | per_ns::PerNs, | 10 | per_ns::PerNs, |
11 | resolver::HasResolver, | 11 | resolver::HasResolver, |
12 | type_ref::{Mutability, TypeRef}, | 12 | type_ref::{Mutability, TypeRef}, |
13 | AdtId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, | 13 | AdtId, AssocContainerId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, |
14 | LocalEnumVariantId, LocalModuleId, LocalStructFieldId, Lookup, ModuleId, StaticId, StructId, | 14 | ImplId, LocalEnumVariantId, LocalModuleId, LocalStructFieldId, Lookup, ModuleId, StaticId, |
15 | TraitId, TypeAliasId, TypeParamId, UnionId, | 15 | StructId, TraitId, TypeAliasId, TypeParamId, UnionId, |
16 | }; | 16 | }; |
17 | use hir_expand::{ | 17 | use hir_expand::{ |
18 | diagnostics::DiagnosticSink, | 18 | diagnostics::DiagnosticSink, |
@@ -25,7 +25,10 @@ use hir_ty::{ | |||
25 | }; | 25 | }; |
26 | use ra_db::{CrateId, Edition, FileId}; | 26 | use ra_db::{CrateId, Edition, FileId}; |
27 | use ra_prof::profile; | 27 | use ra_prof::profile; |
28 | use ra_syntax::ast::{self, AttrsOwner}; | 28 | use ra_syntax::{ |
29 | ast::{self, AttrsOwner}, | ||
30 | AstNode, | ||
31 | }; | ||
29 | 32 | ||
30 | use crate::{ | 33 | use crate::{ |
31 | db::{DefDatabase, HirDatabase}, | 34 | db::{DefDatabase, HirDatabase}, |
@@ -119,7 +122,9 @@ impl_froms!( | |||
119 | BuiltinType | 122 | BuiltinType |
120 | ); | 123 | ); |
121 | 124 | ||
122 | pub use hir_def::{attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId}; | 125 | pub use hir_def::{ |
126 | attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId, AssocItemLoc, | ||
127 | }; | ||
123 | use rustc_hash::FxHashSet; | 128 | use rustc_hash::FxHashSet; |
124 | 129 | ||
125 | impl Module { | 130 | impl Module { |
@@ -639,17 +644,49 @@ pub struct MacroDef { | |||
639 | pub(crate) id: MacroDefId, | 644 | pub(crate) id: MacroDefId, |
640 | } | 645 | } |
641 | 646 | ||
647 | /// Invariant: `inner.as_assoc_item(db).is_some()` | ||
648 | /// We do not actively enforce this invariant. | ||
642 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 649 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
643 | pub enum AssocItem { | 650 | pub enum AssocItem { |
644 | Function(Function), | 651 | Function(Function), |
645 | Const(Const), | 652 | Const(Const), |
646 | TypeAlias(TypeAlias), | 653 | TypeAlias(TypeAlias), |
647 | } | 654 | } |
648 | // FIXME: not every function, ... is actually an assoc item. maybe we should make | 655 | pub enum AssocItemContainer { |
649 | // sure that you can only turn actual assoc items into AssocItems. This would | 656 | Trait(Trait), |
650 | // require not implementing From, and instead having some checked way of | 657 | ImplBlock(ImplBlock), |
651 | // casting them, and somehow making the constructors private, which would be annoying. | 658 | } |
652 | impl_froms!(AssocItem: Function, Const, TypeAlias); | 659 | pub trait AsAssocItem { |
660 | fn as_assoc_item(self, db: &impl DefDatabase) -> Option<AssocItem>; | ||
661 | } | ||
662 | |||
663 | impl AsAssocItem for Function { | ||
664 | fn as_assoc_item(self, db: &impl DefDatabase) -> Option<AssocItem> { | ||
665 | as_assoc_item(db, AssocItem::Function, self.id) | ||
666 | } | ||
667 | } | ||
668 | impl AsAssocItem for Const { | ||
669 | fn as_assoc_item(self, db: &impl DefDatabase) -> Option<AssocItem> { | ||
670 | as_assoc_item(db, AssocItem::Const, self.id) | ||
671 | } | ||
672 | } | ||
673 | impl AsAssocItem for TypeAlias { | ||
674 | fn as_assoc_item(self, db: &impl DefDatabase) -> Option<AssocItem> { | ||
675 | as_assoc_item(db, AssocItem::TypeAlias, self.id) | ||
676 | } | ||
677 | } | ||
678 | fn as_assoc_item<ID, DEF, CTOR, AST>(db: &impl DefDatabase, ctor: CTOR, id: ID) -> Option<AssocItem> | ||
679 | where | ||
680 | ID: Lookup<Data = AssocItemLoc<AST>>, | ||
681 | DEF: From<ID>, | ||
682 | CTOR: FnOnce(DEF) -> AssocItem, | ||
683 | AST: AstNode, | ||
684 | { | ||
685 | match id.lookup(db).container { | ||
686 | AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), | ||
687 | AssocContainerId::ContainerId(_) => None, | ||
688 | } | ||
689 | } | ||
653 | 690 | ||
654 | impl AssocItem { | 691 | impl AssocItem { |
655 | pub fn module(self, db: &impl DefDatabase) -> Module { | 692 | pub fn module(self, db: &impl DefDatabase) -> Module { |
@@ -659,6 +696,18 @@ impl AssocItem { | |||
659 | AssocItem::TypeAlias(t) => t.module(db), | 696 | AssocItem::TypeAlias(t) => t.module(db), |
660 | } | 697 | } |
661 | } | 698 | } |
699 | pub fn container(self, db: &impl DefDatabase) -> AssocItemContainer { | ||
700 | let container = match self { | ||
701 | AssocItem::Function(it) => it.id.lookup(db).container, | ||
702 | AssocItem::Const(it) => it.id.lookup(db).container, | ||
703 | AssocItem::TypeAlias(it) => it.id.lookup(db).container, | ||
704 | }; | ||
705 | match container { | ||
706 | AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), | ||
707 | AssocContainerId::ImplId(id) => AssocItemContainer::ImplBlock(id.into()), | ||
708 | AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), | ||
709 | } | ||
710 | } | ||
662 | } | 711 | } |
663 | 712 | ||
664 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | 713 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] |
@@ -769,6 +818,7 @@ impl TypeParam { | |||
769 | } | 818 | } |
770 | } | 819 | } |
771 | 820 | ||
821 | // FIXME: rename from `ImplBlock` to `Impl` | ||
772 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 822 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
773 | pub struct ImplBlock { | 823 | pub struct ImplBlock { |
774 | pub(crate) id: ImplId, | 824 | pub(crate) id: ImplId, |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index e1c7b7a20..5cd965f7a 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -39,10 +39,10 @@ mod has_source; | |||
39 | 39 | ||
40 | pub use crate::{ | 40 | pub use crate::{ |
41 | code_model::{ | 41 | code_model::{ |
42 | Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum, | 42 | Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency, |
43 | EnumVariant, FieldSource, Function, GenericDef, HasAttrs, HasVisibility, ImplBlock, Local, | 43 | DefWithBody, Docs, Enum, EnumVariant, FieldSource, Function, GenericDef, HasAttrs, |
44 | MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias, | 44 | HasVisibility, ImplBlock, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, |
45 | TypeParam, Union, VariantDef, | 45 | StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, |
46 | }, | 46 | }, |
47 | has_source::HasSource, | 47 | has_source::HasSource, |
48 | source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, | 48 | source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, |
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 7cf3b59a7..9506f2e1c 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -323,11 +323,18 @@ impl<T: Clone> InFile<&T> { | |||
323 | } | 323 | } |
324 | } | 324 | } |
325 | 325 | ||
326 | impl<T> InFile<Option<T>> { | ||
327 | pub fn transpose(self) -> Option<InFile<T>> { | ||
328 | let value = self.value?; | ||
329 | Some(InFile::new(self.file_id, value)) | ||
330 | } | ||
331 | } | ||
332 | |||
326 | impl InFile<SyntaxNode> { | 333 | impl InFile<SyntaxNode> { |
327 | pub fn ancestors_with_macros<'a>( | 334 | pub fn ancestors_with_macros( |
328 | self, | 335 | self, |
329 | db: &'a impl crate::db::AstDatabase, | 336 | db: &impl crate::db::AstDatabase, |
330 | ) -> impl Iterator<Item = InFile<SyntaxNode>> + 'a { | 337 | ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ { |
331 | std::iter::successors(Some(self), move |node| match node.value.parent() { | 338 | std::iter::successors(Some(self), move |node| match node.value.parent() { |
332 | Some(parent) => Some(node.with_value(parent)), | 339 | Some(parent) => Some(node.with_value(parent)), |
333 | None => { | 340 | None => { |
@@ -338,6 +345,15 @@ impl InFile<SyntaxNode> { | |||
338 | } | 345 | } |
339 | } | 346 | } |
340 | 347 | ||
348 | impl InFile<SyntaxToken> { | ||
349 | pub fn ancestors_with_macros( | ||
350 | self, | ||
351 | db: &impl crate::db::AstDatabase, | ||
352 | ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ { | ||
353 | self.map(|it| it.parent()).ancestors_with_macros(db) | ||
354 | } | ||
355 | } | ||
356 | |||
341 | impl<N: AstNode> InFile<N> { | 357 | impl<N: AstNode> InFile<N> { |
342 | pub fn descendants<T: AstNode>(self) -> impl Iterator<Item = InFile<T>> { | 358 | pub fn descendants<T: AstNode>(self) -> impl Iterator<Item = InFile<T>> { |
343 | self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n)) | 359 | self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n)) |
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index 11ad6d137..69940fc36 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs | |||
@@ -16,24 +16,16 @@ pub(crate) fn goto_type_definition( | |||
16 | let token = pick_best(file.token_at_offset(position.offset))?; | 16 | let token = pick_best(file.token_at_offset(position.offset))?; |
17 | let token = descend_into_macros(db, position.file_id, token); | 17 | let token = descend_into_macros(db, position.file_id, token); |
18 | 18 | ||
19 | let node = token.value.ancestors().find_map(|token| { | 19 | let node = token |
20 | token | 20 | .value |
21 | .ancestors() | 21 | .ancestors() |
22 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some()) | 22 | .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; |
23 | })?; | ||
24 | 23 | ||
25 | let analyzer = hir::SourceAnalyzer::new(db, token.with_value(&node), None); | 24 | let analyzer = hir::SourceAnalyzer::new(db, token.with_value(&node), None); |
26 | 25 | ||
27 | let ty: hir::Type = if let Some(ty) = | 26 | let ty: hir::Type = ast::Expr::cast(node.clone()) |
28 | ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e)) | 27 | .and_then(|e| analyzer.type_of(db, &e)) |
29 | { | 28 | .or_else(|| ast::Pat::cast(node.clone()).and_then(|p| analyzer.type_of_pat(db, &p)))?; |
30 | ty | ||
31 | } else if let Some(ty) = ast::Pat::cast(node.clone()).and_then(|p| analyzer.type_of_pat(db, &p)) | ||
32 | { | ||
33 | ty | ||
34 | } else { | ||
35 | return None; | ||
36 | }; | ||
37 | 29 | ||
38 | let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?; | 30 | let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?; |
39 | 31 | ||
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 59c86bbfa..2e598fdcd 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -758,7 +758,7 @@ pub fn handle_code_lens( | |||
758 | // Gather runnables | 758 | // Gather runnables |
759 | for runnable in world.analysis().runnables(file_id)? { | 759 | for runnable in world.analysis().runnables(file_id)? { |
760 | let title = match &runnable.kind { | 760 | let title = match &runnable.kind { |
761 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️Run Test", | 761 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️\u{fe0e}Run Test", |
762 | RunnableKind::Bench { .. } => "Run Bench", | 762 | RunnableKind::Bench { .. } => "Run Bench", |
763 | RunnableKind::Bin => "Run", | 763 | RunnableKind::Bin => "Run", |
764 | } | 764 | } |
diff --git a/crates/ra_parser/src/grammar/params.rs b/crates/ra_parser/src/grammar/params.rs index ed4f93347..272661b1d 100644 --- a/crates/ra_parser/src/grammar/params.rs +++ b/crates/ra_parser/src/grammar/params.rs | |||
@@ -114,8 +114,11 @@ fn value_parameter(p: &mut Parser, flavor: Flavor) { | |||
114 | // test fn_pointer_param_ident_path | 114 | // test fn_pointer_param_ident_path |
115 | // type Foo = fn(Bar::Baz); | 115 | // type Foo = fn(Bar::Baz); |
116 | // type Qux = fn(baz: Bar::Baz); | 116 | // type Qux = fn(baz: Bar::Baz); |
117 | |||
118 | // test fn_pointer_unnamed_arg | ||
119 | // type Foo = fn(_: bar); | ||
117 | Flavor::FnPointer => { | 120 | Flavor::FnPointer => { |
118 | if p.at(IDENT) && p.nth(1) == T![:] && !p.nth_at(1, T![::]) { | 121 | if (p.at(IDENT) || p.at(UNDERSCORE)) && p.nth(1) == T![:] && !p.nth_at(1, T![::]) { |
119 | patterns::pattern_single(p); | 122 | patterns::pattern_single(p); |
120 | types::ascription(p); | 123 | types::ascription(p); |
121 | } else { | 124 | } else { |
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index 69edc3c66..653d5bd14 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml | |||
@@ -19,3 +19,5 @@ ra_cfg = { path = "../ra_cfg" } | |||
19 | 19 | ||
20 | serde = { version = "1.0.89", features = ["derive"] } | 20 | serde = { version = "1.0.89", features = ["derive"] } |
21 | serde_json = "1.0.39" | 21 | serde_json = "1.0.39" |
22 | |||
23 | anyhow = "1.0.26" | ||
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 60cb8c1eb..22d226a74 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -2,14 +2,13 @@ | |||
2 | 2 | ||
3 | use std::path::{Path, PathBuf}; | 3 | use std::path::{Path, PathBuf}; |
4 | 4 | ||
5 | use anyhow::{Context, Result}; | ||
5 | use cargo_metadata::{CargoOpt, MetadataCommand}; | 6 | use cargo_metadata::{CargoOpt, MetadataCommand}; |
6 | use ra_arena::{impl_arena_id, Arena, RawId}; | 7 | use ra_arena::{impl_arena_id, Arena, RawId}; |
7 | use ra_db::Edition; | 8 | use ra_db::Edition; |
8 | use rustc_hash::FxHashMap; | 9 | use rustc_hash::FxHashMap; |
9 | use serde::Deserialize; | 10 | use serde::Deserialize; |
10 | 11 | ||
11 | use crate::Result; | ||
12 | |||
13 | /// `CargoWorkspace` represents the logical structure of, well, a Cargo | 12 | /// `CargoWorkspace` represents the logical structure of, well, a Cargo |
14 | /// workspace. It pretty closely mirrors `cargo metadata` output. | 13 | /// workspace. It pretty closely mirrors `cargo metadata` output. |
15 | /// | 14 | /// |
@@ -171,7 +170,9 @@ impl CargoWorkspace { | |||
171 | if let Some(parent) = cargo_toml.parent() { | 170 | if let Some(parent) = cargo_toml.parent() { |
172 | meta.current_dir(parent); | 171 | meta.current_dir(parent); |
173 | } | 172 | } |
174 | let meta = meta.exec().map_err(|e| format!("cargo metadata failed: {}", e))?; | 173 | let meta = meta.exec().with_context(|| { |
174 | format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) | ||
175 | })?; | ||
175 | let mut pkg_by_id = FxHashMap::default(); | 176 | let mut pkg_by_id = FxHashMap::default(); |
176 | let mut packages = Arena::default(); | 177 | let mut packages = Arena::default(); |
177 | let mut targets = Arena::default(); | 178 | let mut targets = Arena::default(); |
@@ -181,7 +182,9 @@ impl CargoWorkspace { | |||
181 | for meta_pkg in meta.packages { | 182 | for meta_pkg in meta.packages { |
182 | let cargo_metadata::Package { id, edition, name, manifest_path, .. } = meta_pkg; | 183 | let cargo_metadata::Package { id, edition, name, manifest_path, .. } = meta_pkg; |
183 | let is_member = ws_members.contains(&id); | 184 | let is_member = ws_members.contains(&id); |
184 | let edition = edition.parse::<Edition>()?; | 185 | let edition = edition |
186 | .parse::<Edition>() | ||
187 | .with_context(|| format!("Failed to parse edition {}", edition))?; | ||
185 | let pkg = packages.alloc(PackageData { | 188 | let pkg = packages.alloc(PackageData { |
186 | name, | 189 | name, |
187 | manifest: manifest_path, | 190 | manifest: manifest_path, |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index bc1d15406..fef405b7f 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -12,6 +12,7 @@ use std::{ | |||
12 | process::Command, | 12 | process::Command, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | use anyhow::{bail, Context, Result}; | ||
15 | use ra_cfg::CfgOptions; | 16 | use ra_cfg::CfgOptions; |
16 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; | 17 | use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; |
17 | use rustc_hash::FxHashMap; | 18 | use rustc_hash::FxHashMap; |
@@ -23,8 +24,6 @@ pub use crate::{ | |||
23 | sysroot::Sysroot, | 24 | sysroot::Sysroot, |
24 | }; | 25 | }; |
25 | 26 | ||
26 | pub type Result<T> = ::std::result::Result<T, Box<dyn Error + Send + Sync>>; | ||
27 | |||
28 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | 27 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
29 | pub struct CargoTomlNotFoundError(pub PathBuf); | 28 | pub struct CargoTomlNotFoundError(pub PathBuf); |
30 | 29 | ||
@@ -81,15 +80,36 @@ impl ProjectWorkspace { | |||
81 | ) -> Result<ProjectWorkspace> { | 80 | ) -> Result<ProjectWorkspace> { |
82 | match find_rust_project_json(path) { | 81 | match find_rust_project_json(path) { |
83 | Some(json_path) => { | 82 | Some(json_path) => { |
84 | let file = File::open(json_path)?; | 83 | let file = File::open(&json_path) |
84 | .with_context(|| format!("Failed to open json file {}", json_path.display()))?; | ||
85 | let reader = BufReader::new(file); | 85 | let reader = BufReader::new(file); |
86 | Ok(ProjectWorkspace::Json { project: from_reader(reader)? }) | 86 | Ok(ProjectWorkspace::Json { |
87 | project: from_reader(reader).with_context(|| { | ||
88 | format!("Failed to deserialize json file {}", json_path.display()) | ||
89 | })?, | ||
90 | }) | ||
87 | } | 91 | } |
88 | None => { | 92 | None => { |
89 | let cargo_toml = find_cargo_toml(path)?; | 93 | let cargo_toml = find_cargo_toml(path).with_context(|| { |
90 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)?; | 94 | format!("Failed to find Cargo.toml for path {}", path.display()) |
91 | let sysroot = | 95 | })?; |
92 | if with_sysroot { Sysroot::discover(&cargo_toml)? } else { Sysroot::default() }; | 96 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) |
97 | .with_context(|| { | ||
98 | format!( | ||
99 | "Failed to read Cargo metadata from Cargo.toml file {}", | ||
100 | cargo_toml.display() | ||
101 | ) | ||
102 | })?; | ||
103 | let sysroot = if with_sysroot { | ||
104 | Sysroot::discover(&cargo_toml).with_context(|| { | ||
105 | format!( | ||
106 | "Failed to find sysroot for Cargo.toml file {}", | ||
107 | cargo_toml.display() | ||
108 | ) | ||
109 | })? | ||
110 | } else { | ||
111 | Sysroot::default() | ||
112 | }; | ||
93 | Ok(ProjectWorkspace::Cargo { cargo, sysroot }) | 113 | Ok(ProjectWorkspace::Cargo { cargo, sysroot }) |
94 | } | 114 | } |
95 | } | 115 | } |
@@ -403,11 +423,20 @@ pub fn get_rustc_cfg_options() -> CfgOptions { | |||
403 | } | 423 | } |
404 | } | 424 | } |
405 | 425 | ||
406 | match (|| -> Result<_> { | 426 | match (|| -> Result<String> { |
407 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. | 427 | // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. |
408 | let output = Command::new("rustc").args(&["--print", "cfg", "-O"]).output()?; | 428 | let output = Command::new("rustc") |
429 | .args(&["--print", "cfg", "-O"]) | ||
430 | .output() | ||
431 | .context("Failed to get output from rustc --print cfg -O")?; | ||
409 | if !output.status.success() { | 432 | if !output.status.success() { |
410 | Err("failed to get rustc cfgs")?; | 433 | bail!( |
434 | "rustc --print cfg -O exited with exit code ({})", | ||
435 | output | ||
436 | .status | ||
437 | .code() | ||
438 | .map_or(String::from("no exit code"), |code| format!("{}", code)) | ||
439 | ); | ||
411 | } | 440 | } |
412 | Ok(String::from_utf8(output.stdout)?) | 441 | Ok(String::from_utf8(output.stdout)?) |
413 | })() { | 442 | })() { |
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index a23265fc0..7b9cc899c 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use anyhow::{anyhow, bail, Context, Result}; | ||
3 | use std::{ | 4 | use std::{ |
4 | env, | 5 | env, |
5 | path::{Path, PathBuf}, | 6 | path::{Path, PathBuf}, |
@@ -8,8 +9,6 @@ use std::{ | |||
8 | 9 | ||
9 | use ra_arena::{impl_arena_id, Arena, RawId}; | 10 | use ra_arena::{impl_arena_id, Arena, RawId}; |
10 | 11 | ||
11 | use crate::Result; | ||
12 | |||
13 | #[derive(Default, Debug, Clone)] | 12 | #[derive(Default, Debug, Clone)] |
14 | pub struct Sysroot { | 13 | pub struct Sysroot { |
15 | crates: Arena<SysrootCrate, SysrootCrateData>, | 14 | crates: Arena<SysrootCrate, SysrootCrateData>, |
@@ -51,7 +50,7 @@ impl Sysroot { | |||
51 | let src = try_find_src_path(cargo_toml)?; | 50 | let src = try_find_src_path(cargo_toml)?; |
52 | 51 | ||
53 | if !src.exists() { | 52 | if !src.exists() { |
54 | Err(format!( | 53 | Err(anyhow!( |
55 | "can't load standard library from sysroot\n\ | 54 | "can't load standard library from sysroot\n\ |
56 | {}\n\ | 55 | {}\n\ |
57 | (discovered via `rustc --print sysroot`)\n\ | 56 | (discovered via `rustc --print sysroot`)\n\ |
@@ -100,9 +99,14 @@ fn try_find_src_path(cargo_toml: &Path) -> Result<PathBuf> { | |||
100 | .current_dir(cargo_toml.parent().unwrap()) | 99 | .current_dir(cargo_toml.parent().unwrap()) |
101 | .args(&["--print", "sysroot"]) | 100 | .args(&["--print", "sysroot"]) |
102 | .output() | 101 | .output() |
103 | .map_err(|e| format!("rustc --print sysroot failed: {}", e))?; | 102 | .context("rustc --print sysroot failed")?; |
104 | if !rustc_output.status.success() { | 103 | if !rustc_output.status.success() { |
105 | Err("failed to locate sysroot")?; | 104 | match rustc_output.status.code() { |
105 | Some(code) => { | ||
106 | bail!("failed to locate sysroot: rustc --print sysroot exited with code {}", code) | ||
107 | } | ||
108 | None => bail!("failed to locate sysroot: rustc --print sysroot terminated by signal"), | ||
109 | }; | ||
106 | } | 110 | } |
107 | let stdout = String::from_utf8(rustc_output.stdout)?; | 111 | let stdout = String::from_utf8(rustc_output.stdout)?; |
108 | let sysroot_path = Path::new(stdout.trim()); | 112 | let sysroot_path = Path::new(stdout.trim()); |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index d3e8888bd..9cc7930f7 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -18,8 +18,8 @@ use crate::{ | |||
18 | pub use self::{ | 18 | pub use self::{ |
19 | expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, | 19 | expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, |
20 | extensions::{ | 20 | extensions::{ |
21 | FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind, TypeBoundKind, | 21 | AttrKind, FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind, |
22 | VisibilityKind, | 22 | TypeBoundKind, VisibilityKind, |
23 | }, | 23 | }, |
24 | generated::*, | 24 | generated::*, |
25 | tokens::*, | 25 | tokens::*, |
@@ -217,10 +217,7 @@ fn test_doc_comment_multi_line_block_strips_suffix() { | |||
217 | #[test] | 217 | #[test] |
218 | fn test_comments_preserve_trailing_whitespace() { | 218 | fn test_comments_preserve_trailing_whitespace() { |
219 | let file = SourceFile::parse( | 219 | let file = SourceFile::parse( |
220 | r#" | 220 | "\n/// Representation of a Realm. \n/// In the specification these are called Realm Records.\nstruct Realm {}", |
221 | /// Representation of a Realm. | ||
222 | /// In the specification these are called Realm Records. | ||
223 | struct Realm {}"#, | ||
224 | ) | 221 | ) |
225 | .ok() | 222 | .ok() |
226 | .unwrap(); | 223 | .unwrap(); |
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index 7dcf084de..44de4af89 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs | |||
@@ -37,6 +37,12 @@ fn text_of_first_token(node: &SyntaxNode) -> &SmolStr { | |||
37 | node.green().children().next().and_then(|it| it.into_token()).unwrap().text() | 37 | node.green().children().next().and_then(|it| it.into_token()).unwrap().text() |
38 | } | 38 | } |
39 | 39 | ||
40 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
41 | pub enum AttrKind { | ||
42 | Inner, | ||
43 | Outer, | ||
44 | } | ||
45 | |||
40 | impl ast::Attr { | 46 | impl ast::Attr { |
41 | pub fn as_simple_atom(&self) -> Option<SmolStr> { | 47 | pub fn as_simple_atom(&self) -> Option<SmolStr> { |
42 | match self.input() { | 48 | match self.input() { |
@@ -71,6 +77,18 @@ impl ast::Attr { | |||
71 | _ => None, | 77 | _ => None, |
72 | } | 78 | } |
73 | } | 79 | } |
80 | |||
81 | pub fn kind(&self) -> AttrKind { | ||
82 | let first_token = self.syntax().first_token(); | ||
83 | let first_token_kind = first_token.as_ref().map(SyntaxToken::kind); | ||
84 | let second_token_kind = | ||
85 | first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind); | ||
86 | |||
87 | match (first_token_kind, second_token_kind) { | ||
88 | (Some(SyntaxKind::POUND), Some(SyntaxKind::EXCL)) => AttrKind::Inner, | ||
89 | _ => AttrKind::Outer, | ||
90 | } | ||
91 | } | ||
74 | } | 92 | } |
75 | 93 | ||
76 | #[derive(Debug, Clone, PartialEq, Eq)] | 94 | #[derive(Debug, Clone, PartialEq, Eq)] |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rs b/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rs new file mode 100644 index 000000000..1ebbe5b03 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rs | |||
@@ -0,0 +1 @@ | |||
type Foo = fn(_: bar); | |||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.txt b/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.txt new file mode 100644 index 000000000..52d8f21a4 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | SOURCE_FILE@[0; 23) | ||
2 | TYPE_ALIAS_DEF@[0; 22) | ||
3 | TYPE_KW@[0; 4) "type" | ||
4 | WHITESPACE@[4; 5) " " | ||
5 | NAME@[5; 8) | ||
6 | IDENT@[5; 8) "Foo" | ||
7 | WHITESPACE@[8; 9) " " | ||
8 | EQ@[9; 10) "=" | ||
9 | WHITESPACE@[10; 11) " " | ||
10 | FN_POINTER_TYPE@[11; 21) | ||
11 | FN_KW@[11; 13) "fn" | ||
12 | PARAM_LIST@[13; 21) | ||
13 | L_PAREN@[13; 14) "(" | ||
14 | PARAM@[14; 20) | ||
15 | PLACEHOLDER_PAT@[14; 15) | ||
16 | UNDERSCORE@[14; 15) "_" | ||
17 | COLON@[15; 16) ":" | ||
18 | WHITESPACE@[16; 17) " " | ||
19 | PATH_TYPE@[17; 20) | ||
20 | PATH@[17; 20) | ||
21 | PATH_SEGMENT@[17; 20) | ||
22 | NAME_REF@[17; 20) | ||
23 | IDENT@[17; 20) "bar" | ||
24 | R_PAREN@[20; 21) ")" | ||
25 | SEMI@[21; 22) ";" | ||
26 | WHITESPACE@[22; 23) "\n" | ||