aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/ast_transform.rs9
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs4
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs8
-rw-r--r--crates/ra_db/src/fixture.rs6
-rw-r--r--crates/ra_db/src/input.rs12
-rw-r--r--crates/ra_hir/Cargo.toml1
-rw-r--r--crates/ra_hir/src/code_model.rs56
-rw-r--r--crates/ra_hir/src/semantics.rs8
-rw-r--r--crates/ra_hir_def/src/body.rs12
-rw-r--r--crates/ra_hir_def/src/body/lower.rs48
-rw-r--r--crates/ra_hir_def/src/data.rs28
-rw-r--r--crates/ra_hir_def/src/nameres.rs9
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs48
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs22
-rw-r--r--crates/ra_hir_def/src/resolver.rs15
-rw-r--r--crates/ra_hir_def/src/visibility.rs33
-rw-r--r--crates/ra_hir_expand/src/lib.rs20
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs20
-rw-r--r--crates/ra_ide/src/call_info.rs14
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs32
-rw-r--r--crates/ra_ide/src/completion/complete_path.rs70
-rw-r--r--crates/ra_ide/src/completion/presentation.rs15
-rw-r--r--crates/ra_ide/src/display.rs37
-rw-r--r--crates/ra_ide/src/display/function_signature.rs4
-rw-r--r--crates/ra_ide/src/goto_definition.rs15
-rw-r--r--crates/ra_ide/src/hover.rs64
-rw-r--r--crates/ra_ide/src/lib.rs3
-rw-r--r--crates/ra_ide/src/mock_analysis.rs2
-rw-r--r--crates/ra_ide/src/ssr.rs36
-rw-r--r--crates/ra_ide/src/typing.rs40
-rw-r--r--crates/ra_parser/src/grammar/params.rs64
-rw-r--r--crates/ra_project_model/src/lib.rs7
-rw-r--r--crates/ra_syntax/src/ast/make.rs16
-rw-r--r--crates/ra_syntax/src/tests.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0032_fn_pointer_type.txt3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0116_trait_fn_placeholder_parameter.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0116_trait_fn_placeholder_parameter.txt47
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0123_param_list_vararg.txt3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.txt44
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.txt3
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rs (renamed from crates/ra_syntax/test_data/parser/inline/ok/0153_trait_fn_patterns.rs)1
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.txt (renamed from crates/ra_syntax/test_data/parser/inline/ok/0153_trait_fn_patterns.txt)47
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_variadic_fun.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_variadic_fun.txt133
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rs (renamed from crates/ra_syntax/test_data/parser/inline/ok/0152_fn_patterns.rs)0
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.txt (renamed from crates/ra_syntax/test_data/parser/inline/ok/0152_fn_patterns.txt)0
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs42
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--crates/rust-analyzer/src/main_loop.rs74
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs33
-rw-r--r--crates/rust-analyzer/src/req.rs5
-rw-r--r--crates/rust-analyzer/src/world.rs1
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs15
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs15
-rw-r--r--crates/test_utils/src/lib.rs3
56 files changed, 932 insertions, 329 deletions
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 42856f0ca..45558c448 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -37,7 +37,6 @@ pub struct SubstituteTypeParams<'a> {
37impl<'a> SubstituteTypeParams<'a> { 37impl<'a> SubstituteTypeParams<'a> {
38 pub fn for_trait_impl( 38 pub fn for_trait_impl(
39 source_scope: &'a SemanticsScope<'a, RootDatabase>, 39 source_scope: &'a SemanticsScope<'a, RootDatabase>,
40 db: &'a RootDatabase,
41 // FIXME: there's implicit invariant that `trait_` and `source_scope` match... 40 // FIXME: there's implicit invariant that `trait_` and `source_scope` match...
42 trait_: hir::Trait, 41 trait_: hir::Trait,
43 impl_def: ast::ImplDef, 42 impl_def: ast::ImplDef,
@@ -45,7 +44,7 @@ impl<'a> SubstituteTypeParams<'a> {
45 let substs = get_syntactic_substs(impl_def).unwrap_or_default(); 44 let substs = get_syntactic_substs(impl_def).unwrap_or_default();
46 let generic_def: hir::GenericDef = trait_.into(); 45 let generic_def: hir::GenericDef = trait_.into();
47 let substs_by_param: FxHashMap<_, _> = generic_def 46 let substs_by_param: FxHashMap<_, _> = generic_def
48 .params(db) 47 .params(source_scope.db)
49 .into_iter() 48 .into_iter()
50 // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky 49 // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
51 .skip(1) 50 .skip(1)
@@ -104,7 +103,6 @@ impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
104pub struct QualifyPaths<'a> { 103pub struct QualifyPaths<'a> {
105 target_scope: &'a SemanticsScope<'a, RootDatabase>, 104 target_scope: &'a SemanticsScope<'a, RootDatabase>,
106 source_scope: &'a SemanticsScope<'a, RootDatabase>, 105 source_scope: &'a SemanticsScope<'a, RootDatabase>,
107 db: &'a RootDatabase,
108 previous: Box<dyn AstTransform<'a> + 'a>, 106 previous: Box<dyn AstTransform<'a> + 'a>,
109} 107}
110 108
@@ -112,9 +110,8 @@ impl<'a> QualifyPaths<'a> {
112 pub fn new( 110 pub fn new(
113 target_scope: &'a SemanticsScope<'a, RootDatabase>, 111 target_scope: &'a SemanticsScope<'a, RootDatabase>,
114 source_scope: &'a SemanticsScope<'a, RootDatabase>, 112 source_scope: &'a SemanticsScope<'a, RootDatabase>,
115 db: &'a RootDatabase,
116 ) -> Self { 113 ) -> Self {
117 Self { target_scope, source_scope, db, previous: Box::new(NullTransformer) } 114 Self { target_scope, source_scope, previous: Box::new(NullTransformer) }
118 } 115 }
119 116
120 fn get_substitution_inner( 117 fn get_substitution_inner(
@@ -132,7 +129,7 @@ impl<'a> QualifyPaths<'a> {
132 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; 129 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
133 match resolution { 130 match resolution {
134 PathResolution::Def(def) => { 131 PathResolution::Def(def) => {
135 let found_path = from.find_use_path(self.db, def)?; 132 let found_path = from.find_use_path(self.source_scope.db, def)?;
136 let mut path = path_to_ast(found_path); 133 let mut path = path_to_ast(found_path);
137 134
138 let type_args = p 135 let type_args = p
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index 639180d37..e5920b6f6 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -142,8 +142,8 @@ fn add_missing_impl_members_inner(
142 let n_existing_items = impl_item_list.impl_items().count(); 142 let n_existing_items = impl_item_list.impl_items().count();
143 let source_scope = sema.scope_for_def(trait_); 143 let source_scope = sema.scope_for_def(trait_);
144 let target_scope = sema.scope(impl_item_list.syntax()); 144 let target_scope = sema.scope(impl_item_list.syntax());
145 let ast_transform = QualifyPaths::new(&target_scope, &source_scope, sema.db) 145 let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
146 .or(SubstituteTypeParams::for_trait_impl(&source_scope, sema.db, trait_, impl_node)); 146 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_node));
147 let items = missing_items 147 let items = missing_items
148 .into_iter() 148 .into_iter()
149 .map(|it| ast_transform::apply(&*ast_transform, it)) 149 .map(|it| ast_transform::apply(&*ast_transform, it))
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index e5d8c639d..97cf90ae4 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -2,7 +2,7 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use hir::{db::HirDatabase, Adt, HasSource, Semantics}; 5use hir::{Adt, HasSource, Semantics};
6use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; 6use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};
7 7
8use crate::{Assist, AssistCtx, AssistId}; 8use crate::{Assist, AssistCtx, AssistId};
@@ -88,11 +88,7 @@ fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
88 }) 88 })
89} 89}
90 90
91fn build_pat( 91fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
92 db: &impl HirDatabase,
93 module: hir::Module,
94 var: hir::EnumVariant,
95) -> Option<ast::Pat> {
96 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?); 92 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?);
97 93
98 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 94 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index 3dc86ca2d..3464f43df 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -64,7 +64,9 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId
64 crate_graph.add_crate_root( 64 crate_graph.add_crate_root(
65 file_id, 65 file_id,
66 meta.edition, 66 meta.edition,
67 meta.krate, 67 meta.krate.map(|name| {
68 CrateName::new(&name).expect("Fixture crate name should not contain dashes")
69 }),
68 meta.cfg, 70 meta.cfg,
69 meta.env, 71 meta.env,
70 Default::default(), 72 Default::default(),
@@ -124,7 +126,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
124 let crate_id = crate_graph.add_crate_root( 126 let crate_id = crate_graph.add_crate_root(
125 file_id, 127 file_id,
126 meta.edition, 128 meta.edition,
127 Some(krate.clone()), 129 Some(CrateName::new(&krate).unwrap()),
128 meta.cfg, 130 meta.cfg,
129 meta.env, 131 meta.env,
130 Default::default(), 132 Default::default(),
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 06d40db96..bde843001 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -14,6 +14,7 @@ use rustc_hash::FxHashMap;
14use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15 15
16use crate::{RelativePath, RelativePathBuf}; 16use crate::{RelativePath, RelativePathBuf};
17use fmt::Display;
17 18
18/// `FileId` is an integer which uniquely identifies a file. File paths are 19/// `FileId` is an integer which uniquely identifies a file. File paths are
19/// messy and system-dependent, so most of the code should work directly with 20/// messy and system-dependent, so most of the code should work directly with
@@ -83,6 +84,7 @@ pub struct CrateGraph {
83#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 84#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
84pub struct CrateId(pub u32); 85pub struct CrateId(pub u32);
85 86
87#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct CrateName(SmolStr); 88pub struct CrateName(SmolStr);
87 89
88impl CrateName { 90impl CrateName {
@@ -103,6 +105,12 @@ impl CrateName {
103 } 105 }
104} 106}
105 107
108impl Display for CrateName {
109 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110 write!(f, "{}", self.0)
111 }
112}
113
106#[derive(Debug, Clone, PartialEq, Eq)] 114#[derive(Debug, Clone, PartialEq, Eq)]
107pub struct CrateData { 115pub struct CrateData {
108 pub root_file_id: FileId, 116 pub root_file_id: FileId,
@@ -110,7 +118,7 @@ pub struct CrateData {
110 /// The name to display to the end user. 118 /// The name to display to the end user.
111 /// This actual crate name can be different in a particular dependent crate 119 /// This actual crate name can be different in a particular dependent crate
112 /// or may even be missing for some cases, such as a dummy crate for the code snippet. 120 /// or may even be missing for some cases, such as a dummy crate for the code snippet.
113 pub display_name: Option<String>, 121 pub display_name: Option<CrateName>,
114 pub cfg_options: CfgOptions, 122 pub cfg_options: CfgOptions,
115 pub env: Env, 123 pub env: Env,
116 pub extern_source: ExternSource, 124 pub extern_source: ExternSource,
@@ -150,7 +158,7 @@ impl CrateGraph {
150 &mut self, 158 &mut self,
151 file_id: FileId, 159 file_id: FileId,
152 edition: Edition, 160 edition: Edition,
153 display_name: Option<String>, 161 display_name: Option<CrateName>,
154 cfg_options: CfgOptions, 162 cfg_options: CfgOptions,
155 env: Env, 163 env: Env,
156 extern_source: ExternSource, 164 extern_source: ExternSource,
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index 266c4cff3..42193b492 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -11,6 +11,7 @@ doctest = false
11log = "0.4.8" 11log = "0.4.8"
12rustc-hash = "1.1.0" 12rustc-hash = "1.1.0"
13either = "1.5.3" 13either = "1.5.3"
14arrayvec = "0.5.1"
14 15
15itertools = "0.8.2" 16itertools = "0.8.2"
16 17
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 41d4e2ed3..ff041150b 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1,6 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use arrayvec::ArrayVec;
4use either::Either; 5use either::Either;
5use hir_def::{ 6use hir_def::{
6 adt::StructKind, 7 adt::StructKind,
@@ -226,7 +227,9 @@ impl Module {
226 Some((name, def)) 227 Some((name, def))
227 } 228 }
228 }) 229 })
229 .map(|(name, def)| (name.clone(), def.into())) 230 .flat_map(|(name, def)| {
231 ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item))
232 })
230 .collect() 233 .collect()
231 } 234 }
232 235
@@ -308,7 +311,11 @@ impl StructField {
308 self.parent.variant_data(db).fields()[self.id].name.clone() 311 self.parent.variant_data(db).fields()[self.id].name.clone()
309 } 312 }
310 313
311 pub fn ty(&self, db: &impl HirDatabase) -> Type { 314 /// Returns the type as in the signature of the struct (i.e., with
315 /// placeholder types for type parameters). This is good for showing
316 /// signature help, but not so good to actually get the type of the field
317 /// when you actually have a variable of the struct.
318 pub fn signature_ty(&self, db: &impl HirDatabase) -> Type {
312 let var_id = self.parent.into(); 319 let var_id = self.parent.into();
313 let generic_def_id: GenericDefId = match self.parent { 320 let generic_def_id: GenericDefId = match self.parent {
314 VariantDef::Struct(it) => it.id.into(), 321 VariantDef::Struct(it) => it.id.into(),
@@ -482,6 +489,10 @@ impl Adt {
482 let subst = db.generic_defaults(self.into()); 489 let subst = db.generic_defaults(self.into());
483 subst.iter().any(|ty| ty == &Ty::Unknown) 490 subst.iter().any(|ty| ty == &Ty::Unknown)
484 } 491 }
492
493 /// Turns this ADT into a type. Any type parameters of the ADT will be
494 /// turned into unknown types, which is good for e.g. finding the most
495 /// general set of completions, but will not look very nice when printed.
485 pub fn ty(self, db: &impl HirDatabase) -> Type { 496 pub fn ty(self, db: &impl HirDatabase) -> Type {
486 let id = AdtId::from(self); 497 let id = AdtId::from(self);
487 Type::from_def(db, id.module(db).krate, id) 498 Type::from_def(db, id.module(db).krate, id)
@@ -1028,7 +1039,7 @@ impl Type {
1028 krate: CrateId, 1039 krate: CrateId,
1029 def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>, 1040 def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>,
1030 ) -> Type { 1041 ) -> Type {
1031 let substs = Substs::type_params(db, def); 1042 let substs = Substs::build_for_def(db, def).fill_with_unknown().build();
1032 let ty = db.ty(def.into()).subst(&substs); 1043 let ty = db.ty(def.into()).subst(&substs);
1033 Type::new(db, krate, def, ty) 1044 Type::new(db, krate, def, ty)
1034 } 1045 }
@@ -1288,15 +1299,36 @@ pub enum ScopeDef {
1288 Unknown, 1299 Unknown,
1289} 1300}
1290 1301
1291impl From<PerNs> for ScopeDef { 1302impl ScopeDef {
1292 fn from(def: PerNs) -> Self { 1303 pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> {
1293 def.take_types() 1304 let mut items = ArrayVec::new();
1294 .or_else(|| def.take_values()) 1305
1295 .map(|module_def_id| ScopeDef::ModuleDef(module_def_id.into())) 1306 match (def.take_types(), def.take_values()) {
1296 .or_else(|| { 1307 (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())),
1297 def.take_macros().map(|macro_def_id| ScopeDef::MacroDef(macro_def_id.into())) 1308 (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())),
1298 }) 1309 (Some(m1), Some(m2)) => {
1299 .unwrap_or(ScopeDef::Unknown) 1310 // Some items, like unit structs and enum variants, are
1311 // returned as both a type and a value. Here we want
1312 // to de-duplicate them.
1313 if m1 != m2 {
1314 items.push(ScopeDef::ModuleDef(m1.into()));
1315 items.push(ScopeDef::ModuleDef(m2.into()));
1316 } else {
1317 items.push(ScopeDef::ModuleDef(m1.into()));
1318 }
1319 }
1320 (None, None) => {}
1321 };
1322
1323 if let Some(macro_def_id) = def.take_macros() {
1324 items.push(ScopeDef::MacroDef(macro_def_id.into()));
1325 }
1326
1327 if items.is_empty() {
1328 items.push(ScopeDef::Unknown);
1329 }
1330
1331 items
1300 } 1332 }
1301} 1333}
1302 1334
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 3782a9984..788bb3eb7 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -344,7 +344,13 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> {
344 344
345 resolver.process_all_names(self.db, &mut |name, def| { 345 resolver.process_all_names(self.db, &mut |name, def| {
346 let def = match def { 346 let def = match def {
347 resolver::ScopeDef::PerNs(it) => it.into(), 347 resolver::ScopeDef::PerNs(it) => {
348 let items = ScopeDef::all_items(it);
349 for item in items {
350 f(name.clone(), item);
351 }
352 return;
353 }
348 resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), 354 resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
349 resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), 355 resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
350 resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), 356 resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }),
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 57ba45b45..2bc405a59 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -47,13 +47,19 @@ impl Expander {
47 pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>( 47 pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>(
48 &mut self, 48 &mut self,
49 db: &DB, 49 db: &DB,
50 local_scope: Option<&ItemScope>,
50 macro_call: ast::MacroCall, 51 macro_call: ast::MacroCall,
51 ) -> Option<(Mark, T)> { 52 ) -> Option<(Mark, T)> {
52 let macro_call = InFile::new(self.current_file_id, &macro_call); 53 let macro_call = InFile::new(self.current_file_id, &macro_call);
53 54
54 if let Some(call_id) = 55 if let Some(call_id) = macro_call.as_call_id(db, |path| {
55 macro_call.as_call_id(db, |path| self.resolve_path_as_macro(db, &path)) 56 if let Some(local_scope) = local_scope {
56 { 57 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
58 return Some(def);
59 }
60 }
61 self.resolve_path_as_macro(db, &path)
62 }) {
57 let file_id = call_id.as_file(); 63 let file_id = call_id.as_file();
58 if let Some(node) = db.parse_or_expand(file_id) { 64 if let Some(node) = db.parse_or_expand(file_id) {
59 if let Some(expr) = T::cast(node) { 65 if let Some(expr) = T::cast(node) {
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index ec1b0c2e7..54b5591d3 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -3,7 +3,10 @@
3 3
4use either::Either; 4use either::Either;
5 5
6use hir_expand::name::{name, AsName, Name}; 6use hir_expand::{
7 name::{name, AsName, Name},
8 MacroDefId, MacroDefKind,
9};
7use ra_arena::Arena; 10use ra_arena::Arena;
8use ra_syntax::{ 11use ra_syntax::{
9 ast::{ 12 ast::{
@@ -452,19 +455,30 @@ where
452 None => self.alloc_expr(Expr::Missing, syntax_ptr), 455 None => self.alloc_expr(Expr::Missing, syntax_ptr),
453 } 456 }
454 } 457 }
455 // FIXME expand to statements in statement position
456 ast::Expr::MacroCall(e) => { 458 ast::Expr::MacroCall(e) => {
457 let macro_call = self.expander.to_source(AstPtr::new(&e)); 459 if let Some(name) = is_macro_rules(&e) {
458 match self.expander.enter_expand(self.db, e) { 460 let mac = MacroDefId {
459 Some((mark, expansion)) => { 461 krate: Some(self.expander.module.krate),
460 self.source_map 462 ast_id: Some(self.expander.ast_id(&e)),
461 .expansions 463 kind: MacroDefKind::Declarative,
462 .insert(macro_call, self.expander.current_file_id); 464 };
463 let id = self.collect_expr(expansion); 465 self.body.item_scope.define_legacy_macro(name, mac);
464 self.expander.exit(self.db, mark); 466
465 id 467 // FIXME: do we still need to allocate this as missing ?
468 self.alloc_expr(Expr::Missing, syntax_ptr)
469 } else {
470 let macro_call = self.expander.to_source(AstPtr::new(&e));
471 match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) {
472 Some((mark, expansion)) => {
473 self.source_map
474 .expansions
475 .insert(macro_call, self.expander.current_file_id);
476 let id = self.collect_expr(expansion);
477 self.expander.exit(self.db, mark);
478 id
479 }
480 None => self.alloc_expr(Expr::Missing, syntax_ptr),
466 } 481 }
467 None => self.alloc_expr(Expr::Missing, syntax_ptr),
468 } 482 }
469 } 483 }
470 484
@@ -686,6 +700,16 @@ where
686 } 700 }
687} 701}
688 702
703fn is_macro_rules(m: &ast::MacroCall) -> Option<Name> {
704 let name = m.path()?.segment()?.name_ref()?.as_name();
705
706 if name == name![macro_rules] {
707 Some(m.name()?.as_name())
708 } else {
709 None
710 }
711}
712
689impl From<ast::BinOp> for BinaryOp { 713impl From<ast::BinOp> for BinaryOp {
690 fn from(ast_op: ast::BinOp) -> Self { 714 fn from(ast_op: ast::BinOp) -> Self {
691 match ast_op { 715 match ast_op {
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index a72eb5369..c0b16b7fa 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -34,7 +34,8 @@ pub struct FunctionData {
34 34
35impl FunctionData { 35impl FunctionData {
36 pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> { 36 pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> {
37 let src = func.lookup(db).source(db); 37 let loc = func.lookup(db);
38 let src = loc.source(db);
38 let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing); 39 let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing);
39 let mut params = Vec::new(); 40 let mut params = Vec::new();
40 let mut has_self_param = false; 41 let mut has_self_param = false;
@@ -76,7 +77,9 @@ impl FunctionData {
76 ret_type 77 ret_type
77 }; 78 };
78 79
79 let visibility = RawVisibility::from_ast(db, src.map(|s| s.visibility())); 80 let vis_default = RawVisibility::default_for_container(loc.container);
81 let visibility =
82 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
80 83
81 let sig = FunctionData { name, params, ret_type, has_self_param, visibility }; 84 let sig = FunctionData { name, params, ret_type, has_self_param, visibility };
82 Arc::new(sig) 85 Arc::new(sig)
@@ -105,10 +108,13 @@ impl TypeAliasData {
105 db: &impl DefDatabase, 108 db: &impl DefDatabase,
106 typ: TypeAliasId, 109 typ: TypeAliasId,
107 ) -> Arc<TypeAliasData> { 110 ) -> Arc<TypeAliasData> {
108 let node = typ.lookup(db).source(db); 111 let loc = typ.lookup(db);
112 let node = loc.source(db);
109 let name = node.value.name().map_or_else(Name::missing, |n| n.as_name()); 113 let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
110 let type_ref = node.value.type_ref().map(TypeRef::from_ast); 114 let type_ref = node.value.type_ref().map(TypeRef::from_ast);
111 let visibility = RawVisibility::from_ast(db, node.map(|n| n.visibility())); 115 let vis_default = RawVisibility::default_for_container(loc.container);
116 let visibility =
117 RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
112 Arc::new(TypeAliasData { name, type_ref, visibility }) 118 Arc::new(TypeAliasData { name, type_ref, visibility })
113 } 119 }
114} 120}
@@ -230,22 +236,26 @@ pub struct ConstData {
230 236
231impl ConstData { 237impl ConstData {
232 pub(crate) fn const_data_query(db: &impl DefDatabase, konst: ConstId) -> Arc<ConstData> { 238 pub(crate) fn const_data_query(db: &impl DefDatabase, konst: ConstId) -> Arc<ConstData> {
233 let node = konst.lookup(db).source(db); 239 let loc = konst.lookup(db);
234 Arc::new(ConstData::new(db, node)) 240 let node = loc.source(db);
241 let vis_default = RawVisibility::default_for_container(loc.container);
242 Arc::new(ConstData::new(db, vis_default, node))
235 } 243 }
236 244
237 pub(crate) fn static_data_query(db: &impl DefDatabase, konst: StaticId) -> Arc<ConstData> { 245 pub(crate) fn static_data_query(db: &impl DefDatabase, konst: StaticId) -> Arc<ConstData> {
238 let node = konst.lookup(db).source(db); 246 let node = konst.lookup(db).source(db);
239 Arc::new(ConstData::new(db, node)) 247 Arc::new(ConstData::new(db, RawVisibility::private(), node))
240 } 248 }
241 249
242 fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>( 250 fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>(
243 db: &impl DefDatabase, 251 db: &impl DefDatabase,
252 vis_default: RawVisibility,
244 node: InFile<N>, 253 node: InFile<N>,
245 ) -> ConstData { 254 ) -> ConstData {
246 let name = node.value.name().map(|n| n.as_name()); 255 let name = node.value.name().map(|n| n.as_name());
247 let type_ref = TypeRef::from_ast_opt(node.value.ascribed_type()); 256 let type_ref = TypeRef::from_ast_opt(node.value.ascribed_type());
248 let visibility = RawVisibility::from_ast(db, node.map(|n| n.visibility())); 257 let visibility =
258 RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
249 ConstData { name, type_ref, visibility } 259 ConstData { name, type_ref, visibility }
250 } 260 }
251} 261}
@@ -280,7 +290,7 @@ fn collect_impl_items_in_macro(
280 return Vec::new(); 290 return Vec::new();
281 } 291 }
282 292
283 if let Some((mark, items)) = expander.enter_expand(db, m) { 293 if let Some((mark, items)) = expander.enter_expand(db, None, m) {
284 let items: InFile<ast::MacroItems> = expander.to_source(items); 294 let items: InFile<ast::MacroItems> = expander.to_source(items);
285 let mut res = collect_impl_items( 295 let mut res = collect_impl_items(
286 db, 296 db,
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index 81eac52ad..03515309e 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -177,8 +177,13 @@ pub struct ModuleData {
177 177
178impl CrateDefMap { 178impl CrateDefMap {
179 pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 179 pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
180 let _p = profile("crate_def_map_query") 180 let _p = profile("crate_def_map_query").detail(|| {
181 .detail(|| db.crate_graph()[krate].display_name.clone().unwrap_or_default()); 181 db.crate_graph()[krate]
182 .display_name
183 .as_ref()
184 .map(ToString::to_string)
185 .unwrap_or_default()
186 });
182 let def_map = { 187 let def_map = {
183 let edition = db.crate_graph()[krate].edition; 188 let edition = db.crate_graph()[krate].edition;
184 let mut modules: Arena<LocalModuleId, ModuleData> = Arena::default(); 189 let mut modules: Arena<LocalModuleId, ModuleData> = Arena::default();
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index d0459d9b0..db9838cb5 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -102,6 +102,7 @@ struct MacroDirective {
102 module_id: LocalModuleId, 102 module_id: LocalModuleId,
103 ast_id: AstIdWithPath<ast::MacroCall>, 103 ast_id: AstIdWithPath<ast::MacroCall>,
104 legacy: Option<MacroCallId>, 104 legacy: Option<MacroCallId>,
105 depth: usize,
105} 106}
106 107
107#[derive(Clone, Debug, Eq, PartialEq)] 108#[derive(Clone, Debug, Eq, PartialEq)]
@@ -134,6 +135,7 @@ where
134 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; 135 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
135 ModCollector { 136 ModCollector {
136 def_collector: &mut *self, 137 def_collector: &mut *self,
138 macro_depth: 0,
137 module_id, 139 module_id,
138 file_id: file_id.into(), 140 file_id: file_id.into(),
139 raw_items: &raw_items, 141 raw_items: &raw_items,
@@ -516,7 +518,7 @@ where
516 macros.retain(|directive| { 518 macros.retain(|directive| {
517 if let Some(call_id) = directive.legacy { 519 if let Some(call_id) = directive.legacy {
518 res = ReachedFixedPoint::No; 520 res = ReachedFixedPoint::No;
519 resolved.push((directive.module_id, call_id)); 521 resolved.push((directive.module_id, call_id, directive.depth));
520 return false; 522 return false;
521 } 523 }
522 524
@@ -530,7 +532,7 @@ where
530 ); 532 );
531 resolved_res.resolved_def.take_macros() 533 resolved_res.resolved_def.take_macros()
532 }) { 534 }) {
533 resolved.push((directive.module_id, call_id)); 535 resolved.push((directive.module_id, call_id, directive.depth));
534 res = ReachedFixedPoint::No; 536 res = ReachedFixedPoint::No;
535 return false; 537 return false;
536 } 538 }
@@ -541,7 +543,7 @@ where
541 if let Some(call_id) = 543 if let Some(call_id) =
542 directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path)) 544 directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path))
543 { 545 {
544 resolved.push((directive.module_id, call_id)); 546 resolved.push((directive.module_id, call_id, 0));
545 res = ReachedFixedPoint::No; 547 res = ReachedFixedPoint::No;
546 return false; 548 return false;
547 } 549 }
@@ -552,8 +554,12 @@ where
552 self.unexpanded_macros = macros; 554 self.unexpanded_macros = macros;
553 self.unexpanded_attribute_macros = attribute_macros; 555 self.unexpanded_attribute_macros = attribute_macros;
554 556
555 for (module_id, macro_call_id) in resolved { 557 for (module_id, macro_call_id, depth) in resolved {
556 self.collect_macro_expansion(module_id, macro_call_id); 558 if depth > 1024 {
559 log::debug!("Max macro expansion depth reached");
560 continue;
561 }
562 self.collect_macro_expansion(module_id, macro_call_id, depth);
557 } 563 }
558 564
559 res 565 res
@@ -573,12 +579,18 @@ where
573 None 579 None
574 } 580 }
575 581
576 fn collect_macro_expansion(&mut self, module_id: LocalModuleId, macro_call_id: MacroCallId) { 582 fn collect_macro_expansion(
583 &mut self,
584 module_id: LocalModuleId,
585 macro_call_id: MacroCallId,
586 depth: usize,
587 ) {
577 let file_id: HirFileId = macro_call_id.as_file(); 588 let file_id: HirFileId = macro_call_id.as_file();
578 let raw_items = self.db.raw_items(file_id); 589 let raw_items = self.db.raw_items(file_id);
579 let mod_dir = self.mod_dirs[&module_id].clone(); 590 let mod_dir = self.mod_dirs[&module_id].clone();
580 ModCollector { 591 ModCollector {
581 def_collector: &mut *self, 592 def_collector: &mut *self,
593 macro_depth: depth,
582 file_id, 594 file_id,
583 module_id, 595 module_id,
584 raw_items: &raw_items, 596 raw_items: &raw_items,
@@ -595,6 +607,7 @@ where
595/// Walks a single module, populating defs, imports and macros 607/// Walks a single module, populating defs, imports and macros
596struct ModCollector<'a, D> { 608struct ModCollector<'a, D> {
597 def_collector: D, 609 def_collector: D,
610 macro_depth: usize,
598 module_id: LocalModuleId, 611 module_id: LocalModuleId,
599 file_id: HirFileId, 612 file_id: HirFileId,
600 raw_items: &'a raw::RawItems, 613 raw_items: &'a raw::RawItems,
@@ -684,6 +697,7 @@ where
684 697
685 ModCollector { 698 ModCollector {
686 def_collector: &mut *self.def_collector, 699 def_collector: &mut *self.def_collector,
700 macro_depth: self.macro_depth,
687 module_id, 701 module_id,
688 file_id: self.file_id, 702 file_id: self.file_id,
689 raw_items: self.raw_items, 703 raw_items: self.raw_items,
@@ -713,6 +727,7 @@ where
713 let raw_items = self.def_collector.db.raw_items(file_id.into()); 727 let raw_items = self.def_collector.db.raw_items(file_id.into());
714 ModCollector { 728 ModCollector {
715 def_collector: &mut *self.def_collector, 729 def_collector: &mut *self.def_collector,
730 macro_depth: self.macro_depth,
716 module_id, 731 module_id,
717 file_id: file_id.into(), 732 file_id: file_id.into(),
718 raw_items: &raw_items, 733 raw_items: &raw_items,
@@ -887,6 +902,7 @@ where
887 module_id: self.module_id, 902 module_id: self.module_id,
888 ast_id, 903 ast_id,
889 legacy: Some(macro_call_id), 904 legacy: Some(macro_call_id),
905 depth: self.macro_depth + 1,
890 }); 906 });
891 907
892 return; 908 return;
@@ -902,6 +918,7 @@ where
902 module_id: self.module_id, 918 module_id: self.module_id,
903 ast_id, 919 ast_id,
904 legacy: None, 920 legacy: None,
921 depth: self.macro_depth + 1,
905 }); 922 });
906 } 923 }
907 924
@@ -971,13 +988,26 @@ mod tests {
971 } 988 }
972 989
973 #[test] 990 #[test]
974 fn test_macro_expand_will_stop() { 991 fn test_macro_expand_will_stop_1() {
992 do_resolve(
993 r#"
994 macro_rules! foo {
995 ($($ty:ty)*) => { foo!($($ty)*); }
996 }
997 foo!(KABOOM);
998 "#,
999 );
1000 }
1001
1002 #[ignore] // this test does succeed, but takes quite a while :/
1003 #[test]
1004 fn test_macro_expand_will_stop_2() {
975 do_resolve( 1005 do_resolve(
976 r#" 1006 r#"
977 macro_rules! foo { 1007 macro_rules! foo {
978 ($($ty:ty)*) => { foo!($($ty)*, $($ty)*); } 1008 ($($ty:ty)*) => { foo!($($ty)* $($ty)*); }
979 } 1009 }
980foo!(KABOOM); 1010 foo!(KABOOM);
981 "#, 1011 "#,
982 ); 1012 );
983 } 1013 }
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs
index dda5ed699..3f33a75b9 100644
--- a/crates/ra_hir_def/src/nameres/tests.rs
+++ b/crates/ra_hir_def/src/nameres/tests.rs
@@ -102,6 +102,28 @@ fn crate_def_map_super_super() {
102} 102}
103 103
104#[test] 104#[test]
105fn crate_def_map_fn_mod_same_name() {
106 let map = def_map(
107 "
108 //- /lib.rs
109 mod m {
110 pub mod z {}
111 pub fn z() {}
112 }
113 ",
114 );
115 assert_snapshot!(map, @r###"
116 â‹®crate
117 â‹®m: t
118 â‹®
119 â‹®crate::m
120 â‹®z: t v
121 â‹®
122 â‹®crate::m::z
123 "###)
124}
125
126#[test]
105fn bogus_paths() { 127fn bogus_paths() {
106 covers!(bogus_paths); 128 covers!(bogus_paths);
107 let map = def_map( 129 let map = def_map(
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs
index 2734d51a0..123fae72a 100644
--- a/crates/ra_hir_def/src/resolver.rs
+++ b/crates/ra_hir_def/src/resolver.rs
@@ -381,6 +381,11 @@ impl Resolver {
381 db: &impl DefDatabase, 381 db: &impl DefDatabase,
382 path: &ModPath, 382 path: &ModPath,
383 ) -> Option<MacroDefId> { 383 ) -> Option<MacroDefId> {
384 // Search item scope legacy macro first
385 if let Some(def) = self.resolve_local_macro_def(path) {
386 return Some(def);
387 }
388
384 let (item_map, module) = self.module_scope()?; 389 let (item_map, module) = self.module_scope()?;
385 item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros() 390 item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros()
386 } 391 }
@@ -413,6 +418,16 @@ impl Resolver {
413 }) 418 })
414 } 419 }
415 420
421 fn resolve_local_macro_def(&self, path: &ModPath) -> Option<MacroDefId> {
422 let name = path.as_ident()?;
423 self.scopes.iter().rev().find_map(|scope| {
424 if let Scope::LocalItemsScope(body) = scope {
425 return body.item_scope.get_legacy_macro(name);
426 }
427 None
428 })
429 }
430
416 pub fn module(&self) -> Option<ModuleId> { 431 pub fn module(&self) -> Option<ModuleId> {
417 let (def_map, local_id) = self.module_scope()?; 432 let (def_map, local_id) = self.module_scope()?;
418 Some(ModuleId { krate: def_map.krate, local_id }) 433 Some(ModuleId { krate: def_map.krate, local_id })
diff --git a/crates/ra_hir_def/src/visibility.rs b/crates/ra_hir_def/src/visibility.rs
index d8296da4b..e0c59e905 100644
--- a/crates/ra_hir_def/src/visibility.rs
+++ b/crates/ra_hir_def/src/visibility.rs
@@ -6,7 +6,7 @@ use ra_syntax::ast;
6use crate::{ 6use crate::{
7 db::DefDatabase, 7 db::DefDatabase,
8 path::{ModPath, PathKind}, 8 path::{ModPath, PathKind},
9 ModuleId, 9 AssocContainerId, ModuleId,
10}; 10};
11 11
12/// Visibility of an item, not yet resolved. 12/// Visibility of an item, not yet resolved.
@@ -20,11 +20,30 @@ pub enum RawVisibility {
20} 20}
21 21
22impl RawVisibility { 22impl RawVisibility {
23 const fn private() -> RawVisibility { 23 pub(crate) const fn private() -> RawVisibility {
24 let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() }; 24 let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() };
25 RawVisibility::Module(path) 25 RawVisibility::Module(path)
26 } 26 }
27 27
28 pub(crate) fn default_for_container(container_id: AssocContainerId) -> Self {
29 match container_id {
30 AssocContainerId::TraitId(_) => RawVisibility::Public,
31 _ => RawVisibility::private(),
32 }
33 }
34
35 pub(crate) fn from_ast_with_default(
36 db: &impl DefDatabase,
37 default: RawVisibility,
38 node: InFile<Option<ast::Visibility>>,
39 ) -> RawVisibility {
40 Self::from_ast_with_hygiene_and_default(
41 node.value,
42 default,
43 &Hygiene::new(db, node.file_id),
44 )
45 }
46
28 pub(crate) fn from_ast( 47 pub(crate) fn from_ast(
29 db: &impl DefDatabase, 48 db: &impl DefDatabase,
30 node: InFile<Option<ast::Visibility>>, 49 node: InFile<Option<ast::Visibility>>,
@@ -36,8 +55,16 @@ impl RawVisibility {
36 node: Option<ast::Visibility>, 55 node: Option<ast::Visibility>,
37 hygiene: &Hygiene, 56 hygiene: &Hygiene,
38 ) -> RawVisibility { 57 ) -> RawVisibility {
58 Self::from_ast_with_hygiene_and_default(node, RawVisibility::private(), hygiene)
59 }
60
61 pub(crate) fn from_ast_with_hygiene_and_default(
62 node: Option<ast::Visibility>,
63 default: RawVisibility,
64 hygiene: &Hygiene,
65 ) -> RawVisibility {
39 let node = match node { 66 let node = match node {
40 None => return RawVisibility::private(), 67 None => return default,
41 Some(node) => node, 68 Some(node) => node,
42 }; 69 };
43 match node.kind() { 70 match node.kind() {
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 3fce73e8a..7b72eb7a0 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -17,7 +17,7 @@ pub mod eager;
17use std::hash::Hash; 17use std::hash::Hash;
18use std::sync::Arc; 18use std::sync::Arc;
19 19
20use ra_db::{salsa, CrateId, FileId}; 20use ra_db::{impl_intern_key, salsa, CrateId, FileId};
21use ra_syntax::{ 21use ra_syntax::{
22 algo, 22 algo,
23 ast::{self, AstNode}, 23 ast::{self, AstNode},
@@ -174,25 +174,11 @@ pub enum MacroCallId {
174 174
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
176pub struct LazyMacroId(salsa::InternId); 176pub struct LazyMacroId(salsa::InternId);
177impl salsa::InternKey for LazyMacroId { 177impl_intern_key!(LazyMacroId);
178 fn from_intern_id(v: salsa::InternId) -> Self {
179 LazyMacroId(v)
180 }
181 fn as_intern_id(&self) -> salsa::InternId {
182 self.0
183 }
184}
185 178
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 179#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
187pub struct EagerMacroId(salsa::InternId); 180pub struct EagerMacroId(salsa::InternId);
188impl salsa::InternKey for EagerMacroId { 181impl_intern_key!(EagerMacroId);
189 fn from_intern_id(v: salsa::InternId) -> Self {
190 EagerMacroId(v)
191 }
192 fn as_intern_id(&self) -> salsa::InternId {
193 self.0
194 }
195}
196 182
197impl From<LazyMacroId> for MacroCallId { 183impl From<LazyMacroId> for MacroCallId {
198 fn from(it: LazyMacroId) -> Self { 184 fn from(it: LazyMacroId) -> Self {
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 32457bbf7..3b7022ad5 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -363,6 +363,26 @@ fn main() {
363} 363}
364 364
365#[test] 365#[test]
366fn infer_local_macro() {
367 assert_snapshot!(
368 infer(r#"
369fn main() {
370 macro_rules! foo {
371 () => { 1usize }
372 }
373 let _a = foo!();
374}
375"#),
376 @r###"
377 ![0; 6) '1usize': usize
378 [11; 90) '{ ...!(); }': ()
379 [17; 66) 'macro_... }': {unknown}
380 [75; 77) '_a': usize
381 "###
382 );
383}
384
385#[test]
366fn infer_builtin_macros_line() { 386fn infer_builtin_macros_line() {
367 assert_snapshot!( 387 assert_snapshot!(
368 infer(r#" 388 infer(r#"
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index 2b35a3803..39d09a07f 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -544,6 +544,20 @@ fn main() {
544 } 544 }
545 545
546 #[test] 546 #[test]
547 fn generic_struct() {
548 let info = call_info(
549 r#"
550struct TS<T>(T);
551fn main() {
552 let s = TS(<|>);
553}"#,
554 );
555
556 assert_eq!(info.label(), "struct TS<T>(T) -> TS");
557 assert_eq!(info.active_parameter, Some(0));
558 }
559
560 #[test]
547 #[should_panic] 561 #[should_panic]
548 fn cant_call_named_structs() { 562 fn cant_call_named_structs() {
549 let _ = call_info( 563 let _ = call_info(
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 81e5037aa..f07611d88 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -402,6 +402,38 @@ mod tests {
402 } 402 }
403 403
404 #[test] 404 #[test]
405 fn completes_trait_method_from_other_module() {
406 assert_debug_snapshot!(
407 do_ref_completion(
408 r"
409 struct A {}
410 mod m {
411 pub trait Trait { fn the_method(&self); }
412 }
413 use m::Trait;
414 impl Trait for A {}
415 fn foo(a: A) {
416 a.<|>
417 }
418 ",
419 ),
420 @r###"
421 [
422 CompletionItem {
423 label: "the_method()",
424 source_range: [219; 219),
425 delete: [219; 219),
426 insert: "the_method()$0",
427 kind: Method,
428 lookup: "the_method",
429 detail: "fn the_method(&self)",
430 },
431 ]
432 "###
433 );
434 }
435
436 #[test]
405 fn test_no_non_self_method() { 437 fn test_no_non_self_method() {
406 assert_debug_snapshot!( 438 assert_debug_snapshot!(
407 do_ref_completion( 439 do_ref_completion(
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs
index d588ee364..3db17f15f 100644
--- a/crates/ra_ide/src/completion/complete_path.rs
+++ b/crates/ra_ide/src/completion/complete_path.rs
@@ -967,4 +967,74 @@ mod tests {
967 ] 967 ]
968 "###); 968 "###);
969 } 969 }
970
971 #[test]
972 fn function_mod_share_name() {
973 assert_debug_snapshot!(
974 do_reference_completion(
975 r"
976 fn foo() {
977 self::m::<|>
978 }
979
980 mod m {
981 pub mod z {}
982 pub fn z() {}
983 }
984 ",
985 ),
986 @r###"
987 [
988 CompletionItem {
989 label: "z",
990 source_range: [57; 57),
991 delete: [57; 57),
992 insert: "z",
993 kind: Module,
994 },
995 CompletionItem {
996 label: "z()",
997 source_range: [57; 57),
998 delete: [57; 57),
999 insert: "z()$0",
1000 kind: Function,
1001 lookup: "z",
1002 detail: "pub fn z()",
1003 },
1004 ]
1005 "###
1006 );
1007 }
1008
1009 #[test]
1010 fn completes_hashmap_new() {
1011 assert_debug_snapshot!(
1012 do_reference_completion(
1013 r"
1014 struct RandomState;
1015 struct HashMap<K, V, S = RandomState> {}
1016
1017 impl<K, V> HashMap<K, V, RandomState> {
1018 pub fn new() -> HashMap<K, V, RandomState> { }
1019 }
1020 fn foo() {
1021 HashMap::<|>
1022 }
1023 "
1024 ),
1025 @r###"
1026 [
1027 CompletionItem {
1028 label: "new()",
1029 source_range: [292; 292),
1030 delete: [292; 292),
1031 insert: "new()$0",
1032 kind: Function,
1033 lookup: "new",
1034 detail: "pub fn new() -> HashMap<K, V, RandomState>",
1035 },
1036 ]
1037 "###
1038 );
1039 }
970} 1040}
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index a4e9aefe2..253848602 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -1,6 +1,6 @@
1//! This modules takes care of rendering various definitions as completion items. 1//! This modules takes care of rendering various definitions as completion items.
2 2
3use hir::{db::HirDatabase, Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type}; 3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type};
4use join_to_string::join; 4use join_to_string::join;
5use ra_syntax::ast::NameOwner; 5use ra_syntax::ast::NameOwner;
6use test_utils::tested_by; 6use test_utils::tested_by;
@@ -9,7 +9,10 @@ use crate::completion::{
9 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 9 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
10}; 10};
11 11
12use crate::display::{const_label, macro_label, type_label, FunctionSignature}; 12use crate::{
13 display::{const_label, macro_label, type_label, FunctionSignature},
14 RootDatabase,
15};
13 16
14impl Completions { 17impl Completions {
15 pub(crate) fn add_field( 18 pub(crate) fn add_field(
@@ -273,8 +276,10 @@ impl Completions {
273 pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) { 276 pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) {
274 let is_deprecated = is_deprecated(variant, ctx.db); 277 let is_deprecated = is_deprecated(variant, ctx.db);
275 let name = variant.name(ctx.db); 278 let name = variant.name(ctx.db);
276 let detail_types = 279 let detail_types = variant
277 variant.fields(ctx.db).into_iter().map(|field| (field.name(ctx.db), field.ty(ctx.db))); 280 .fields(ctx.db)
281 .into_iter()
282 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
278 let detail = match variant.kind(ctx.db) { 283 let detail = match variant.kind(ctx.db) {
279 StructKind::Tuple | StructKind::Unit => { 284 StructKind::Tuple | StructKind::Unit => {
280 join(detail_types.map(|(_, t)| t.display(ctx.db).to_string())) 285 join(detail_types.map(|(_, t)| t.display(ctx.db).to_string()))
@@ -298,7 +303,7 @@ impl Completions {
298 } 303 }
299} 304}
300 305
301fn is_deprecated(node: impl HasAttrs, db: &impl HirDatabase) -> bool { 306fn is_deprecated(node: impl HasAttrs, db: &RootDatabase) -> bool {
302 node.attrs(db).by_key("deprecated").exists() 307 node.attrs(db).by_key("deprecated").exists()
303} 308}
304 309
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index eaeaaa2b4..c395057a7 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -6,6 +6,8 @@ mod navigation_target;
6mod structure; 6mod structure;
7mod short_label; 7mod short_label;
8 8
9use std::fmt::{Display, Write};
10
9use ra_syntax::{ 11use ra_syntax::{
10 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, 12 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
11 SyntaxKind::{ATTR, COMMENT}, 13 SyntaxKind::{ATTR, COMMENT},
@@ -67,24 +69,27 @@ pub(crate) fn macro_label(node: &ast::MacroCall) -> String {
67 format!("{}macro_rules! {}", vis, name) 69 format!("{}macro_rules! {}", vis, name)
68} 70}
69 71
70pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { 72pub(crate) fn rust_code_markup(code: &impl Display) -> String {
71 rust_code_markup_with_doc::<_, &str>(val, None, None) 73 rust_code_markup_with_doc(code, None, None)
72} 74}
73 75
74pub(crate) fn rust_code_markup_with_doc<CODE, DOC>( 76pub(crate) fn rust_code_markup_with_doc(
75 val: CODE, 77 code: &impl Display,
76 doc: Option<DOC>, 78 doc: Option<&str>,
77 mod_path: Option<String>, 79 mod_path: Option<&str>,
78) -> String 80) -> String {
79where 81 let mut markup = "```rust\n".to_owned();
80 CODE: AsRef<str>, 82
81 DOC: AsRef<str>, 83 if let Some(mod_path) = mod_path {
82{ 84 if !mod_path.is_empty() {
83 let mod_path = 85 write!(markup, "{}\n", mod_path).unwrap();
84 mod_path.filter(|path| !path.is_empty()).map(|path| path + "\n").unwrap_or_default(); 86 }
87 }
88 write!(markup, "{}\n```", code).unwrap();
89
85 if let Some(doc) = doc { 90 if let Some(doc) = doc {
86 format!("```rust\n{}{}\n```\n\n{}", mod_path, val.as_ref(), doc.as_ref()) 91 write!(markup, "\n\n{}", doc).unwrap();
87 } else {
88 format!("```rust\n{}{}\n```", mod_path, val.as_ref())
89 } 92 }
93
94 markup
90} 95}
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 2c4c932de..ec1bbd5a0 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -64,7 +64,7 @@ impl FunctionSignature {
64 .fields(db) 64 .fields(db)
65 .into_iter() 65 .into_iter()
66 .map(|field: hir::StructField| { 66 .map(|field: hir::StructField| {
67 let ty = field.ty(db); 67 let ty = field.signature_ty(db);
68 format!("{}", ty.display(db)) 68 format!("{}", ty.display(db))
69 }) 69 })
70 .collect(); 70 .collect();
@@ -102,7 +102,7 @@ impl FunctionSignature {
102 .into_iter() 102 .into_iter()
103 .map(|field: hir::StructField| { 103 .map(|field: hir::StructField| {
104 let name = field.name(db); 104 let name = field.name(db);
105 let ty = field.ty(db); 105 let ty = field.signature_ty(db);
106 format!("{}: {}", name, ty.display(db)) 106 format!("{}: {}", name, ty.display(db))
107 }) 107 })
108 .collect(); 108 .collect();
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index a55a13ffc..a7be92ce3 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -788,6 +788,21 @@ mod tests {
788 } 788 }
789 789
790 #[test] 790 #[test]
791 fn goto_def_in_local_macro() {
792 check_goto(
793 "
794 //- /lib.rs
795 fn bar() {
796 macro_rules! foo { () => { () } }
797 <|>foo!();
798 }
799 ",
800 "foo MACRO_CALL FileId(1) [15; 48) [28; 31)",
801 "macro_rules! foo { () => { () } }|foo",
802 );
803 }
804
805 #[test]
791 fn goto_def_for_field_init_shorthand() { 806 fn goto_def_for_field_init_shorthand() {
792 covers!(ra_ide_db::goto_def_for_field_init_shorthand); 807 covers!(ra_ide_db::goto_def_for_field_init_shorthand);
793 check_goto( 808 check_goto(
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 0bbba4855..5b3760c18 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,4 +1,5 @@
1//! FIXME: write short doc here 1//! Logic for computing info that is displayed when the user hovers over any
2//! source code items (e.g. function call, struct field, variable symbol...)
2 3
3use hir::{ 4use hir::{
4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 5 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef,
@@ -24,35 +25,20 @@ use itertools::Itertools;
24use std::iter::once; 25use std::iter::once;
25 26
26/// Contains the results when hovering over an item 27/// Contains the results when hovering over an item
27#[derive(Debug, Clone)] 28#[derive(Debug, Default)]
28pub struct HoverResult { 29pub struct HoverResult {
29 results: Vec<String>, 30 results: Vec<String>,
30 exact: bool,
31}
32
33impl Default for HoverResult {
34 fn default() -> Self {
35 HoverResult::new()
36 }
37} 31}
38 32
39impl HoverResult { 33impl HoverResult {
40 pub fn new() -> HoverResult { 34 pub fn new() -> HoverResult {
41 HoverResult { 35 Self::default()
42 results: Vec::new(),
43 // We assume exact by default
44 exact: true,
45 }
46 } 36 }
47 37
48 pub fn extend(&mut self, item: Option<String>) { 38 pub fn extend(&mut self, item: Option<String>) {
49 self.results.extend(item); 39 self.results.extend(item);
50 } 40 }
51 41
52 pub fn is_exact(&self) -> bool {
53 self.exact
54 }
55
56 pub fn is_empty(&self) -> bool { 42 pub fn is_empty(&self) -> bool {
57 self.results.is_empty() 43 self.results.is_empty()
58 } 44 }
@@ -72,20 +58,7 @@ impl HoverResult {
72 /// Returns the results converted into markup 58 /// Returns the results converted into markup
73 /// for displaying in a UI 59 /// for displaying in a UI
74 pub fn to_markup(&self) -> String { 60 pub fn to_markup(&self) -> String {
75 let mut markup = if !self.exact { 61 self.results.join("\n\n---\n")
76 let mut msg = String::from("Failed to exactly resolve the symbol. This is probably because rust_analyzer does not yet support traits.");
77 if !self.results.is_empty() {
78 msg.push_str(" \nThese items were found instead:");
79 }
80 msg.push_str("\n\n---\n");
81 msg
82 } else {
83 String::new()
84 };
85
86 markup.push_str(&self.results.join("\n\n---\n"));
87
88 markup
89 } 62 }
90} 63}
91 64
@@ -94,10 +67,10 @@ fn hover_text(
94 desc: Option<String>, 67 desc: Option<String>,
95 mod_path: Option<String>, 68 mod_path: Option<String>,
96) -> Option<String> { 69) -> Option<String> {
97 match (desc, docs, mod_path) { 70 if let Some(desc) = desc {
98 (Some(desc), docs, mod_path) => Some(rust_code_markup_with_doc(desc, docs, mod_path)), 71 Some(rust_code_markup_with_doc(&desc, docs.as_deref(), mod_path.as_deref()))
99 (None, Some(docs), _) => Some(docs), 72 } else {
100 _ => None, 73 docs
101 } 74 }
102} 75}
103 76
@@ -121,7 +94,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
121 94
122fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { 95fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
123 let mod_path = def.module(db).map(|module| { 96 let mod_path = def.module(db).map(|module| {
124 once(db.crate_graph()[module.krate().into()].display_name.clone()) 97 once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string))
125 .chain( 98 .chain(
126 module 99 module
127 .path_to_root(db) 100 .path_to_root(db)
@@ -133,7 +106,7 @@ fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
133 .flatten() 106 .flatten()
134 .join("::") 107 .join("::")
135 }); 108 });
136 mod_path 109 mod_path // FIXME: replace dashes with underscores in crate display name
137} 110}
138 111
139fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { 112fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> {
@@ -170,9 +143,7 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
170 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 143 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
171 ModuleDef::BuiltinType(it) => Some(it.to_string()), 144 ModuleDef::BuiltinType(it) => Some(it.to_string()),
172 }, 145 },
173 Definition::Local(it) => { 146 Definition::Local(it) => Some(rust_code_markup(&it.ty(db).display_truncated(db, None))),
174 Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string()))
175 }
176 Definition::TypeParam(_) | Definition::SelfType(_) => { 147 Definition::TypeParam(_) | Definition::SelfType(_) => {
177 // FIXME: Hover for generic param 148 // FIXME: Hover for generic param
178 None 149 None
@@ -237,7 +208,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
237 } 208 }
238 }?; 209 }?;
239 210
240 res.extend(Some(rust_code_markup(ty.display_truncated(db, None).to_string()))); 211 res.extend(Some(rust_code_markup(&ty.display_truncated(db, None))));
241 let range = sema.original_range(&node).range; 212 let range = sema.original_range(&node).range;
242 Some(RangeInfo::new(range, res)) 213 Some(RangeInfo::new(range, res))
243} 214}
@@ -595,7 +566,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
595 ); 566 );
596 let hover = analysis.hover(position).unwrap().unwrap(); 567 let hover = analysis.hover(position).unwrap().unwrap();
597 assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing")); 568 assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing"));
598 assert_eq!(hover.info.is_exact(), true);
599 } 569 }
600 570
601 #[test] 571 #[test]
@@ -618,7 +588,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
618 ); 588 );
619 let hover = analysis.hover(position).unwrap().unwrap(); 589 let hover = analysis.hover(position).unwrap().unwrap();
620 assert_eq!(trim_markup_opt(hover.info.first()), Some("const C: u32")); 590 assert_eq!(trim_markup_opt(hover.info.first()), Some("const C: u32"));
621 assert_eq!(hover.info.is_exact(), true);
622 } 591 }
623 592
624 #[test] 593 #[test]
@@ -635,7 +604,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
635 ); 604 );
636 let hover = analysis.hover(position).unwrap().unwrap(); 605 let hover = analysis.hover(position).unwrap().unwrap();
637 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 606 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing"));
638 assert_eq!(hover.info.is_exact(), true);
639 607
640 /* FIXME: revive these tests 608 /* FIXME: revive these tests
641 let (analysis, position) = single_file_with_position( 609 let (analysis, position) = single_file_with_position(
@@ -651,7 +619,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
651 619
652 let hover = analysis.hover(position).unwrap().unwrap(); 620 let hover = analysis.hover(position).unwrap().unwrap();
653 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 621 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing"));
654 assert_eq!(hover.info.is_exact(), true);
655 622
656 let (analysis, position) = single_file_with_position( 623 let (analysis, position) = single_file_with_position(
657 " 624 "
@@ -665,7 +632,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
665 ); 632 );
666 let hover = analysis.hover(position).unwrap().unwrap(); 633 let hover = analysis.hover(position).unwrap().unwrap();
667 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing")); 634 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing"));
668 assert_eq!(hover.info.is_exact(), true);
669 635
670 let (analysis, position) = single_file_with_position( 636 let (analysis, position) = single_file_with_position(
671 " 637 "
@@ -678,7 +644,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
678 ); 644 );
679 let hover = analysis.hover(position).unwrap().unwrap(); 645 let hover = analysis.hover(position).unwrap().unwrap();
680 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing")); 646 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing"));
681 assert_eq!(hover.info.is_exact(), true);
682 */ 647 */
683 } 648 }
684 649
@@ -696,7 +661,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
696 ); 661 );
697 let hover = analysis.hover(position).unwrap().unwrap(); 662 let hover = analysis.hover(position).unwrap().unwrap();
698 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 663 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32"));
699 assert_eq!(hover.info.is_exact(), true);
700 } 664 }
701 665
702 #[test] 666 #[test]
@@ -714,7 +678,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
714 ); 678 );
715 let hover = analysis.hover(position).unwrap().unwrap(); 679 let hover = analysis.hover(position).unwrap().unwrap();
716 assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo")); 680 assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo"));
717 assert_eq!(hover.info.is_exact(), true);
718 } 681 }
719 682
720 #[test] 683 #[test]
@@ -726,7 +689,6 @@ fn func(foo: i32) { if true { <|>foo; }; }
726 ); 689 );
727 let hover = analysis.hover(position).unwrap().unwrap(); 690 let hover = analysis.hover(position).unwrap().unwrap();
728 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 691 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32"));
729 assert_eq!(hover.info.is_exact(), true);
730 } 692 }
731 693
732 #[test] 694 #[test]
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index e9af80b6c..5ab06c6cf 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -473,9 +473,10 @@ impl Analysis {
473 pub fn structural_search_replace( 473 pub fn structural_search_replace(
474 &self, 474 &self,
475 query: &str, 475 query: &str,
476 parse_only: bool,
476 ) -> Cancelable<Result<SourceChange, SsrError>> { 477 ) -> Cancelable<Result<SourceChange, SsrError>> {
477 self.with_db(|db| { 478 self.with_db(|db| {
478 let edits = ssr::parse_search_replace(query, db)?; 479 let edits = ssr::parse_search_replace(query, parse_only, db)?;
479 Ok(SourceChange::source_file_edits("ssr", edits)) 480 Ok(SourceChange::source_file_edits("ssr", edits))
480 }) 481 })
481 } 482 }
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index 25816cf6f..2cf77a31f 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -109,7 +109,7 @@ impl MockAnalysis {
109 let other_crate = crate_graph.add_crate_root( 109 let other_crate = crate_graph.add_crate_root(
110 file_id, 110 file_id,
111 Edition2018, 111 Edition2018,
112 Some(crate_name.to_owned()), 112 Some(CrateName::new(crate_name).unwrap()),
113 cfg_options, 113 cfg_options,
114 Env::default(), 114 Env::default(),
115 Default::default(), 115 Default::default(),
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index c011a2e74..1c9710a5d 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -1,8 +1,10 @@
1//! structural search replace 1//! structural search replace
2 2
3use crate::source_change::SourceFileEdit; 3use crate::source_change::SourceFileEdit;
4use ra_db::{SourceDatabase, SourceDatabaseExt};
5use ra_ide_db::symbol_index::SymbolsDatabase;
4use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
5use ra_syntax::ast::make::expr_from_text; 7use ra_syntax::ast::make::try_expr_from_text;
6use ra_syntax::ast::{AstToken, Comment}; 8use ra_syntax::ast::{AstToken, Comment};
7use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; 9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode};
8use ra_text_edit::{TextEdit, TextEditBuilder}; 10use ra_text_edit::{TextEdit, TextEditBuilder};
@@ -10,9 +12,6 @@ use rustc_hash::FxHashMap;
10use std::collections::HashMap; 12use std::collections::HashMap;
11use std::str::FromStr; 13use std::str::FromStr;
12 14
13pub use ra_db::{SourceDatabase, SourceDatabaseExt};
14use ra_ide_db::symbol_index::SymbolsDatabase;
15
16#[derive(Debug, PartialEq)] 15#[derive(Debug, PartialEq)]
17pub struct SsrError(String); 16pub struct SsrError(String);
18 17
@@ -26,14 +25,17 @@ impl std::error::Error for SsrError {}
26 25
27pub fn parse_search_replace( 26pub fn parse_search_replace(
28 query: &str, 27 query: &str,
28 parse_only: bool,
29 db: &RootDatabase, 29 db: &RootDatabase,
30) -> Result<Vec<SourceFileEdit>, SsrError> { 30) -> Result<Vec<SourceFileEdit>, SsrError> {
31 let mut edits = vec![]; 31 let mut edits = vec![];
32 let query: SsrQuery = query.parse()?; 32 let query: SsrQuery = query.parse()?;
33 if parse_only {
34 return Ok(edits);
35 }
33 for &root in db.local_roots().iter() { 36 for &root in db.local_roots().iter() {
34 let sr = db.source_root(root); 37 let sr = db.source_root(root);
35 for file_id in sr.walk() { 38 for file_id in sr.walk() {
36 dbg!(db.file_relative_path(file_id));
37 let matches = find(&query.pattern, db.parse(file_id).tree().syntax()); 39 let matches = find(&query.pattern, db.parse(file_id).tree().syntax());
38 if !matches.matches.is_empty() { 40 if !matches.matches.is_empty() {
39 edits.push(SourceFileEdit { file_id, edit: replace(&matches, &query.template) }); 41 edits.push(SourceFileEdit { file_id, edit: replace(&matches, &query.template) });
@@ -106,7 +108,10 @@ impl FromStr for SsrQuery {
106 template = replace_in_template(template, var, new_var); 108 template = replace_in_template(template, var, new_var);
107 } 109 }
108 110
109 let template = expr_from_text(&template).syntax().clone(); 111 let template = try_expr_from_text(&template)
112 .ok_or(SsrError("Template is not an expression".into()))?
113 .syntax()
114 .clone();
110 let mut placeholders = FxHashMap::default(); 115 let mut placeholders = FxHashMap::default();
111 116
112 traverse(&template, &mut |n| { 117 traverse(&template, &mut |n| {
@@ -118,7 +123,13 @@ impl FromStr for SsrQuery {
118 } 123 }
119 }); 124 });
120 125
121 let pattern = SsrPattern { pattern: expr_from_text(&pattern).syntax().clone(), vars }; 126 let pattern = SsrPattern {
127 pattern: try_expr_from_text(&pattern)
128 .ok_or(SsrError("Pattern is not an expression".into()))?
129 .syntax()
130 .clone(),
131 vars,
132 };
122 let template = SsrTemplate { template, placeholders }; 133 let template = SsrTemplate { template, placeholders };
123 Ok(SsrQuery { pattern, template }) 134 Ok(SsrQuery { pattern, template })
124 } 135 }
@@ -284,7 +295,6 @@ mod tests {
284 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a"); 295 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a");
285 assert_eq!(result.pattern.vars[1].0, "__search_pattern_b"); 296 assert_eq!(result.pattern.vars[1].0, "__search_pattern_b");
286 assert_eq!(&result.template.template.text(), "bar(__search_pattern_b, __search_pattern_a)"); 297 assert_eq!(&result.template.template.text(), "bar(__search_pattern_b, __search_pattern_a)");
287 dbg!(result.template.placeholders);
288 } 298 }
289 299
290 #[test] 300 #[test]
@@ -335,6 +345,16 @@ mod tests {
335 } 345 }
336 346
337 #[test] 347 #[test]
348 fn parser_invlid_pattern() {
349 assert_eq!(parse_error_text(" ==>> ()"), "Parse error: Pattern is not an expression");
350 }
351
352 #[test]
353 fn parser_invlid_template() {
354 assert_eq!(parse_error_text("() ==>> )"), "Parse error: Template is not an expression");
355 }
356
357 #[test]
338 fn parse_match_replace() { 358 fn parse_match_replace() {
339 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap(); 359 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap();
340 let input = "fn main() { foo(1+2); }"; 360 let input = "fn main() { foo(1+2); }";
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 53c65f8bc..cb2cd2479 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -213,14 +213,14 @@ fn foo() {
213 type_char( 213 type_char(
214 '.', 214 '.',
215 r" 215 r"
216 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 216 fn main() {
217 self.child_impl(db, name) 217 xs.foo()
218 <|> 218 <|>
219 } 219 }
220 ", 220 ",
221 r" 221 r"
222 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 222 fn main() {
223 self.child_impl(db, name) 223 xs.foo()
224 . 224 .
225 } 225 }
226 ", 226 ",
@@ -228,8 +228,8 @@ fn foo() {
228 type_char_noop( 228 type_char_noop(
229 '.', 229 '.',
230 r" 230 r"
231 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 231 fn main() {
232 self.child_impl(db, name) 232 xs.foo()
233 <|> 233 <|>
234 } 234 }
235 ", 235 ",
@@ -241,14 +241,14 @@ fn foo() {
241 type_char( 241 type_char(
242 '.', 242 '.',
243 r" 243 r"
244 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 244 fn main() {
245 self.child_impl(db, name) 245 xs.foo()
246 <|>; 246 <|>;
247 } 247 }
248 ", 248 ",
249 r" 249 r"
250 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 250 fn main() {
251 self.child_impl(db, name) 251 xs.foo()
252 .; 252 .;
253 } 253 }
254 ", 254 ",
@@ -256,8 +256,8 @@ fn foo() {
256 type_char_noop( 256 type_char_noop(
257 '.', 257 '.',
258 r" 258 r"
259 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 259 fn main() {
260 self.child_impl(db, name) 260 xs.foo()
261 <|>; 261 <|>;
262 } 262 }
263 ", 263 ",
@@ -269,15 +269,15 @@ fn foo() {
269 type_char( 269 type_char(
270 '.', 270 '.',
271 r" 271 r"
272 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 272 fn main() {
273 self.child_impl(db, name) 273 xs.foo()
274 .first() 274 .first()
275 <|> 275 <|>
276 } 276 }
277 ", 277 ",
278 r" 278 r"
279 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 279 fn main() {
280 self.child_impl(db, name) 280 xs.foo()
281 .first() 281 .first()
282 . 282 .
283 } 283 }
@@ -286,8 +286,8 @@ fn foo() {
286 type_char_noop( 286 type_char_noop(
287 '.', 287 '.',
288 r" 288 r"
289 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 289 fn main() {
290 self.child_impl(db, name) 290 xs.foo()
291 .first() 291 .first()
292 <|> 292 <|>
293 } 293 }
@@ -334,7 +334,7 @@ fn foo() {
334 type_char_noop( 334 type_char_noop(
335 '.', 335 '.',
336 r" 336 r"
337 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 337 fn main() {
338 <|> 338 <|>
339 } 339 }
340 ", 340 ",
@@ -342,7 +342,7 @@ fn foo() {
342 type_char_noop( 342 type_char_noop(
343 '.', 343 '.',
344 r" 344 r"
345 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { 345 fn main() {
346 <|> 346 <|>
347 } 347 }
348 ", 348 ",
diff --git a/crates/ra_parser/src/grammar/params.rs b/crates/ra_parser/src/grammar/params.rs
index 272661b1d..f0da173cc 100644
--- a/crates/ra_parser/src/grammar/params.rs
+++ b/crates/ra_parser/src/grammar/params.rs
@@ -56,21 +56,17 @@ fn list_(p: &mut Parser, flavor: Flavor) {
56 // fn f(#[attr1] pat: Type) {} 56 // fn f(#[attr1] pat: Type) {}
57 attributes::outer_attributes(p); 57 attributes::outer_attributes(p);
58 58
59 // test param_list_vararg
60 // extern "C" { fn printf(format: *const i8, ...) -> i32; }
61 match flavor {
62 FnDef | FnPointer if p.eat(T![...]) => break,
63 _ => (),
64 }
65
66 if !p.at_ts(VALUE_PARAMETER_FIRST) { 59 if !p.at_ts(VALUE_PARAMETER_FIRST) {
67 p.error("expected value parameter"); 60 p.error("expected value parameter");
68 break; 61 break;
69 } 62 }
70 value_parameter(p, flavor); 63 let param = value_parameter(p, flavor);
71 if !p.at(ket) { 64 if !p.at(ket) {
72 p.expect(T![,]); 65 p.expect(T![,]);
73 } 66 }
67 if let Variadic(true) = param {
68 break;
69 }
74 } 70 }
75 71
76 p.expect(ket); 72 p.expect(ket);
@@ -79,32 +75,25 @@ fn list_(p: &mut Parser, flavor: Flavor) {
79 75
80const VALUE_PARAMETER_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST); 76const VALUE_PARAMETER_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST);
81 77
82fn value_parameter(p: &mut Parser, flavor: Flavor) { 78struct Variadic(bool);
79
80fn value_parameter(p: &mut Parser, flavor: Flavor) -> Variadic {
81 let mut res = Variadic(false);
83 let m = p.start(); 82 let m = p.start();
84 match flavor { 83 match flavor {
85 // test trait_fn_placeholder_parameter 84 // test param_list_vararg
86 // trait Foo { 85 // extern "C" { fn printf(format: *const i8, ...) -> i32; }
87 // fn bar(_: u64, mut x: i32); 86 Flavor::FnDef | Flavor::FnPointer if p.eat(T![...]) => res = Variadic(true),
88 // }
89
90 // test trait_fn_patterns
91 // trait T {
92 // fn f1((a, b): (usize, usize)) {}
93 // fn f2(S { a, b }: S) {}
94 // fn f3(NewType(a): NewType) {}
95 // fn f4(&&a: &&usize) {}
96 // }
97 87
98 // test fn_patterns 88 // test fn_def_param
99 // impl U { 89 // fn foo((x, y): (i32, i32)) {}
100 // fn f1((a, b): (usize, usize)) {}
101 // fn f2(S { a, b }: S) {}
102 // fn f3(NewType(a): NewType) {}
103 // fn f4(&&a: &&usize) {}
104 // }
105 Flavor::FnDef => { 90 Flavor::FnDef => {
106 patterns::pattern(p); 91 patterns::pattern(p);
107 types::ascription(p); 92 if variadic_param(p) {
93 res = Variadic(true)
94 } else {
95 types::ascription(p);
96 }
108 } 97 }
109 // test value_parameters_no_patterns 98 // test value_parameters_no_patterns
110 // type F = Box<Fn(i32, &i32, &i32, ())>; 99 // type F = Box<Fn(i32, &i32, &i32, ())>;
@@ -120,7 +109,11 @@ fn value_parameter(p: &mut Parser, flavor: Flavor) {
120 Flavor::FnPointer => { 109 Flavor::FnPointer => {
121 if (p.at(IDENT) || p.at(UNDERSCORE)) && p.nth(1) == T![:] && !p.nth_at(1, T![::]) { 110 if (p.at(IDENT) || p.at(UNDERSCORE)) && p.nth(1) == T![:] && !p.nth_at(1, T![::]) {
122 patterns::pattern_single(p); 111 patterns::pattern_single(p);
123 types::ascription(p); 112 if variadic_param(p) {
113 res = Variadic(true)
114 } else {
115 types::ascription(p);
116 }
124 } else { 117 } else {
125 types::type_(p); 118 types::type_(p);
126 } 119 }
@@ -137,6 +130,17 @@ fn value_parameter(p: &mut Parser, flavor: Flavor) {
137 } 130 }
138 } 131 }
139 m.complete(p, PARAM); 132 m.complete(p, PARAM);
133 res
134}
135
136fn variadic_param(p: &mut Parser) -> bool {
137 if p.at(T![:]) && p.nth_at(1, T![...]) {
138 p.bump(T![:]);
139 p.bump(T![...]);
140 true
141 } else {
142 false
143 }
140} 144}
141 145
142// test self_param 146// test self_param
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index a6274709d..897874813 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -245,7 +245,10 @@ impl ProjectWorkspace {
245 let crate_id = crate_graph.add_crate_root( 245 let crate_id = crate_graph.add_crate_root(
246 file_id, 246 file_id,
247 Edition::Edition2018, 247 Edition::Edition2018,
248 Some(krate.name(&sysroot).to_string()), 248 Some(
249 CrateName::new(krate.name(&sysroot))
250 .expect("Sysroot crate names should not contain dashes"),
251 ),
249 cfg_options, 252 cfg_options,
250 env, 253 env,
251 extern_source, 254 extern_source,
@@ -296,7 +299,7 @@ impl ProjectWorkspace {
296 let crate_id = crate_graph.add_crate_root( 299 let crate_id = crate_graph.add_crate_root(
297 file_id, 300 file_id,
298 edition, 301 edition,
299 Some(pkg.name(&cargo).to_string()), 302 Some(CrateName::normalize_dashes(pkg.name(&cargo))),
300 cfg_options, 303 cfg_options,
301 env, 304 env,
302 extern_source, 305 extern_source,
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index ae8829807..9f6f1cc53 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -112,10 +112,14 @@ pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
112 let token = token(op); 112 let token = token(op);
113 expr_from_text(&format!("{}{}", token, expr.syntax())) 113 expr_from_text(&format!("{}{}", token, expr.syntax()))
114} 114}
115pub fn expr_from_text(text: &str) -> ast::Expr { 115fn expr_from_text(text: &str) -> ast::Expr {
116 ast_from_text(&format!("const C: () = {};", text)) 116 ast_from_text(&format!("const C: () = {};", text))
117} 117}
118 118
119pub fn try_expr_from_text(text: &str) -> Option<ast::Expr> {
120 try_ast_from_text(&format!("const C: () = {};", text))
121}
122
119pub fn bind_pat(name: ast::Name) -> ast::BindPat { 123pub fn bind_pat(name: ast::Name) -> ast::BindPat {
120 return from_text(name.text()); 124 return from_text(name.text());
121 125
@@ -239,6 +243,16 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
239 node 243 node
240} 244}
241 245
246fn try_ast_from_text<N: AstNode>(text: &str) -> Option<N> {
247 let parse = SourceFile::parse(text);
248 let node = parse.tree().syntax().descendants().find_map(N::cast)?;
249 let node = node.syntax().clone();
250 let node = unroot(node);
251 let node = N::cast(node).unwrap();
252 assert_eq!(node.syntax().text_range().start(), 0.into());
253 Some(node)
254}
255
242fn unroot(n: SyntaxNode) -> SyntaxNode { 256fn unroot(n: SyntaxNode) -> SyntaxNode {
243 SyntaxNode::new_root(n.green().clone()) 257 SyntaxNode::new_root(n.green().clone())
244} 258}
diff --git a/crates/ra_syntax/src/tests.rs b/crates/ra_syntax/src/tests.rs
index d331d541e..6a8cb6bb5 100644
--- a/crates/ra_syntax/src/tests.rs
+++ b/crates/ra_syntax/src/tests.rs
@@ -34,6 +34,7 @@ fn main() {
34 "##; 34 "##;
35 35
36 let parse = SourceFile::parse(code); 36 let parse = SourceFile::parse(code);
37 // eprintln!("{:#?}", parse.syntax_node());
37 assert!(parse.ok().is_ok()); 38 assert!(parse.ok().is_ok());
38} 39}
39 40
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0032_fn_pointer_type.txt b/crates/ra_syntax/test_data/parser/inline/ok/0032_fn_pointer_type.txt
index a0a8aea76..4c17f0db8 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0032_fn_pointer_type.txt
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0032_fn_pointer_type.txt
@@ -81,7 +81,8 @@ SOURCE_FILE@[0; 113)
81 WHITESPACE@[97; 98) " " 81 WHITESPACE@[97; 98) " "
82 COMMA@[98; 99) "," 82 COMMA@[98; 99) ","
83 WHITESPACE@[99; 100) " " 83 WHITESPACE@[99; 100) " "
84 DOTDOTDOT@[100; 103) "..." 84 PARAM@[100; 103)
85 DOTDOTDOT@[100; 103) "..."
85 WHITESPACE@[103; 104) " " 86 WHITESPACE@[103; 104) " "
86 R_PAREN@[104; 105) ")" 87 R_PAREN@[104; 105) ")"
87 WHITESPACE@[105; 106) " " 88 WHITESPACE@[105; 106) " "
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0116_trait_fn_placeholder_parameter.rs b/crates/ra_syntax/test_data/parser/inline/ok/0116_trait_fn_placeholder_parameter.rs
deleted file mode 100644
index 472cb8803..000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0116_trait_fn_placeholder_parameter.rs
+++ /dev/null
@@ -1,3 +0,0 @@
1trait Foo {
2 fn bar(_: u64, mut x: i32);
3}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0116_trait_fn_placeholder_parameter.txt b/crates/ra_syntax/test_data/parser/inline/ok/0116_trait_fn_placeholder_parameter.txt
deleted file mode 100644
index 158236c5a..000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0116_trait_fn_placeholder_parameter.txt
+++ /dev/null
@@ -1,47 +0,0 @@
1SOURCE_FILE@[0; 46)
2 TRAIT_DEF@[0; 45)
3 TRAIT_KW@[0; 5) "trait"
4 WHITESPACE@[5; 6) " "
5 NAME@[6; 9)
6 IDENT@[6; 9) "Foo"
7 WHITESPACE@[9; 10) " "
8 ITEM_LIST@[10; 45)
9 L_CURLY@[10; 11) "{"
10 WHITESPACE@[11; 16) "\n "
11 FN_DEF@[16; 43)
12 FN_KW@[16; 18) "fn"
13 WHITESPACE@[18; 19) " "
14 NAME@[19; 22)
15 IDENT@[19; 22) "bar"
16 PARAM_LIST@[22; 42)
17 L_PAREN@[22; 23) "("
18 PARAM@[23; 29)
19 PLACEHOLDER_PAT@[23; 24)
20 UNDERSCORE@[23; 24) "_"
21 COLON@[24; 25) ":"
22 WHITESPACE@[25; 26) " "
23 PATH_TYPE@[26; 29)
24 PATH@[26; 29)
25 PATH_SEGMENT@[26; 29)
26 NAME_REF@[26; 29)
27 IDENT@[26; 29) "u64"
28 COMMA@[29; 30) ","
29 WHITESPACE@[30; 31) " "
30 PARAM@[31; 41)
31 BIND_PAT@[31; 36)
32 MUT_KW@[31; 34) "mut"
33 WHITESPACE@[34; 35) " "
34 NAME@[35; 36)
35 IDENT@[35; 36) "x"
36 COLON@[36; 37) ":"
37 WHITESPACE@[37; 38) " "
38 PATH_TYPE@[38; 41)
39 PATH@[38; 41)
40 PATH_SEGMENT@[38; 41)
41 NAME_REF@[38; 41)
42 IDENT@[38; 41) "i32"
43 R_PAREN@[41; 42) ")"
44 SEMI@[42; 43) ";"
45 WHITESPACE@[43; 44) "\n"
46 R_CURLY@[44; 45) "}"
47 WHITESPACE@[45; 46) "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0123_param_list_vararg.txt b/crates/ra_syntax/test_data/parser/inline/ok/0123_param_list_vararg.txt
index 836e8e55b..6c3b17868 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0123_param_list_vararg.txt
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0123_param_list_vararg.txt
@@ -32,7 +32,8 @@ SOURCE_FILE@[0; 57)
32 IDENT@[38; 40) "i8" 32 IDENT@[38; 40) "i8"
33 COMMA@[40; 41) "," 33 COMMA@[40; 41) ","
34 WHITESPACE@[41; 42) " " 34 WHITESPACE@[41; 42) " "
35 DOTDOTDOT@[42; 45) "..." 35 PARAM@[42; 45)
36 DOTDOTDOT@[42; 45) "..."
36 R_PAREN@[45; 46) ")" 37 R_PAREN@[45; 46) ")"
37 WHITESPACE@[46; 47) " " 38 WHITESPACE@[46; 47) " "
38 RET_TYPE@[47; 53) 39 RET_TYPE@[47; 53)
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.rs b/crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.rs
new file mode 100644
index 000000000..7b277c16b
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.rs
@@ -0,0 +1 @@
fn foo((x, y): (i32, i32)) {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.txt b/crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.txt
new file mode 100644
index 000000000..103e254a6
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0156_fn_def_param.txt
@@ -0,0 +1,44 @@
1SOURCE_FILE@[0; 30)
2 FN_DEF@[0; 29)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 6)
6 IDENT@[3; 6) "foo"
7 PARAM_LIST@[6; 26)
8 L_PAREN@[6; 7) "("
9 PARAM@[7; 25)
10 TUPLE_PAT@[7; 13)
11 L_PAREN@[7; 8) "("
12 BIND_PAT@[8; 9)
13 NAME@[8; 9)
14 IDENT@[8; 9) "x"
15 COMMA@[9; 10) ","
16 WHITESPACE@[10; 11) " "
17 BIND_PAT@[11; 12)
18 NAME@[11; 12)
19 IDENT@[11; 12) "y"
20 R_PAREN@[12; 13) ")"
21 COLON@[13; 14) ":"
22 WHITESPACE@[14; 15) " "
23 TUPLE_TYPE@[15; 25)
24 L_PAREN@[15; 16) "("
25 PATH_TYPE@[16; 19)
26 PATH@[16; 19)
27 PATH_SEGMENT@[16; 19)
28 NAME_REF@[16; 19)
29 IDENT@[16; 19) "i32"
30 COMMA@[19; 20) ","
31 WHITESPACE@[20; 21) " "
32 PATH_TYPE@[21; 24)
33 PATH@[21; 24)
34 PATH_SEGMENT@[21; 24)
35 NAME_REF@[21; 24)
36 IDENT@[21; 24) "i32"
37 R_PAREN@[24; 25) ")"
38 R_PAREN@[25; 26) ")"
39 WHITESPACE@[26; 27) " "
40 BLOCK_EXPR@[27; 29)
41 BLOCK@[27; 29)
42 L_CURLY@[27; 28) "{"
43 R_CURLY@[28; 29) "}"
44 WHITESPACE@[29; 30) "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.txt b/crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.txt
index 719c99c17..254eafc36 100644
--- a/crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.txt
+++ b/crates/ra_syntax/test_data/parser/ok/0051_parameter_attrs.txt
@@ -118,7 +118,8 @@ SOURCE_FILE@[0; 519)
118 IDENT@[108; 112) "attr" 118 IDENT@[108; 112) "attr"
119 R_BRACK@[112; 113) "]" 119 R_BRACK@[112; 113) "]"
120 WHITESPACE@[113; 114) " " 120 WHITESPACE@[113; 114) " "
121 DOTDOTDOT@[114; 117) "..." 121 PARAM@[114; 117)
122 DOTDOTDOT@[114; 117) "..."
122 R_PAREN@[117; 118) ")" 123 R_PAREN@[117; 118) ")"
123 WHITESPACE@[118; 119) " " 124 WHITESPACE@[118; 119) " "
124 RET_TYPE@[119; 125) 125 RET_TYPE@[119; 125)
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0153_trait_fn_patterns.rs b/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rs
index a94bf378a..3b666af8e 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0153_trait_fn_patterns.rs
+++ b/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rs
@@ -3,4 +3,5 @@ trait T {
3 fn f2(S { a, b }: S) {} 3 fn f2(S { a, b }: S) {}
4 fn f3(NewType(a): NewType) {} 4 fn f3(NewType(a): NewType) {}
5 fn f4(&&a: &&usize) {} 5 fn f4(&&a: &&usize) {}
6 fn bar(_: u64, mut x: i32);
6} 7}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0153_trait_fn_patterns.txt b/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.txt
index b22df8dbe..eb2e3a503 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0153_trait_fn_patterns.txt
+++ b/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.txt
@@ -1,11 +1,11 @@
1SOURCE_FILE@[0; 138) 1SOURCE_FILE@[0; 170)
2 TRAIT_DEF@[0; 137) 2 TRAIT_DEF@[0; 169)
3 TRAIT_KW@[0; 5) "trait" 3 TRAIT_KW@[0; 5) "trait"
4 WHITESPACE@[5; 6) " " 4 WHITESPACE@[5; 6) " "
5 NAME@[6; 7) 5 NAME@[6; 7)
6 IDENT@[6; 7) "T" 6 IDENT@[6; 7) "T"
7 WHITESPACE@[7; 8) " " 7 WHITESPACE@[7; 8) " "
8 ITEM_LIST@[8; 137) 8 ITEM_LIST@[8; 169)
9 L_CURLY@[8; 9) "{" 9 L_CURLY@[8; 9) "{"
10 WHITESPACE@[9; 14) "\n " 10 WHITESPACE@[9; 14) "\n "
11 FN_DEF@[14; 46) 11 FN_DEF@[14; 46)
@@ -156,6 +156,41 @@ SOURCE_FILE@[0; 138)
156 BLOCK@[133; 135) 156 BLOCK@[133; 135)
157 L_CURLY@[133; 134) "{" 157 L_CURLY@[133; 134) "{"
158 R_CURLY@[134; 135) "}" 158 R_CURLY@[134; 135) "}"
159 WHITESPACE@[135; 136) "\n" 159 WHITESPACE@[135; 140) "\n "
160 R_CURLY@[136; 137) "}" 160 FN_DEF@[140; 167)
161 WHITESPACE@[137; 138) "\n" 161 FN_KW@[140; 142) "fn"
162 WHITESPACE@[142; 143) " "
163 NAME@[143; 146)
164 IDENT@[143; 146) "bar"
165 PARAM_LIST@[146; 166)
166 L_PAREN@[146; 147) "("
167 PARAM@[147; 153)
168 PLACEHOLDER_PAT@[147; 148)
169 UNDERSCORE@[147; 148) "_"
170 COLON@[148; 149) ":"
171 WHITESPACE@[149; 150) " "
172 PATH_TYPE@[150; 153)
173 PATH@[150; 153)
174 PATH_SEGMENT@[150; 153)
175 NAME_REF@[150; 153)
176 IDENT@[150; 153) "u64"
177 COMMA@[153; 154) ","
178 WHITESPACE@[154; 155) " "
179 PARAM@[155; 165)
180 BIND_PAT@[155; 160)
181 MUT_KW@[155; 158) "mut"
182 WHITESPACE@[158; 159) " "
183 NAME@[159; 160)
184 IDENT@[159; 160) "x"
185 COLON@[160; 161) ":"
186 WHITESPACE@[161; 162) " "
187 PATH_TYPE@[162; 165)
188 PATH@[162; 165)
189 PATH_SEGMENT@[162; 165)
190 NAME_REF@[162; 165)
191 IDENT@[162; 165) "i32"
192 R_PAREN@[165; 166) ")"
193 SEMI@[166; 167) ";"
194 WHITESPACE@[167; 168) "\n"
195 R_CURLY@[168; 169) "}"
196 WHITESPACE@[169; 170) "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0063_variadic_fun.rs b/crates/ra_syntax/test_data/parser/ok/0063_variadic_fun.rs
new file mode 100644
index 000000000..a16afbaf3
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0063_variadic_fun.rs
@@ -0,0 +1,5 @@
1extern "C" {
2 fn a(_: *mut u8, ...,);
3 fn b(_: *mut u8, _: ...);
4 fn c(_: *mut u8, #[cfg(never)] [w, t, f]: ...,);
5}
diff --git a/crates/ra_syntax/test_data/parser/ok/0063_variadic_fun.txt b/crates/ra_syntax/test_data/parser/ok/0063_variadic_fun.txt
new file mode 100644
index 000000000..186f03626
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0063_variadic_fun.txt
@@ -0,0 +1,133 @@
1SOURCE_FILE@[0; 126)
2 EXTERN_BLOCK@[0; 125)
3 ABI@[0; 10)
4 EXTERN_KW@[0; 6) "extern"
5 WHITESPACE@[6; 7) " "
6 STRING@[7; 10) "\"C\""
7 WHITESPACE@[10; 11) " "
8 EXTERN_ITEM_LIST@[11; 125)
9 L_CURLY@[11; 12) "{"
10 WHITESPACE@[12; 17) "\n "
11 FN_DEF@[17; 40)
12 FN_KW@[17; 19) "fn"
13 WHITESPACE@[19; 20) " "
14 NAME@[20; 21)
15 IDENT@[20; 21) "a"
16 PARAM_LIST@[21; 39)
17 L_PAREN@[21; 22) "("
18 PARAM@[22; 32)
19 PLACEHOLDER_PAT@[22; 23)
20 UNDERSCORE@[22; 23) "_"
21 COLON@[23; 24) ":"
22 WHITESPACE@[24; 25) " "
23 POINTER_TYPE@[25; 32)
24 STAR@[25; 26) "*"
25 MUT_KW@[26; 29) "mut"
26 WHITESPACE@[29; 30) " "
27 PATH_TYPE@[30; 32)
28 PATH@[30; 32)
29 PATH_SEGMENT@[30; 32)
30 NAME_REF@[30; 32)
31 IDENT@[30; 32) "u8"
32 COMMA@[32; 33) ","
33 WHITESPACE@[33; 34) " "
34 PARAM@[34; 37)
35 DOTDOTDOT@[34; 37) "..."
36 COMMA@[37; 38) ","
37 R_PAREN@[38; 39) ")"
38 SEMI@[39; 40) ";"
39 WHITESPACE@[40; 45) "\n "
40 FN_DEF@[45; 70)
41 FN_KW@[45; 47) "fn"
42 WHITESPACE@[47; 48) " "
43 NAME@[48; 49)
44 IDENT@[48; 49) "b"
45 PARAM_LIST@[49; 69)
46 L_PAREN@[49; 50) "("
47 PARAM@[50; 60)
48 PLACEHOLDER_PAT@[50; 51)
49 UNDERSCORE@[50; 51) "_"
50 COLON@[51; 52) ":"
51 WHITESPACE@[52; 53) " "
52 POINTER_TYPE@[53; 60)
53 STAR@[53; 54) "*"
54 MUT_KW@[54; 57) "mut"
55 WHITESPACE@[57; 58) " "
56 PATH_TYPE@[58; 60)
57 PATH@[58; 60)
58 PATH_SEGMENT@[58; 60)
59 NAME_REF@[58; 60)
60 IDENT@[58; 60) "u8"
61 COMMA@[60; 61) ","
62 WHITESPACE@[61; 62) " "
63 PARAM@[62; 68)
64 PLACEHOLDER_PAT@[62; 63)
65 UNDERSCORE@[62; 63) "_"
66 COLON@[63; 64) ":"
67 WHITESPACE@[64; 65) " "
68 DOTDOTDOT@[65; 68) "..."
69 R_PAREN@[68; 69) ")"
70 SEMI@[69; 70) ";"
71 WHITESPACE@[70; 75) "\n "
72 FN_DEF@[75; 123)
73 FN_KW@[75; 77) "fn"
74 WHITESPACE@[77; 78) " "
75 NAME@[78; 79)
76 IDENT@[78; 79) "c"
77 PARAM_LIST@[79; 122)
78 L_PAREN@[79; 80) "("
79 PARAM@[80; 90)
80 PLACEHOLDER_PAT@[80; 81)
81 UNDERSCORE@[80; 81) "_"
82 COLON@[81; 82) ":"
83 WHITESPACE@[82; 83) " "
84 POINTER_TYPE@[83; 90)
85 STAR@[83; 84) "*"
86 MUT_KW@[84; 87) "mut"
87 WHITESPACE@[87; 88) " "
88 PATH_TYPE@[88; 90)
89 PATH@[88; 90)
90 PATH_SEGMENT@[88; 90)
91 NAME_REF@[88; 90)
92 IDENT@[88; 90) "u8"
93 COMMA@[90; 91) ","
94 WHITESPACE@[91; 92) " "
95 ATTR@[92; 105)
96 POUND@[92; 93) "#"
97 L_BRACK@[93; 94) "["
98 PATH@[94; 97)
99 PATH_SEGMENT@[94; 97)
100 NAME_REF@[94; 97)
101 IDENT@[94; 97) "cfg"
102 TOKEN_TREE@[97; 104)
103 L_PAREN@[97; 98) "("
104 IDENT@[98; 103) "never"
105 R_PAREN@[103; 104) ")"
106 R_BRACK@[104; 105) "]"
107 WHITESPACE@[105; 106) " "
108 PARAM@[106; 120)
109 SLICE_PAT@[106; 115)
110 L_BRACK@[106; 107) "["
111 BIND_PAT@[107; 108)
112 NAME@[107; 108)
113 IDENT@[107; 108) "w"
114 COMMA@[108; 109) ","
115 WHITESPACE@[109; 110) " "
116 BIND_PAT@[110; 111)
117 NAME@[110; 111)
118 IDENT@[110; 111) "t"
119 COMMA@[111; 112) ","
120 WHITESPACE@[112; 113) " "
121 BIND_PAT@[113; 114)
122 NAME@[113; 114)
123 IDENT@[113; 114) "f"
124 R_BRACK@[114; 115) "]"
125 COLON@[115; 116) ":"
126 WHITESPACE@[116; 117) " "
127 DOTDOTDOT@[117; 120) "..."
128 COMMA@[120; 121) ","
129 R_PAREN@[121; 122) ")"
130 SEMI@[122; 123) ";"
131 WHITESPACE@[123; 124) "\n"
132 R_CURLY@[124; 125) "}"
133 WHITESPACE@[125; 126) "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0152_fn_patterns.rs b/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rs
index b49e872d7..b49e872d7 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0152_fn_patterns.rs
+++ b/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rs
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0152_fn_patterns.txt b/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.txt
index b30030de3..b30030de3 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0152_fn_patterns.txt
+++ b/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.txt
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 53751aafb..321861b16 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -19,50 +19,48 @@ impl CargoTargetSpec {
19 pub(crate) fn runnable_args( 19 pub(crate) fn runnable_args(
20 spec: Option<CargoTargetSpec>, 20 spec: Option<CargoTargetSpec>,
21 kind: &RunnableKind, 21 kind: &RunnableKind,
22 ) -> Result<Vec<String>> { 22 ) -> Result<(Vec<String>, Vec<String>)> {
23 let mut res = Vec::new(); 23 let mut args = Vec::new();
24 let mut extra_args = Vec::new();
24 match kind { 25 match kind {
25 RunnableKind::Test { test_id } => { 26 RunnableKind::Test { test_id } => {
26 res.push("test".to_string()); 27 args.push("test".to_string());
27 if let Some(spec) = spec { 28 if let Some(spec) = spec {
28 spec.push_to(&mut res); 29 spec.push_to(&mut args);
29 } 30 }
30 res.push("--".to_string()); 31 extra_args.push(test_id.to_string());
31 res.push(test_id.to_string());
32 if let TestId::Path(_) = test_id { 32 if let TestId::Path(_) = test_id {
33 res.push("--exact".to_string()); 33 extra_args.push("--exact".to_string());
34 } 34 }
35 res.push("--nocapture".to_string()); 35 extra_args.push("--nocapture".to_string());
36 } 36 }
37 RunnableKind::TestMod { path } => { 37 RunnableKind::TestMod { path } => {
38 res.push("test".to_string()); 38 args.push("test".to_string());
39 if let Some(spec) = spec { 39 if let Some(spec) = spec {
40 spec.push_to(&mut res); 40 spec.push_to(&mut args);
41 } 41 }
42 res.push("--".to_string()); 42 extra_args.push(path.to_string());
43 res.push(path.to_string()); 43 extra_args.push("--nocapture".to_string());
44 res.push("--nocapture".to_string());
45 } 44 }
46 RunnableKind::Bench { test_id } => { 45 RunnableKind::Bench { test_id } => {
47 res.push("bench".to_string()); 46 args.push("bench".to_string());
48 if let Some(spec) = spec { 47 if let Some(spec) = spec {
49 spec.push_to(&mut res); 48 spec.push_to(&mut args);
50 } 49 }
51 res.push("--".to_string()); 50 extra_args.push(test_id.to_string());
52 res.push(test_id.to_string());
53 if let TestId::Path(_) = test_id { 51 if let TestId::Path(_) = test_id {
54 res.push("--exact".to_string()); 52 extra_args.push("--exact".to_string());
55 } 53 }
56 res.push("--nocapture".to_string()); 54 extra_args.push("--nocapture".to_string());
57 } 55 }
58 RunnableKind::Bin => { 56 RunnableKind::Bin => {
59 res.push("run".to_string()); 57 args.push("run".to_string());
60 if let Some(spec) = spec { 58 if let Some(spec) = spec {
61 spec.push_to(&mut res); 59 spec.push_to(&mut args);
62 } 60 }
63 } 61 }
64 } 62 }
65 Ok(res) 63 Ok((args, extra_args))
66 } 64 }
67 65
68 pub(crate) fn for_file( 66 pub(crate) fn for_file(
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 084e17b04..6b9a11a87 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -55,6 +55,9 @@ pub struct ServerConfig {
55 55
56 /// Cargo feature configurations. 56 /// Cargo feature configurations.
57 pub cargo_features: CargoFeatures, 57 pub cargo_features: CargoFeatures,
58
59 /// Enabled if the vscode_lldb extension is available.
60 pub vscode_lldb: bool,
58} 61}
59 62
60impl Default for ServerConfig { 63impl Default for ServerConfig {
@@ -76,6 +79,7 @@ impl Default for ServerConfig {
76 additional_out_dirs: FxHashMap::default(), 79 additional_out_dirs: FxHashMap::default(),
77 cargo_features: Default::default(), 80 cargo_features: Default::default(),
78 rustfmt_args: Vec::new(), 81 rustfmt_args: Vec::new(),
82 vscode_lldb: false,
79 } 83 }
80 } 84 }
81} 85}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 2b3b16d35..1fefc66aa 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -16,7 +16,10 @@ use std::{
16 16
17use crossbeam_channel::{select, unbounded, RecvError, Sender}; 17use crossbeam_channel::{select, unbounded, RecvError, Sender};
18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
19use lsp_types::{ClientCapabilities, NumberOrString}; 19use lsp_types::{
20 ClientCapabilities, NumberOrString, WorkDoneProgress, WorkDoneProgressBegin,
21 WorkDoneProgressCreateParams, WorkDoneProgressEnd, WorkDoneProgressReport,
22};
20use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask}; 23use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask};
21use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId}; 24use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId};
22use ra_prof::profile; 25use ra_prof::profile;
@@ -189,6 +192,7 @@ pub fn main_loop(
189 all_targets: config.cargo_watch_all_targets, 192 all_targets: config.cargo_watch_all_targets,
190 }, 193 },
191 rustfmt_args: config.rustfmt_args, 194 rustfmt_args: config.rustfmt_args,
195 vscode_lldb: config.vscode_lldb,
192 } 196 }
193 }; 197 };
194 198
@@ -329,6 +333,7 @@ struct LoopState {
329 in_flight_libraries: usize, 333 in_flight_libraries: usize,
330 pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>, 334 pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>,
331 workspace_loaded: bool, 335 workspace_loaded: bool,
336 roots_scanned_progress: Option<usize>,
332} 337}
333 338
334impl LoopState { 339impl LoopState {
@@ -428,17 +433,15 @@ fn loop_turn(
428 && loop_state.in_flight_libraries == 0 433 && loop_state.in_flight_libraries == 0
429 { 434 {
430 loop_state.workspace_loaded = true; 435 loop_state.workspace_loaded = true;
431 let n_packages: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum();
432 if world_state.feature_flags.get("notifications.workspace-loaded") {
433 let msg = format!("workspace loaded, {} rust packages", n_packages);
434 show_message(req::MessageType::Info, msg, &connection.sender);
435 }
436 world_state.check_watcher.update(); 436 world_state.check_watcher.update();
437 pool.execute({ 437 pool.execute({
438 let subs = loop_state.subscriptions.subscriptions(); 438 let subs = loop_state.subscriptions.subscriptions();
439 let snap = world_state.snapshot(); 439 let snap = world_state.snapshot();
440 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) 440 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ())
441 }); 441 });
442 send_startup_progress(&connection.sender, loop_state, world_state);
443 } else if !loop_state.workspace_loaded {
444 send_startup_progress(&connection.sender, loop_state, world_state);
442 } 445 }
443 446
444 if state_changed { 447 if state_changed {
@@ -703,6 +706,65 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state:
703 } 706 }
704} 707}
705 708
709fn send_startup_progress(
710 sender: &Sender<Message>,
711 loop_state: &mut LoopState,
712 world_state: &WorldState,
713) {
714 if !world_state.feature_flags.get("notifications.workspace-loaded") {
715 return;
716 }
717
718 let total: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum();
719 let prev_progress = loop_state.roots_scanned_progress;
720 let progress = total - world_state.roots_to_scan;
721 loop_state.roots_scanned_progress = Some(progress);
722
723 match (prev_progress, loop_state.workspace_loaded) {
724 (None, false) => {
725 let work_done_progress_create = request_new::<req::WorkDoneProgressCreate>(
726 loop_state.next_request_id(),
727 WorkDoneProgressCreateParams {
728 token: req::ProgressToken::String("rustAnalyzer/startup".into()),
729 },
730 );
731 sender.send(work_done_progress_create.into()).unwrap();
732 send_startup_progress_notif(
733 sender,
734 WorkDoneProgress::Begin(WorkDoneProgressBegin {
735 title: "rust-analyzer".into(),
736 cancellable: None,
737 message: Some(format!("{}/{} packages", progress, total)),
738 percentage: Some(100.0 * progress as f64 / total as f64),
739 }),
740 );
741 }
742 (Some(prev), false) if progress != prev => send_startup_progress_notif(
743 sender,
744 WorkDoneProgress::Report(WorkDoneProgressReport {
745 cancellable: None,
746 message: Some(format!("{}/{} packages", progress, total)),
747 percentage: Some(100.0 * progress as f64 / total as f64),
748 }),
749 ),
750 (_, true) => send_startup_progress_notif(
751 sender,
752 WorkDoneProgress::End(WorkDoneProgressEnd {
753 message: Some(format!("rust-analyzer loaded, {} packages", progress)),
754 }),
755 ),
756 _ => {}
757 }
758}
759
760fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) {
761 let notif = notification_new::<req::Progress>(req::ProgressParams {
762 token: req::ProgressToken::String("rustAnalyzer/startup".into()),
763 value: req::ProgressParamsValue::WorkDone(work_done_progress),
764 });
765 sender.send(notif.into()).unwrap();
766}
767
706struct PoolDispatcher<'a> { 768struct PoolDispatcher<'a> {
707 req: Option<Request>, 769 req: Option<Request>,
708 pool: &'a ThreadPool, 770 pool: &'a ThreadPool,
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 6482f3b77..1cc2f6571 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -381,6 +381,7 @@ pub fn handle_runnables(
381 label, 381 label,
382 bin: "cargo".to_string(), 382 bin: "cargo".to_string(),
383 args: check_args, 383 args: check_args,
384 extra_args: Vec::new(),
384 env: FxHashMap::default(), 385 env: FxHashMap::default(),
385 cwd: workspace_root.map(|root| root.to_string_lossy().to_string()), 386 cwd: workspace_root.map(|root| root.to_string_lossy().to_string()),
386 }); 387 });
@@ -794,18 +795,35 @@ pub fn handle_code_lens(
794 RunnableKind::Bin => "Run", 795 RunnableKind::Bin => "Run",
795 } 796 }
796 .to_string(); 797 .to_string();
797 let r = to_lsp_runnable(&world, file_id, runnable)?; 798 let mut r = to_lsp_runnable(&world, file_id, runnable)?;
798 let lens = CodeLens { 799 let lens = CodeLens {
799 range: r.range, 800 range: r.range,
800 command: Some(Command { 801 command: Some(Command {
801 title, 802 title,
802 command: "rust-analyzer.runSingle".into(), 803 command: "rust-analyzer.runSingle".into(),
803 arguments: Some(vec![to_value(r).unwrap()]), 804 arguments: Some(vec![to_value(&r).unwrap()]),
804 }), 805 }),
805 data: None, 806 data: None,
806 }; 807 };
807
808 lenses.push(lens); 808 lenses.push(lens);
809
810 if world.options.vscode_lldb {
811 if r.args[0] == "run" {
812 r.args[0] = "build".into();
813 } else {
814 r.args.push("--no-run".into());
815 }
816 let debug_lens = CodeLens {
817 range: r.range,
818 command: Some(Command {
819 title: "Debug".into(),
820 command: "rust-analyzer.debugSingle".into(),
821 arguments: Some(vec![to_value(r).unwrap()]),
822 }),
823 data: None,
824 };
825 lenses.push(debug_lens);
826 }
809 } 827 }
810 828
811 // Handle impls 829 // Handle impls
@@ -914,7 +932,10 @@ pub fn handle_document_highlight(
914 932
915pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> { 933pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> {
916 let _p = profile("handle_ssr"); 934 let _p = profile("handle_ssr");
917 world.analysis().structural_search_replace(&params.arg)??.try_conv_with(&world) 935 world
936 .analysis()
937 .structural_search_replace(&params.query, params.parse_only)??
938 .try_conv_with(&world)
918} 939}
919 940
920pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 941pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> {
@@ -952,7 +973,7 @@ fn to_lsp_runnable(
952 runnable: Runnable, 973 runnable: Runnable,
953) -> Result<req::Runnable> { 974) -> Result<req::Runnable> {
954 let spec = CargoTargetSpec::for_file(world, file_id)?; 975 let spec = CargoTargetSpec::for_file(world, file_id)?;
955 let args = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; 976 let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?;
956 let line_index = world.analysis().file_line_index(file_id)?; 977 let line_index = world.analysis().file_line_index(file_id)?;
957 let label = match &runnable.kind { 978 let label = match &runnable.kind {
958 RunnableKind::Test { test_id } => format!("test {}", test_id), 979 RunnableKind::Test { test_id } => format!("test {}", test_id),
@@ -965,6 +986,7 @@ fn to_lsp_runnable(
965 label, 986 label,
966 bin: "cargo".to_string(), 987 bin: "cargo".to_string(),
967 args, 988 args,
989 extra_args,
968 env: { 990 env: {
969 let mut m = FxHashMap::default(); 991 let mut m = FxHashMap::default();
970 m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); 992 m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
@@ -973,6 +995,7 @@ fn to_lsp_runnable(
973 cwd: world.workspace_root_for(file_id).map(|root| root.to_string_lossy().to_string()), 995 cwd: world.workspace_root_for(file_id).map(|root| root.to_string_lossy().to_string()),
974 }) 996 })
975} 997}
998
976fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>> { 999fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>> {
977 let line_index = world.analysis().file_line_index(file_id)?; 1000 let line_index = world.analysis().file_line_index(file_id)?;
978 let res = world 1001 let res = world
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs
index a3efe3b9f..9e27d3f1c 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -169,6 +169,7 @@ pub struct Runnable {
169 pub label: String, 169 pub label: String,
170 pub bin: String, 170 pub bin: String,
171 pub args: Vec<String>, 171 pub args: Vec<String>,
172 pub extra_args: Vec<String>,
172 pub env: FxHashMap<String, String>, 173 pub env: FxHashMap<String, String>,
173 pub cwd: Option<String>, 174 pub cwd: Option<String>,
174} 175}
@@ -217,6 +218,8 @@ impl Request for Ssr {
217} 218}
218 219
219#[derive(Debug, Deserialize, Serialize)] 220#[derive(Debug, Deserialize, Serialize)]
221#[serde(rename_all = "camelCase")]
220pub struct SsrParams { 222pub struct SsrParams {
221 pub arg: String, 223 pub query: String,
224 pub parse_only: bool,
222} 225}
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 058ce2af8..5743471bf 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -38,6 +38,7 @@ pub struct Options {
38 pub inlay_hints: InlayHintsOptions, 38 pub inlay_hints: InlayHintsOptions,
39 pub rustfmt_args: Vec<String>, 39 pub rustfmt_args: Vec<String>,
40 pub cargo_watch: CheckOptions, 40 pub cargo_watch: CheckOptions,
41 pub vscode_lldb: bool,
41} 42}
42 43
43/// `WorldState` is the primary mutable state of the language server 44/// `WorldState` is the primary mutable state of the language server
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 970185dec..145429571 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -75,7 +75,8 @@ fn foo() {
75 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None }, 75 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
76 json!([ 76 json!([
77 { 77 {
78 "args": [ "test", "--", "foo", "--nocapture" ], 78 "args": [ "test" ],
79 "extraArgs": [ "foo", "--nocapture" ],
79 "bin": "cargo", 80 "bin": "cargo",
80 "env": { "RUST_BACKTRACE": "short" }, 81 "env": { "RUST_BACKTRACE": "short" },
81 "cwd": null, 82 "cwd": null,
@@ -90,6 +91,7 @@ fn foo() {
90 "check", 91 "check",
91 "--all" 92 "--all"
92 ], 93 ],
94 "extraArgs": [],
93 "bin": "cargo", 95 "bin": "cargo",
94 "env": {}, 96 "env": {},
95 "cwd": null, 97 "cwd": null,
@@ -141,13 +143,11 @@ fn main() {}
141 143
142 server.wait_until_workspace_is_loaded(); 144 server.wait_until_workspace_is_loaded();
143 server.request::<Runnables>( 145 server.request::<Runnables>(
144 RunnablesParams { 146 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
145 text_document: server.doc_id("foo/tests/spam.rs"),
146 position: None,
147 },
148 json!([ 147 json!([
149 { 148 {
150 "args": [ "test", "--package", "foo", "--test", "spam", "--", "test_eggs", "--exact", "--nocapture" ], 149 "args": [ "test", "--package", "foo", "--test", "spam" ],
150 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
151 "bin": "cargo", 151 "bin": "cargo",
152 "env": { "RUST_BACKTRACE": "short" }, 152 "env": { "RUST_BACKTRACE": "short" },
153 "label": "test test_eggs", 153 "label": "test test_eggs",
@@ -165,6 +165,7 @@ fn main() {}
165 "--test", 165 "--test",
166 "spam" 166 "spam"
167 ], 167 ],
168 "extraArgs": [],
168 "bin": "cargo", 169 "bin": "cargo",
169 "env": {}, 170 "env": {},
170 "cwd": server.path().join("foo"), 171 "cwd": server.path().join("foo"),
@@ -180,7 +181,7 @@ fn main() {}
180 } 181 }
181 } 182 }
182 } 183 }
183 ]) 184 ]),
184 ); 185 );
185} 186}
186 187
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index e28ae61fe..1d7062bdf 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -12,13 +12,14 @@ use lsp_types::{
12 notification::{DidOpenTextDocument, Exit}, 12 notification::{DidOpenTextDocument, Exit},
13 request::Shutdown, 13 request::Shutdown,
14 ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, TextDocumentClientCapabilities, 14 ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, TextDocumentClientCapabilities,
15 TextDocumentIdentifier, TextDocumentItem, Url, 15 TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress,
16}; 16};
17use serde::Serialize; 17use serde::Serialize;
18use serde_json::{to_string_pretty, Value}; 18use serde_json::{to_string_pretty, Value};
19use tempfile::TempDir; 19use tempfile::TempDir;
20use test_utils::{find_mismatch, parse_fixture}; 20use test_utils::{find_mismatch, parse_fixture};
21 21
22use req::{ProgressParams, ProgressParamsValue};
22use rust_analyzer::{main_loop, req, ServerConfig}; 23use rust_analyzer::{main_loop, req, ServerConfig};
23 24
24pub struct Project<'a> { 25pub struct Project<'a> {
@@ -201,10 +202,14 @@ impl Server {
201 } 202 }
202 pub fn wait_until_workspace_is_loaded(&self) { 203 pub fn wait_until_workspace_is_loaded(&self) {
203 self.wait_for_message_cond(1, &|msg: &Message| match msg { 204 self.wait_for_message_cond(1, &|msg: &Message| match msg {
204 Message::Notification(n) if n.method == "window/showMessage" => { 205 Message::Notification(n) if n.method == "$/progress" => {
205 let msg = 206 match n.clone().extract::<ProgressParams>("$/progress").unwrap() {
206 n.clone().extract::<req::ShowMessageParams>("window/showMessage").unwrap(); 207 ProgressParams {
207 msg.message.starts_with("workspace loaded") 208 token: req::ProgressToken::String(ref token),
209 value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)),
210 } if token == "rustAnalyzer/startup" => true,
211 _ => false,
212 }
208 } 213 }
209 _ => false, 214 _ => false,
210 }) 215 })
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index a0d8f4d37..db03df1c4 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -397,6 +397,8 @@ pub fn skip_slow_tests() -> bool {
397 should_skip 397 should_skip
398} 398}
399 399
400const REWRITE: bool = false;
401
400/// Asserts that `expected` and `actual` strings are equal. If they differ only 402/// Asserts that `expected` and `actual` strings are equal. If they differ only
401/// in trailing or leading whitespace the test won't fail and 403/// in trailing or leading whitespace the test won't fail and
402/// the contents of `actual` will be written to the file located at `path`. 404/// the contents of `actual` will be written to the file located at `path`.
@@ -412,7 +414,6 @@ fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
412 fs::write(path, actual).unwrap(); 414 fs::write(path, actual).unwrap();
413 return; 415 return;
414 } 416 }
415 const REWRITE: bool = false;
416 if REWRITE { 417 if REWRITE {
417 println!("rewriting {}", pretty_path.display()); 418 println!("rewriting {}", pretty_path.display());
418 fs::write(path, actual).unwrap(); 419 fs::write(path, actual).unwrap();