diff options
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | crates/ra_hir_def/src/body/lower.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/collector.rs | 206 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/builtin_macro.rs | 19 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer/coerce.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/coercion.rs | 37 | ||||
-rw-r--r-- | crates/ra_ide/src/goto_definition.rs | 3 | ||||
-rw-r--r-- | docs/user/README.md | 17 | ||||
-rw-r--r-- | editors/code/package-lock.json | 5 | ||||
-rw-r--r-- | editors/code/package.json | 1 | ||||
-rw-r--r-- | editors/code/src/extension.ts | 12 | ||||
-rw-r--r-- | editors/code/src/server.ts | 11 |
13 files changed, 160 insertions, 171 deletions
@@ -37,7 +37,8 @@ $ cargo xtask install | |||
37 | $ cargo xtask install --server | 37 | $ cargo xtask install --server |
38 | ``` | 38 | ``` |
39 | 39 | ||
40 | For non-standard setup of VS Code and other editors, see [./docs/user](./docs/user). | 40 | For non-standard setup of VS Code and other editors, or if the language server |
41 | cannot start, see [./docs/user](./docs/user). | ||
41 | 42 | ||
42 | ## Documentation | 43 | ## Documentation |
43 | 44 | ||
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 71c08f024..cc068ff94 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs | |||
@@ -437,9 +437,7 @@ where | |||
437 | None => self.alloc_expr(Expr::Missing, syntax_ptr), | 437 | None => self.alloc_expr(Expr::Missing, syntax_ptr), |
438 | } | 438 | } |
439 | } | 439 | } |
440 | 440 | // FIXME expand to statements in statement position | |
441 | // FIXME implement HIR for these: | ||
442 | ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
443 | ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) { | 441 | ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) { |
444 | Some((mark, expansion)) => { | 442 | Some((mark, expansion)) => { |
445 | let id = self.collect_expr(expansion); | 443 | let id = self.collect_expr(expansion); |
@@ -448,6 +446,9 @@ where | |||
448 | } | 446 | } |
449 | None => self.alloc_expr(Expr::Missing, syntax_ptr), | 447 | None => self.alloc_expr(Expr::Missing, syntax_ptr), |
450 | }, | 448 | }, |
449 | |||
450 | // FIXME implement HIR for these: | ||
451 | ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
451 | } | 452 | } |
452 | } | 453 | } |
453 | 454 | ||
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 3ff071f9e..a80067979 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -12,7 +12,7 @@ use hir_expand::{ | |||
12 | use ra_cfg::CfgOptions; | 12 | use ra_cfg::CfgOptions; |
13 | use ra_db::{CrateId, FileId}; | 13 | use ra_db::{CrateId, FileId}; |
14 | use ra_syntax::ast; | 14 | use ra_syntax::ast; |
15 | use rustc_hash::{FxHashMap, FxHashSet}; | 15 | use rustc_hash::FxHashMap; |
16 | use test_utils::tested_by; | 16 | use test_utils::tested_by; |
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
@@ -63,42 +63,12 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
63 | unexpanded_macros: Vec::new(), | 63 | unexpanded_macros: Vec::new(), |
64 | unexpanded_attribute_macros: Vec::new(), | 64 | unexpanded_attribute_macros: Vec::new(), |
65 | mod_dirs: FxHashMap::default(), | 65 | mod_dirs: FxHashMap::default(), |
66 | macro_stack_monitor: MacroStackMonitor::default(), | ||
67 | poison_macros: FxHashSet::default(), | ||
68 | cfg_options, | 66 | cfg_options, |
69 | }; | 67 | }; |
70 | collector.collect(); | 68 | collector.collect(); |
71 | collector.finish() | 69 | collector.finish() |
72 | } | 70 | } |
73 | 71 | ||
74 | #[derive(Default)] | ||
75 | struct MacroStackMonitor { | ||
76 | counts: FxHashMap<MacroDefId, u32>, | ||
77 | |||
78 | /// Mainly use for test | ||
79 | validator: Option<Box<dyn Fn(u32) -> bool>>, | ||
80 | } | ||
81 | |||
82 | impl MacroStackMonitor { | ||
83 | fn increase(&mut self, macro_def_id: MacroDefId) { | ||
84 | *self.counts.entry(macro_def_id).or_default() += 1; | ||
85 | } | ||
86 | |||
87 | fn decrease(&mut self, macro_def_id: MacroDefId) { | ||
88 | *self.counts.entry(macro_def_id).or_default() -= 1; | ||
89 | } | ||
90 | |||
91 | fn is_poison(&self, macro_def_id: MacroDefId) -> bool { | ||
92 | let cur = *self.counts.get(¯o_def_id).unwrap_or(&0); | ||
93 | |||
94 | if let Some(validator) = &self.validator { | ||
95 | validator(cur) | ||
96 | } else { | ||
97 | cur > 100 | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | |||
102 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | 72 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
103 | enum PartialResolvedImport { | 73 | enum PartialResolvedImport { |
104 | /// None of any namespaces is resolved | 74 | /// None of any namespaces is resolved |
@@ -127,6 +97,14 @@ struct ImportDirective { | |||
127 | status: PartialResolvedImport, | 97 | status: PartialResolvedImport, |
128 | } | 98 | } |
129 | 99 | ||
100 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
101 | struct MacroDirective { | ||
102 | module_id: LocalModuleId, | ||
103 | ast_id: AstId<ast::MacroCall>, | ||
104 | path: Path, | ||
105 | legacy: Option<MacroCallId>, | ||
106 | } | ||
107 | |||
130 | /// Walks the tree of module recursively | 108 | /// Walks the tree of module recursively |
131 | struct DefCollector<'a, DB> { | 109 | struct DefCollector<'a, DB> { |
132 | db: &'a DB, | 110 | db: &'a DB, |
@@ -134,25 +112,9 @@ struct DefCollector<'a, DB> { | |||
134 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, LocalImportId)>>, | 112 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, LocalImportId)>>, |
135 | unresolved_imports: Vec<ImportDirective>, | 113 | unresolved_imports: Vec<ImportDirective>, |
136 | resolved_imports: Vec<ImportDirective>, | 114 | resolved_imports: Vec<ImportDirective>, |
137 | unexpanded_macros: Vec<(LocalModuleId, AstId<ast::MacroCall>, Path)>, | 115 | unexpanded_macros: Vec<MacroDirective>, |
138 | unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, Path)>, | 116 | unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, Path)>, |
139 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | 117 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, |
140 | |||
141 | /// Some macro use `$tt:tt which mean we have to handle the macro perfectly | ||
142 | /// To prevent stack overflow, we add a deep counter here for prevent that. | ||
143 | macro_stack_monitor: MacroStackMonitor, | ||
144 | /// Some macros are not well-behavior, which leads to infinite loop | ||
145 | /// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } } | ||
146 | /// We mark it down and skip it in collector | ||
147 | /// | ||
148 | /// FIXME: | ||
149 | /// Right now it only handle a poison macro in a single crate, | ||
150 | /// such that if other crate try to call that macro, | ||
151 | /// the whole process will do again until it became poisoned in that crate. | ||
152 | /// We should handle this macro set globally | ||
153 | /// However, do we want to put it as a global variable? | ||
154 | poison_macros: FxHashSet<MacroDefId>, | ||
155 | |||
156 | cfg_options: &'a CfgOptions, | 118 | cfg_options: &'a CfgOptions, |
157 | } | 119 | } |
158 | 120 | ||
@@ -556,18 +518,24 @@ where | |||
556 | std::mem::replace(&mut self.unexpanded_attribute_macros, Vec::new()); | 518 | std::mem::replace(&mut self.unexpanded_attribute_macros, Vec::new()); |
557 | let mut resolved = Vec::new(); | 519 | let mut resolved = Vec::new(); |
558 | let mut res = ReachedFixedPoint::Yes; | 520 | let mut res = ReachedFixedPoint::Yes; |
559 | macros.retain(|(module_id, ast_id, path)| { | 521 | macros.retain(|directive| { |
522 | if let Some(call_id) = directive.legacy { | ||
523 | res = ReachedFixedPoint::No; | ||
524 | resolved.push((directive.module_id, call_id)); | ||
525 | return false; | ||
526 | } | ||
527 | |||
560 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | 528 | let resolved_res = self.def_map.resolve_path_fp_with_macro( |
561 | self.db, | 529 | self.db, |
562 | ResolveMode::Other, | 530 | ResolveMode::Other, |
563 | *module_id, | 531 | directive.module_id, |
564 | path, | 532 | &directive.path, |
565 | BuiltinShadowMode::Module, | 533 | BuiltinShadowMode::Module, |
566 | ); | 534 | ); |
567 | 535 | ||
568 | if let Some(def) = resolved_res.resolved_def.take_macros() { | 536 | if let Some(def) = resolved_res.resolved_def.take_macros() { |
569 | let call_id = def.as_call_id(self.db, MacroCallKind::FnLike(*ast_id)); | 537 | let call_id = def.as_call_id(self.db, MacroCallKind::FnLike(directive.ast_id)); |
570 | resolved.push((*module_id, call_id, def)); | 538 | resolved.push((directive.module_id, call_id)); |
571 | res = ReachedFixedPoint::No; | 539 | res = ReachedFixedPoint::No; |
572 | return false; | 540 | return false; |
573 | } | 541 | } |
@@ -579,7 +547,7 @@ where | |||
579 | 547 | ||
580 | if let Some(def) = resolved_res { | 548 | if let Some(def) = resolved_res { |
581 | let call_id = def.as_call_id(self.db, MacroCallKind::Attr(*ast_id)); | 549 | let call_id = def.as_call_id(self.db, MacroCallKind::Attr(*ast_id)); |
582 | resolved.push((*module_id, call_id, def)); | 550 | resolved.push((*module_id, call_id)); |
583 | res = ReachedFixedPoint::No; | 551 | res = ReachedFixedPoint::No; |
584 | return false; | 552 | return false; |
585 | } | 553 | } |
@@ -590,8 +558,8 @@ where | |||
590 | self.unexpanded_macros = macros; | 558 | self.unexpanded_macros = macros; |
591 | self.unexpanded_attribute_macros = attribute_macros; | 559 | self.unexpanded_attribute_macros = attribute_macros; |
592 | 560 | ||
593 | for (module_id, macro_call_id, macro_def_id) in resolved { | 561 | for (module_id, macro_call_id) in resolved { |
594 | self.collect_macro_expansion(module_id, macro_call_id, macro_def_id); | 562 | self.collect_macro_expansion(module_id, macro_call_id); |
595 | } | 563 | } |
596 | 564 | ||
597 | res | 565 | res |
@@ -611,36 +579,18 @@ where | |||
611 | None | 579 | None |
612 | } | 580 | } |
613 | 581 | ||
614 | fn collect_macro_expansion( | 582 | fn collect_macro_expansion(&mut self, module_id: LocalModuleId, macro_call_id: MacroCallId) { |
615 | &mut self, | 583 | let file_id: HirFileId = macro_call_id.as_file(); |
616 | module_id: LocalModuleId, | 584 | let raw_items = self.db.raw_items(file_id); |
617 | macro_call_id: MacroCallId, | 585 | let mod_dir = self.mod_dirs[&module_id].clone(); |
618 | macro_def_id: MacroDefId, | 586 | ModCollector { |
619 | ) { | 587 | def_collector: &mut *self, |
620 | if self.poison_macros.contains(¯o_def_id) { | 588 | file_id, |
621 | return; | 589 | module_id, |
622 | } | 590 | raw_items: &raw_items, |
623 | 591 | mod_dir, | |
624 | self.macro_stack_monitor.increase(macro_def_id); | ||
625 | |||
626 | if !self.macro_stack_monitor.is_poison(macro_def_id) { | ||
627 | let file_id: HirFileId = macro_call_id.as_file(); | ||
628 | let raw_items = self.db.raw_items(file_id); | ||
629 | let mod_dir = self.mod_dirs[&module_id].clone(); | ||
630 | ModCollector { | ||
631 | def_collector: &mut *self, | ||
632 | file_id, | ||
633 | module_id, | ||
634 | raw_items: &raw_items, | ||
635 | mod_dir, | ||
636 | } | ||
637 | .collect(raw_items.items()); | ||
638 | } else { | ||
639 | log::error!("Too deep macro expansion: {:?}", macro_call_id); | ||
640 | self.poison_macros.insert(macro_def_id); | ||
641 | } | 592 | } |
642 | 593 | .collect(raw_items.items()); | |
643 | self.macro_stack_monitor.decrease(macro_def_id); | ||
644 | } | 594 | } |
645 | 595 | ||
646 | fn finish(self) -> CrateDefMap { | 596 | fn finish(self) -> CrateDefMap { |
@@ -908,15 +858,20 @@ where | |||
908 | return; | 858 | return; |
909 | } | 859 | } |
910 | 860 | ||
911 | // Case 2: try to resolve in legacy scope and expand macro_rules, triggering | 861 | // Case 2: try to resolve in legacy scope and expand macro_rules |
912 | // recursive item collection. | ||
913 | if let Some(macro_def) = mac.path.as_ident().and_then(|name| { | 862 | if let Some(macro_def) = mac.path.as_ident().and_then(|name| { |
914 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) | 863 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) |
915 | }) { | 864 | }) { |
916 | let macro_call_id = | 865 | let macro_call_id = |
917 | macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id)); | 866 | macro_def.as_call_id(self.def_collector.db, MacroCallKind::FnLike(ast_id)); |
918 | 867 | ||
919 | self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def); | 868 | self.def_collector.unexpanded_macros.push(MacroDirective { |
869 | module_id: self.module_id, | ||
870 | path: mac.path.clone(), | ||
871 | ast_id, | ||
872 | legacy: Some(macro_call_id), | ||
873 | }); | ||
874 | |||
920 | return; | 875 | return; |
921 | } | 876 | } |
922 | 877 | ||
@@ -926,7 +881,13 @@ where | |||
926 | if path.is_ident() { | 881 | if path.is_ident() { |
927 | path.kind = PathKind::Self_; | 882 | path.kind = PathKind::Self_; |
928 | } | 883 | } |
929 | self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path)); | 884 | |
885 | self.def_collector.unexpanded_macros.push(MacroDirective { | ||
886 | module_id: self.module_id, | ||
887 | path, | ||
888 | ast_id, | ||
889 | legacy: None, | ||
890 | }); | ||
930 | } | 891 | } |
931 | 892 | ||
932 | fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) { | 893 | fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) { |
@@ -951,19 +912,13 @@ fn is_macro_rules(path: &Path) -> bool { | |||
951 | 912 | ||
952 | #[cfg(test)] | 913 | #[cfg(test)] |
953 | mod tests { | 914 | mod tests { |
915 | use crate::{db::DefDatabase, test_db::TestDB}; | ||
954 | use ra_arena::Arena; | 916 | use ra_arena::Arena; |
955 | use ra_db::{fixture::WithFixture, SourceDatabase}; | 917 | use ra_db::{fixture::WithFixture, SourceDatabase}; |
956 | use rustc_hash::FxHashSet; | ||
957 | |||
958 | use crate::{db::DefDatabase, test_db::TestDB}; | ||
959 | 918 | ||
960 | use super::*; | 919 | use super::*; |
961 | 920 | ||
962 | fn do_collect_defs( | 921 | fn do_collect_defs(db: &impl DefDatabase, def_map: CrateDefMap) -> CrateDefMap { |
963 | db: &impl DefDatabase, | ||
964 | def_map: CrateDefMap, | ||
965 | monitor: MacroStackMonitor, | ||
966 | ) -> (CrateDefMap, FxHashSet<MacroDefId>) { | ||
967 | let mut collector = DefCollector { | 922 | let mut collector = DefCollector { |
968 | db, | 923 | db, |
969 | def_map, | 924 | def_map, |
@@ -973,19 +928,13 @@ mod tests { | |||
973 | unexpanded_macros: Vec::new(), | 928 | unexpanded_macros: Vec::new(), |
974 | unexpanded_attribute_macros: Vec::new(), | 929 | unexpanded_attribute_macros: Vec::new(), |
975 | mod_dirs: FxHashMap::default(), | 930 | mod_dirs: FxHashMap::default(), |
976 | macro_stack_monitor: monitor, | ||
977 | poison_macros: FxHashSet::default(), | ||
978 | cfg_options: &CfgOptions::default(), | 931 | cfg_options: &CfgOptions::default(), |
979 | }; | 932 | }; |
980 | collector.collect(); | 933 | collector.collect(); |
981 | (collector.def_map, collector.poison_macros) | 934 | collector.def_map |
982 | } | 935 | } |
983 | 936 | ||
984 | fn do_limited_resolve( | 937 | fn do_resolve(code: &str) -> CrateDefMap { |
985 | code: &str, | ||
986 | limit: u32, | ||
987 | poison_limit: u32, | ||
988 | ) -> (CrateDefMap, FxHashSet<MacroDefId>) { | ||
989 | let (db, _file_id) = TestDB::with_single_file(&code); | 938 | let (db, _file_id) = TestDB::with_single_file(&code); |
990 | let krate = db.test_crate(); | 939 | let krate = db.test_crate(); |
991 | 940 | ||
@@ -1003,59 +952,18 @@ mod tests { | |||
1003 | diagnostics: Vec::new(), | 952 | diagnostics: Vec::new(), |
1004 | } | 953 | } |
1005 | }; | 954 | }; |
1006 | 955 | do_collect_defs(&db, def_map) | |
1007 | let mut monitor = MacroStackMonitor::default(); | ||
1008 | monitor.validator = Some(Box::new(move |count| { | ||
1009 | assert!(count < limit); | ||
1010 | count >= poison_limit | ||
1011 | })); | ||
1012 | |||
1013 | do_collect_defs(&db, def_map, monitor) | ||
1014 | } | 956 | } |
1015 | 957 | ||
1016 | #[test] | 958 | #[test] |
1017 | fn test_macro_expand_limit_width() { | 959 | fn test_macro_expand_will_stop() { |
1018 | do_limited_resolve( | 960 | do_resolve( |
1019 | r#" | 961 | r#" |
1020 | macro_rules! foo { | 962 | macro_rules! foo { |
1021 | ($($ty:ty)*) => { foo!($($ty)*, $($ty)*); } | 963 | ($($ty:ty)*) => { foo!($($ty)*, $($ty)*); } |
1022 | } | 964 | } |
1023 | foo!(KABOOM); | 965 | foo!(KABOOM); |
1024 | "#, | 966 | "#, |
1025 | 16, | ||
1026 | 1000, | ||
1027 | ); | 967 | ); |
1028 | } | 968 | } |
1029 | |||
1030 | #[test] | ||
1031 | fn test_macro_expand_poisoned() { | ||
1032 | let (_, poison_macros) = do_limited_resolve( | ||
1033 | r#" | ||
1034 | macro_rules! foo { | ||
1035 | ($ty:ty) => { foo!($ty); } | ||
1036 | } | ||
1037 | foo!(KABOOM); | ||
1038 | "#, | ||
1039 | 100, | ||
1040 | 16, | ||
1041 | ); | ||
1042 | |||
1043 | assert_eq!(poison_macros.len(), 1); | ||
1044 | } | ||
1045 | |||
1046 | #[test] | ||
1047 | fn test_macro_expand_normal() { | ||
1048 | let (_, poison_macros) = do_limited_resolve( | ||
1049 | r#" | ||
1050 | macro_rules! foo { | ||
1051 | ($ident:ident) => { struct $ident {} } | ||
1052 | } | ||
1053 | foo!(Bar); | ||
1054 | "#, | ||
1055 | 16, | ||
1056 | 16, | ||
1057 | ); | ||
1058 | |||
1059 | assert_eq!(poison_macros.len(), 0); | ||
1060 | } | ||
1061 | } | 969 | } |
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index c7071fe85..ec5ace757 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -208,15 +208,20 @@ fn format_args_expand( | |||
208 | _id: MacroCallId, | 208 | _id: MacroCallId, |
209 | tt: &tt::Subtree, | 209 | tt: &tt::Subtree, |
210 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 210 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
211 | // We expand `format_args!("", arg1, arg2)` to | 211 | // We expand `format_args!("", a1, a2)` to |
212 | // `std::fmt::Arguments::new_v1(&[], &[&arg1, &arg2])`, | 212 | // ``` |
213 | // std::fmt::Arguments::new_v1(&[], &[ | ||
214 | // std::fmt::ArgumentV1::new(&arg1,std::fmt::Display::fmt), | ||
215 | // std::fmt::ArgumentV1::new(&arg2,std::fmt::Display::fmt), | ||
216 | // ]) | ||
217 | // ```, | ||
213 | // which is still not really correct, but close enough for now | 218 | // which is still not really correct, but close enough for now |
214 | let mut args = Vec::new(); | 219 | let mut args = Vec::new(); |
215 | let mut current = Vec::new(); | 220 | let mut current = Vec::new(); |
216 | for tt in tt.token_trees.iter().cloned() { | 221 | for tt in tt.token_trees.iter().cloned() { |
217 | match tt { | 222 | match tt { |
218 | tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => { | 223 | tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => { |
219 | args.push(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: current }); | 224 | args.push(current); |
220 | current = Vec::new(); | 225 | current = Vec::new(); |
221 | } | 226 | } |
222 | _ => { | 227 | _ => { |
@@ -225,13 +230,15 @@ fn format_args_expand( | |||
225 | } | 230 | } |
226 | } | 231 | } |
227 | if !current.is_empty() { | 232 | if !current.is_empty() { |
228 | args.push(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: current }); | 233 | args.push(current); |
229 | } | 234 | } |
230 | if args.is_empty() { | 235 | if args.is_empty() { |
231 | return Err(mbe::ExpandError::NoMatchingRule); | 236 | return Err(mbe::ExpandError::NoMatchingRule); |
232 | } | 237 | } |
233 | let _format_string = args.remove(0); | 238 | let _format_string = args.remove(0); |
234 | let arg_tts = args.into_iter().flat_map(|arg| (quote! { & #arg , }).token_trees); | 239 | let arg_tts = args.into_iter().flat_map(|arg| { |
240 | quote! { std::fmt::ArgumentV1::new(&(##arg), std::fmt::Display::fmt), } | ||
241 | }.token_trees).collect::<Vec<_>>(); | ||
235 | let expanded = quote! { | 242 | let expanded = quote! { |
236 | std::fmt::Arguments::new_v1(&[], &[##arg_tts]) | 243 | std::fmt::Arguments::new_v1(&[], &[##arg_tts]) |
237 | }; | 244 | }; |
@@ -360,6 +367,6 @@ mod tests { | |||
360 | BuiltinFnLikeExpander::FormatArgs, | 367 | BuiltinFnLikeExpander::FormatArgs, |
361 | ); | 368 | ); |
362 | 369 | ||
363 | assert_eq!(expanded, r#"std::fmt::Arguments::new_v1(&[] ,&[&arg1(a,b,c),&arg2,])"#); | 370 | assert_eq!(expanded, r#"std::fmt::Arguments::new_v1(&[] ,&[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"#); |
364 | } | 371 | } |
365 | } | 372 | } |
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index 4bdb41619..f68aca789 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -183,8 +183,8 @@ fn to_fragment_kind(db: &dyn AstDatabase, macro_call_id: MacroCallId) -> Fragmen | |||
183 | // FIXME: Handle Pattern | 183 | // FIXME: Handle Pattern |
184 | FragmentKind::Expr | 184 | FragmentKind::Expr |
185 | } | 185 | } |
186 | EXPR_STMT => FragmentKind::Statements, | 186 | // FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that |
187 | BLOCK => FragmentKind::Statements, | 187 | EXPR_STMT | BLOCK => FragmentKind::Expr, |
188 | ARG_LIST => FragmentKind::Expr, | 188 | ARG_LIST => FragmentKind::Expr, |
189 | TRY_EXPR => FragmentKind::Expr, | 189 | TRY_EXPR => FragmentKind::Expr, |
190 | TUPLE_EXPR => FragmentKind::Expr, | 190 | TUPLE_EXPR => FragmentKind::Expr, |
diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 9daa77cfa..0f4dac45e 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs | |||
@@ -332,7 +332,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
332 | // It will not recurse to `coerce`. | 332 | // It will not recurse to `coerce`. |
333 | return self.table.unify_substs(st1, st2, 0); | 333 | return self.table.unify_substs(st1, st2, 0); |
334 | } | 334 | } |
335 | _ => {} | 335 | _ => { |
336 | if self.table.unify_inner_trivial(&derefed_ty, &to_ty) { | ||
337 | return true; | ||
338 | } | ||
339 | } | ||
336 | } | 340 | } |
337 | } | 341 | } |
338 | 342 | ||
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 58b22396f..ac9e3872a 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs | |||
@@ -403,3 +403,40 @@ fn test() { | |||
403 | "### | 403 | "### |
404 | ); | 404 | ); |
405 | } | 405 | } |
406 | |||
407 | #[test] | ||
408 | fn coerce_autoderef_generic() { | ||
409 | assert_snapshot!( | ||
410 | infer_with_mismatches(r#" | ||
411 | struct Foo; | ||
412 | fn takes_ref<T>(x: &T) -> T { *x } | ||
413 | fn test() { | ||
414 | takes_ref(&Foo); | ||
415 | takes_ref(&&Foo); | ||
416 | takes_ref(&&&Foo); | ||
417 | } | ||
418 | "#, true), | ||
419 | @r###" | ||
420 | [29; 30) 'x': &T | ||
421 | [41; 47) '{ *x }': T | ||
422 | [43; 45) '*x': T | ||
423 | [44; 45) 'x': &T | ||
424 | [58; 127) '{ ...oo); }': () | ||
425 | [64; 73) 'takes_ref': fn takes_ref<Foo>(&T) -> T | ||
426 | [64; 79) 'takes_ref(&Foo)': Foo | ||
427 | [74; 78) '&Foo': &Foo | ||
428 | [75; 78) 'Foo': Foo | ||
429 | [85; 94) 'takes_ref': fn takes_ref<&Foo>(&T) -> T | ||
430 | [85; 101) 'takes_...&&Foo)': &Foo | ||
431 | [95; 100) '&&Foo': &&Foo | ||
432 | [96; 100) '&Foo': &Foo | ||
433 | [97; 100) 'Foo': Foo | ||
434 | [107; 116) 'takes_ref': fn takes_ref<&&Foo>(&T) -> T | ||
435 | [107; 124) 'takes_...&&Foo)': &&Foo | ||
436 | [117; 123) '&&&Foo': &&&Foo | ||
437 | [118; 123) '&&Foo': &&Foo | ||
438 | [119; 123) '&Foo': &Foo | ||
439 | [120; 123) 'Foo': Foo | ||
440 | "### | ||
441 | ); | ||
442 | } | ||
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 1b968134d..b93d6a931 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -693,7 +693,6 @@ mod tests { | |||
693 | ); | 693 | ); |
694 | } | 694 | } |
695 | 695 | ||
696 | #[should_panic] // currently failing because of expr mapping problems | ||
697 | #[test] | 696 | #[test] |
698 | fn goto_through_format() { | 697 | fn goto_through_format() { |
699 | check_goto( | 698 | check_goto( |
@@ -718,7 +717,7 @@ mod tests { | |||
718 | format!(\"{}\", fo<|>o()) | 717 | format!(\"{}\", fo<|>o()) |
719 | } | 718 | } |
720 | ", | 719 | ", |
721 | "foo FN_DEF FileId(1) [359; 376) [362; 365)", | 720 | "foo FN_DEF FileId(1) [398; 415) [401; 404)", |
722 | ); | 721 | ); |
723 | } | 722 | } |
724 | 723 | ||
diff --git a/docs/user/README.md b/docs/user/README.md index 5ec8fb25d..04c349342 100644 --- a/docs/user/README.md +++ b/docs/user/README.md | |||
@@ -204,4 +204,19 @@ Installation: | |||
204 | 204 | ||
205 | * You can now invoke the command palette and type LSP enable to locally/globally enable the rust-analyzer LSP (type LSP enable, then choose either locally or globally, then select rust-analyzer) | 205 | * You can now invoke the command palette and type LSP enable to locally/globally enable the rust-analyzer LSP (type LSP enable, then choose either locally or globally, then select rust-analyzer) |
206 | 206 | ||
207 | * Note that `ra_lsp_server` binary must be in `$PATH` for this to work. If it's not the case, you can specify full path to the binary, which is typically `.cargo/bin/ra_lsp_server`. | 207 | ### Setting up the `PATH` variable |
208 | |||
209 | On Unix systems, `rustup` adds `~/.cargo/bin` to `PATH` by modifying the shell's | ||
210 | startup file. Depending on your configuration, your Desktop Environment might not | ||
211 | actually load it. If you find that `rust-analyzer` only runs when starting the | ||
212 | editor from the terminal, you will have to set up your `PATH` variable manually. | ||
213 | |||
214 | There are a couple of ways to do that: | ||
215 | |||
216 | - for Code, set `rust-analyzer.raLspServerPath` to `~/.cargo/bin` (the `~` is | ||
217 | automatically resolved by the extension) | ||
218 | - copy the binary to a location that is already in `PATH`, e.g. `/usr/local/bin` | ||
219 | - on Linux, use PAM to configure the `PATH` variable, by e.g. putting | ||
220 | `PATH DEFAULT=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:@{HOME}/.cargo/bin:@{HOME}/.local/bin` | ||
221 | in your `~/.pam_environment` file; note that this might interfere with other | ||
222 | defaults set by the system administrator via `/etc/environment`. | ||
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 2ceac60a0..099aaaaa2 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json | |||
@@ -763,6 +763,11 @@ | |||
763 | "chalk": "^2.0.1" | 763 | "chalk": "^2.0.1" |
764 | } | 764 | } |
765 | }, | 765 | }, |
766 | "lookpath": { | ||
767 | "version": "1.0.3", | ||
768 | "resolved": "https://registry.npmjs.org/lookpath/-/lookpath-1.0.3.tgz", | ||
769 | "integrity": "sha512-XIdgzlX26g10XnzyZdO/4obybEmfGnZyWQZ2DgmmEfVB79X+n3lhUoIzMe501C6s7RmCpAo66OPegWc+CsxYMg==" | ||
770 | }, | ||
766 | "magic-string": { | 771 | "magic-string": { |
767 | "version": "0.25.3", | 772 | "version": "0.25.3", |
768 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", | 773 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", |
diff --git a/editors/code/package.json b/editors/code/package.json index 94887674b..5dea8fac0 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -31,6 +31,7 @@ | |||
31 | "singleQuote": true | 31 | "singleQuote": true |
32 | }, | 32 | }, |
33 | "dependencies": { | 33 | "dependencies": { |
34 | "lookpath": "^1.0.3", | ||
34 | "seedrandom": "^3.0.1", | 35 | "seedrandom": "^3.0.1", |
35 | "vscode-languageclient": "^5.3.0-next.4" | 36 | "vscode-languageclient": "^5.3.0-next.4" |
36 | }, | 37 | }, |
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index 683497dfd..6637c3bf0 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts | |||
@@ -14,7 +14,7 @@ import * as events from './events'; | |||
14 | import * as notifications from './notifications'; | 14 | import * as notifications from './notifications'; |
15 | import { Server } from './server'; | 15 | import { Server } from './server'; |
16 | 16 | ||
17 | export function activate(context: vscode.ExtensionContext) { | 17 | export async function activate(context: vscode.ExtensionContext) { |
18 | function disposeOnDeactivation(disposable: vscode.Disposable) { | 18 | function disposeOnDeactivation(disposable: vscode.Disposable) { |
19 | context.subscriptions.push(disposable); | 19 | context.subscriptions.push(disposable); |
20 | } | 20 | } |
@@ -159,7 +159,11 @@ export function activate(context: vscode.ExtensionContext) { | |||
159 | }); | 159 | }); |
160 | 160 | ||
161 | // Start the language server, finally! | 161 | // Start the language server, finally! |
162 | startServer(); | 162 | try { |
163 | await startServer(); | ||
164 | } catch (e) { | ||
165 | vscode.window.showErrorMessage(e.message); | ||
166 | } | ||
163 | 167 | ||
164 | if (Server.config.displayInlayHints) { | 168 | if (Server.config.displayInlayHints) { |
165 | const hintsUpdater = new HintsUpdater(); | 169 | const hintsUpdater = new HintsUpdater(); |
@@ -204,10 +208,10 @@ export function deactivate(): Thenable<void> { | |||
204 | return Server.client.stop(); | 208 | return Server.client.stop(); |
205 | } | 209 | } |
206 | 210 | ||
207 | async function reloadServer(startServer: () => void) { | 211 | async function reloadServer(startServer: () => Promise<void>) { |
208 | if (Server.client != null) { | 212 | if (Server.client != null) { |
209 | vscode.window.showInformationMessage('Reloading rust-analyzer...'); | 213 | vscode.window.showInformationMessage('Reloading rust-analyzer...'); |
210 | await Server.client.stop(); | 214 | await Server.client.stop(); |
211 | startServer(); | 215 | await startServer(); |
212 | } | 216 | } |
213 | } | 217 | } |
diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts index 7907b70bc..e717ab294 100644 --- a/editors/code/src/server.ts +++ b/editors/code/src/server.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import { lookpath } from 'lookpath'; | ||
1 | import { homedir } from 'os'; | 2 | import { homedir } from 'os'; |
2 | import * as lc from 'vscode-languageclient'; | 3 | import * as lc from 'vscode-languageclient'; |
3 | 4 | ||
@@ -17,7 +18,7 @@ export class Server { | |||
17 | public static config = new Config(); | 18 | public static config = new Config(); |
18 | public static client: lc.LanguageClient; | 19 | public static client: lc.LanguageClient; |
19 | 20 | ||
20 | public static start( | 21 | public static async start( |
21 | notificationHandlers: Iterable<[string, lc.GenericNotificationHandler]> | 22 | notificationHandlers: Iterable<[string, lc.GenericNotificationHandler]> |
22 | ) { | 23 | ) { |
23 | // '.' Is the fallback if no folder is open | 24 | // '.' Is the fallback if no folder is open |
@@ -27,8 +28,14 @@ export class Server { | |||
27 | folder = workspace.workspaceFolders[0].uri.fsPath.toString(); | 28 | folder = workspace.workspaceFolders[0].uri.fsPath.toString(); |
28 | } | 29 | } |
29 | 30 | ||
31 | const command = expandPathResolving(this.config.raLspServerPath); | ||
32 | if (!(await lookpath(command))) { | ||
33 | throw new Error( | ||
34 | `Cannot find rust-analyzer server \`${command}\` in PATH.` | ||
35 | ); | ||
36 | } | ||
30 | const run: lc.Executable = { | 37 | const run: lc.Executable = { |
31 | command: expandPathResolving(this.config.raLspServerPath), | 38 | command, |
32 | options: { cwd: folder } | 39 | options: { cwd: folder } |
33 | }; | 40 | }; |
34 | const serverOptions: lc.ServerOptions = { | 41 | const serverOptions: lc.ServerOptions = { |