aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/ast_editor.rs30
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_assists/src/move_bounds.rs135
-rw-r--r--crates/ra_hir/src/marks.rs1
-rw-r--r--crates/ra_hir/src/nameres.rs3
-rw-r--r--crates/ra_hir/src/nameres/collector.rs48
-rw-r--r--crates/ra_hir/src/nameres/raw.rs13
-rw-r--r--crates/ra_hir/src/nameres/tests/macros.rs64
-rw-r--r--crates/ra_project_model/src/lib.rs2
-rw-r--r--crates/ra_syntax/src/ast/generated.rs1
-rw-r--r--crates/ra_syntax/src/grammar.ron1
11 files changed, 287 insertions, 13 deletions
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs
index 048478662..a710edce8 100644
--- a/crates/ra_assists/src/ast_editor.rs
+++ b/crates/ra_assists/src/ast_editor.rs
@@ -297,6 +297,11 @@ impl AstBuilder<ast::Path> {
297 ast_node_from_file_text(text) 297 ast_node_from_file_text(text)
298 } 298 }
299 299
300 pub fn from_name(name: ast::Name) -> ast::Path {
301 let name = name.syntax().to_string();
302 Self::from_text(name.as_str())
303 }
304
300 pub fn from_pieces(enum_name: ast::Name, var_name: ast::Name) -> ast::Path { 305 pub fn from_pieces(enum_name: ast::Name, var_name: ast::Name) -> ast::Path {
301 Self::from_text(&format!("{}::{}", enum_name.syntax(), var_name.syntax())) 306 Self::from_text(&format!("{}::{}", enum_name.syntax(), var_name.syntax()))
302 } 307 }
@@ -380,6 +385,31 @@ impl AstBuilder<ast::MatchArmList> {
380 } 385 }
381} 386}
382 387
388impl AstBuilder<ast::WherePred> {
389 fn from_text(text: &str) -> ast::WherePred {
390 ast_node_from_file_text(&format!("fn f() where {} {{ }}", text))
391 }
392
393 pub fn from_pieces(
394 path: ast::Path,
395 bounds: impl Iterator<Item = ast::TypeBound>,
396 ) -> ast::WherePred {
397 let bounds = bounds.map(|b| b.syntax().to_string()).collect::<Vec<_>>().join(" + ");
398 Self::from_text(&format!("{}: {}", path.syntax(), bounds))
399 }
400}
401
402impl AstBuilder<ast::WhereClause> {
403 fn from_text(text: &str) -> ast::WhereClause {
404 ast_node_from_file_text(&format!("fn f() where {} {{ }}", text))
405 }
406
407 pub fn from_predicates(preds: impl Iterator<Item = ast::WherePred>) -> ast::WhereClause {
408 let preds = preds.map(|p| p.syntax().to_string()).collect::<Vec<_>>().join(", ");
409 Self::from_text(preds.as_str())
410 }
411}
412
383fn ast_node_from_file_text<N: AstNode>(text: &str) -> N { 413fn ast_node_from_file_text<N: AstNode>(text: &str) -> N {
384 let parse = SourceFile::parse(text); 414 let parse = SourceFile::parse(text);
385 let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap(); 415 let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap();
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 03eec73ad..10ccc345c 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -102,6 +102,7 @@ mod remove_dbg;
102pub mod auto_import; 102pub mod auto_import;
103mod add_missing_impl_members; 103mod add_missing_impl_members;
104mod move_guard; 104mod move_guard;
105mod move_bounds;
105 106
106fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { 107fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
107 &[ 108 &[
@@ -123,6 +124,7 @@ fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assis
123 inline_local_variable::inline_local_varialbe, 124 inline_local_variable::inline_local_varialbe,
124 move_guard::move_guard_to_arm_body, 125 move_guard::move_guard_to_arm_body,
125 move_guard::move_arm_cond_to_match_guard, 126 move_guard::move_arm_cond_to_match_guard,
127 move_bounds::move_bounds_to_where_clause,
126 ] 128 ]
127} 129}
128 130
diff --git a/crates/ra_assists/src/move_bounds.rs b/crates/ra_assists/src/move_bounds.rs
new file mode 100644
index 000000000..526de1d98
--- /dev/null
+++ b/crates/ra_assists/src/move_bounds.rs
@@ -0,0 +1,135 @@
1use hir::db::HirDatabase;
2use ra_syntax::{
3 ast::{self, AstNode, NameOwner, TypeBoundsOwner},
4 SyntaxElement,
5 SyntaxKind::*,
6 TextRange,
7};
8
9use crate::{ast_editor::AstBuilder, Assist, AssistCtx, AssistId};
10
11pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
12 let type_param_list = ctx.node_at_offset::<ast::TypeParamList>()?;
13
14 let mut type_params = type_param_list.type_params();
15 if type_params.all(|p| p.type_bound_list().is_none()) {
16 return None;
17 }
18
19 let parent = type_param_list.syntax().parent()?;
20 if parent.children_with_tokens().find(|it| it.kind() == WHERE_CLAUSE).is_some() {
21 return None;
22 }
23
24 let anchor: SyntaxElement = match parent.kind() {
25 FN_DEF => ast::FnDef::cast(parent)?.body()?.syntax().clone().into(),
26 TRAIT_DEF => ast::TraitDef::cast(parent)?.item_list()?.syntax().clone().into(),
27 IMPL_BLOCK => ast::ImplBlock::cast(parent)?.item_list()?.syntax().clone().into(),
28 ENUM_DEF => ast::EnumDef::cast(parent)?.variant_list()?.syntax().clone().into(),
29 STRUCT_DEF => parent
30 .children_with_tokens()
31 .find(|it| it.kind() == RECORD_FIELD_DEF_LIST || it.kind() == SEMI)?,
32 _ => return None,
33 };
34
35 ctx.add_action(
36 AssistId("move_bounds_to_where_clause"),
37 "move_bounds_to_where_clause",
38 |edit| {
39 let type_params = type_param_list.type_params().collect::<Vec<_>>();
40
41 for param in &type_params {
42 if let Some(bounds) = param.type_bound_list() {
43 let colon = param
44 .syntax()
45 .children_with_tokens()
46 .find(|it| it.kind() == COLON)
47 .unwrap();
48 let start = colon.text_range().start();
49 let end = bounds.syntax().text_range().end();
50 edit.delete(TextRange::from_to(start, end));
51 }
52 }
53
54 let predicates = type_params.iter().filter_map(build_predicate);
55 let where_clause = AstBuilder::<ast::WhereClause>::from_predicates(predicates);
56
57 let to_insert = match anchor.prev_sibling_or_token() {
58 Some(ref elem) if elem.kind() == WHITESPACE => {
59 format!("{} ", where_clause.syntax())
60 }
61 _ => format!(" {}", where_clause.syntax()),
62 };
63 edit.insert(anchor.text_range().start(), to_insert);
64 edit.target(type_param_list.syntax().text_range());
65 },
66 );
67
68 ctx.build()
69}
70
71fn build_predicate(param: &ast::TypeParam) -> Option<ast::WherePred> {
72 let path = AstBuilder::<ast::Path>::from_name(param.name()?);
73 let predicate =
74 AstBuilder::<ast::WherePred>::from_pieces(path, param.type_bound_list()?.bounds());
75 Some(predicate)
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 use crate::helpers::check_assist;
83
84 #[test]
85 fn move_bounds_to_where_clause_fn() {
86 check_assist(
87 move_bounds_to_where_clause,
88 r#"
89 fn foo<T: u32, <|>F: FnOnce(T) -> T>() {}
90 "#,
91 r#"
92 fn foo<T, <|>F>() where T: u32, F: FnOnce(T) -> T {}
93 "#,
94 );
95 }
96
97 #[test]
98 fn move_bounds_to_where_clause_impl() {
99 check_assist(
100 move_bounds_to_where_clause,
101 r#"
102 impl<U: u32, <|>T> A<U, T> {}
103 "#,
104 r#"
105 impl<U, <|>T> A<U, T> where U: u32 {}
106 "#,
107 );
108 }
109
110 #[test]
111 fn move_bounds_to_where_clause_struct() {
112 check_assist(
113 move_bounds_to_where_clause,
114 r#"
115 struct A<<|>T: Iterator<Item = u32>> {}
116 "#,
117 r#"
118 struct A<<|>T> where T: Iterator<Item = u32> {}
119 "#,
120 );
121 }
122
123 #[test]
124 fn move_bounds_to_where_clause_tuple_struct() {
125 check_assist(
126 move_bounds_to_where_clause,
127 r#"
128 struct Pair<<|>T: u32>(T, T);
129 "#,
130 r#"
131 struct Pair<<|>T>(T, T) where T: u32;
132 "#,
133 );
134 }
135}
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs
index 5b15eee90..2e1d35c8c 100644
--- a/crates/ra_hir/src/marks.rs
+++ b/crates/ra_hir/src/marks.rs
@@ -11,4 +11,5 @@ test_utils::marks!(
11 match_ergonomics_ref 11 match_ergonomics_ref
12 trait_resolution_on_fn_type 12 trait_resolution_on_fn_type
13 infer_while_let 13 infer_while_let
14 macro_rules_from_other_crates_are_visible_with_macro_use
14); 15);
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs
index bbdc606cd..f69179bf6 100644
--- a/crates/ra_hir/src/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -101,6 +101,8 @@ pub struct CrateDefMap {
101 /// However, do we want to put it as a global variable? 101 /// However, do we want to put it as a global variable?
102 poison_macros: FxHashSet<MacroDefId>, 102 poison_macros: FxHashSet<MacroDefId>,
103 103
104 exported_macros: FxHashMap<Name, MacroDefId>,
105
104 diagnostics: Vec<DefDiagnostic>, 106 diagnostics: Vec<DefDiagnostic>,
105} 107}
106 108
@@ -245,6 +247,7 @@ impl CrateDefMap {
245 root, 247 root,
246 modules, 248 modules,
247 poison_macros: FxHashSet::default(), 249 poison_macros: FxHashSet::default(),
250 exported_macros: FxHashMap::default(),
248 diagnostics: Vec::new(), 251 diagnostics: Vec::new(),
249 } 252 }
250 }; 253 };
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs
index 7da2dcdff..5d1c42926 100644
--- a/crates/ra_hir/src/nameres/collector.rs
+++ b/crates/ra_hir/src/nameres/collector.rs
@@ -157,11 +157,42 @@ where
157 // crate root, even if the parent modules is **not** visible. 157 // crate root, even if the parent modules is **not** visible.
158 if export { 158 if export {
159 self.update(self.def_map.root, None, &[(name.clone(), def.clone())]); 159 self.update(self.def_map.root, None, &[(name.clone(), def.clone())]);
160
161 // Exported macros are collected in crate level ready for
162 // glob import with `#[macro_use]`.
163 self.def_map.exported_macros.insert(name.clone(), macro_id);
160 } 164 }
161 self.update(module_id, None, &[(name.clone(), def)]); 165 self.update(module_id, None, &[(name.clone(), def)]);
162 self.global_macro_scope.insert(name, macro_id); 166 self.global_macro_scope.insert(name, macro_id);
163 } 167 }
164 168
169 /// Import macros from `#[macro_use] extern crate`.
170 ///
171 /// They are non-scoped, and will only be inserted into mutable `global_macro_scope`.
172 fn import_macros_from_extern_crate(&mut self, import: &raw::ImportData) {
173 log::debug!(
174 "importing macros from extern crate: {:?} ({:?})",
175 import,
176 self.def_map.edition,
177 );
178
179 let res = self.def_map.resolve_name_in_extern_prelude(
180 &import
181 .path
182 .as_ident()
183 .expect("extern crate should have been desugared to one-element path"),
184 );
185
186 if let Some(ModuleDef::Module(m)) = res.take_types() {
187 tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use);
188
189 let item_map = self.db.crate_def_map(m.krate);
190 for (name, &macro_id) in &item_map.exported_macros {
191 self.global_macro_scope.insert(name.clone(), macro_id);
192 }
193 }
194 }
195
165 fn resolve_imports(&mut self) -> ReachedFixedPoint { 196 fn resolve_imports(&mut self) -> ReachedFixedPoint {
166 let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); 197 let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
167 let mut resolved = Vec::new(); 198 let mut resolved = Vec::new();
@@ -494,11 +525,17 @@ where
494 for item in items { 525 for item in items {
495 match *item { 526 match *item {
496 raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), 527 raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]),
497 raw::RawItem::Import(import) => self.def_collector.unresolved_imports.push(( 528 raw::RawItem::Import(import_id) => {
498 self.module_id, 529 let import = self.raw_items[import_id].clone();
499 import, 530 // This should be processed eagerly instead of deferred to resolving.
500 self.raw_items[import].clone(), 531 // Otherwise, since it will only mutate `global_macro_scope`
501 )), 532 // without `update` names in `mod`s, unresolved macros cannot be expanded.
533 if import.is_extern_crate && import.is_macro_use {
534 self.def_collector.import_macros_from_extern_crate(&import);
535 }
536
537 self.def_collector.unresolved_imports.push((self.module_id, import_id, import));
538 }
502 raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), 539 raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]),
503 raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), 540 raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
504 } 541 }
@@ -860,6 +897,7 @@ mod tests {
860 root, 897 root,
861 modules, 898 modules,
862 poison_macros: FxHashSet::default(), 899 poison_macros: FxHashSet::default(),
900 exported_macros: FxHashMap::default(),
863 diagnostics: Vec::new(), 901 diagnostics: Vec::new(),
864 } 902 }
865 }; 903 };
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs
index 2f973359f..129b047eb 100644
--- a/crates/ra_hir/src/nameres/raw.rs
+++ b/crates/ra_hir/src/nameres/raw.rs
@@ -154,6 +154,7 @@ pub struct ImportData {
154 pub(super) is_glob: bool, 154 pub(super) is_glob: bool,
155 pub(super) is_prelude: bool, 155 pub(super) is_prelude: bool,
156 pub(super) is_extern_crate: bool, 156 pub(super) is_extern_crate: bool,
157 pub(super) is_macro_use: bool,
157} 158}
158 159
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -293,8 +294,14 @@ impl RawItemsCollector {
293 let is_prelude = use_item.has_atom_attr("prelude_import"); 294 let is_prelude = use_item.has_atom_attr("prelude_import");
294 295
295 Path::expand_use_item(&use_item, |path, use_tree, is_glob, alias| { 296 Path::expand_use_item(&use_item, |path, use_tree, is_glob, alias| {
296 let import_data = 297 let import_data = ImportData {
297 ImportData { path, alias, is_glob, is_prelude, is_extern_crate: false }; 298 path,
299 alias,
300 is_glob,
301 is_prelude,
302 is_extern_crate: false,
303 is_macro_use: false,
304 };
298 self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree))); 305 self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree)));
299 }) 306 })
300 } 307 }
@@ -307,12 +314,14 @@ impl RawItemsCollector {
307 if let Some(name_ref) = extern_crate.name_ref() { 314 if let Some(name_ref) = extern_crate.name_ref() {
308 let path = Path::from_name_ref(&name_ref); 315 let path = Path::from_name_ref(&name_ref);
309 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); 316 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
317 let is_macro_use = extern_crate.has_atom_attr("macro_use");
310 let import_data = ImportData { 318 let import_data = ImportData {
311 path, 319 path,
312 alias, 320 alias,
313 is_glob: false, 321 is_glob: false,
314 is_prelude: false, 322 is_prelude: false,
315 is_extern_crate: true, 323 is_extern_crate: true,
324 is_macro_use,
316 }; 325 };
317 self.push_import(current_module, import_data, Either::B(AstPtr::new(&extern_crate))); 326 self.push_import(current_module, import_data, Either::B(AstPtr::new(&extern_crate)));
318 } 327 }
diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs
index 631df2cef..ebfefe273 100644
--- a/crates/ra_hir/src/nameres/tests/macros.rs
+++ b/crates/ra_hir/src/nameres/tests/macros.rs
@@ -99,14 +99,14 @@ fn macro_rules_from_other_crates_are_visible() {
99fn unexpanded_macro_should_expand_by_fixedpoint_loop() { 99fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
100 let map = def_map_with_crate_graph( 100 let map = def_map_with_crate_graph(
101 " 101 "
102 //- /main.rs 102 //- /main.rs
103 macro_rules! baz { 103 macro_rules! baz {
104 () => { 104 () => {
105 use foo::bar; 105 use foo::bar;
106 } 106 }
107 } 107 }
108 108
109 foo!(); 109 foo!();
110 bar!(); 110 bar!();
111 baz!(); 111 baz!();
112 112
@@ -114,7 +114,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
114 #[macro_export] 114 #[macro_export]
115 macro_rules! foo { 115 macro_rules! foo {
116 () => { 116 () => {
117 struct Foo { field: u32 } 117 struct Foo { field: u32 }
118 } 118 }
119 } 119 }
120 #[macro_export] 120 #[macro_export]
@@ -137,3 +137,57 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
137 ⋮foo: m 137 ⋮foo: m
138 "###); 138 "###);
139} 139}
140
141#[test]
142fn macro_rules_from_other_crates_are_visible_with_macro_use() {
143 covers!(macro_rules_from_other_crates_are_visible_with_macro_use);
144 let map = def_map_with_crate_graph(
145 "
146 //- /main.rs
147 #[macro_use]
148 extern crate foo;
149
150 structs!(Foo);
151 structs_priv!(Bar);
152 structs_not_exported!(MacroNotResolved1);
153 crate::structs!(MacroNotResolved2);
154
155 mod bar;
156
157 //- /bar.rs
158 structs!(Baz);
159 crate::structs!(MacroNotResolved3);
160
161 //- /lib.rs
162 #[macro_export]
163 macro_rules! structs {
164 ($i:ident) => { struct $i; }
165 }
166
167 macro_rules! structs_not_exported {
168 ($i:ident) => { struct $i; }
169 }
170
171 mod priv_mod {
172 #[macro_export]
173 macro_rules! structs_priv {
174 ($i:ident) => { struct $i; }
175 }
176 }
177 ",
178 crate_graph! {
179 "main": ("/main.rs", ["foo"]),
180 "foo": ("/lib.rs", []),
181 },
182 );
183 assert_snapshot!(map, @r###"
184 ⋮crate
185 ⋮Bar: t v
186 ⋮Foo: t v
187 ⋮bar: t
188 ⋮foo: t
189
190 ⋮crate::bar
191 ⋮Baz: t v
192 "###);
193}
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 676dc4941..9b2f534e7 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -153,7 +153,7 @@ impl ProjectWorkspace {
153 if let Some(file_id) = load(krate.root(&sysroot)) { 153 if let Some(file_id) = load(krate.root(&sysroot)) {
154 sysroot_crates.insert( 154 sysroot_crates.insert(
155 krate, 155 krate,
156 crate_graph.add_crate_root(file_id, Edition::Edition2015), 156 crate_graph.add_crate_root(file_id, Edition::Edition2018),
157 ); 157 );
158 } 158 }
159 } 159 }
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index e2a92ae60..bcf753f78 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -934,6 +934,7 @@ impl AstNode for ExternCrateItem {
934 &self.syntax 934 &self.syntax
935 } 935 }
936} 936}
937impl ast::AttrsOwner for ExternCrateItem {}
937impl ExternCrateItem { 938impl ExternCrateItem {
938 pub fn name_ref(&self) -> Option<NameRef> { 939 pub fn name_ref(&self) -> Option<NameRef> {
939 AstChildren::new(&self.syntax).next() 940 AstChildren::new(&self.syntax).next()
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index c14ee0e85..3e6c2d3f3 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -669,6 +669,7 @@ Grammar(
669 collections: [("use_trees", "UseTree")] 669 collections: [("use_trees", "UseTree")]
670 ), 670 ),
671 "ExternCrateItem": ( 671 "ExternCrateItem": (
672 traits: ["AttrsOwner"],
672 options: ["NameRef", "Alias"], 673 options: ["NameRef", "Alias"],
673 ), 674 ),
674 "ArgList": ( 675 "ArgList": (