aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
authorSeivan Heidari <[email protected]>2019-12-23 14:35:31 +0000
committerSeivan Heidari <[email protected]>2019-12-23 14:35:31 +0000
commitb21d9337d9200e2cfdc90b386591c72c302dc03e (patch)
treef81f5c08f821115cee26fa4d3ceaae88c7807fd5 /crates/ra_ide
parent18a0937585b836ec5ed054b9ae48e0156ab6d9ef (diff)
parentce07a2daa9e53aa86a769f8641b14c2878444fbc (diff)
Merge branch 'master' into feature/themes
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/Cargo.toml1
-rw-r--r--crates/ra_ide/src/call_info.rs43
-rw-r--r--crates/ra_ide/src/change.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs35
-rw-r--r--crates/ra_ide/src/completion/complete_path.rs17
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_scope.rs37
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs32
-rw-r--r--crates/ra_ide/src/db.rs14
-rw-r--r--crates/ra_ide/src/diagnostics.rs2
-rw-r--r--crates/ra_ide/src/display.rs2
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs84
-rw-r--r--crates/ra_ide/src/expand.rs89
-rw-r--r--crates/ra_ide/src/expand_macro.rs21
-rw-r--r--crates/ra_ide/src/extend_selection.rs80
-rw-r--r--crates/ra_ide/src/goto_definition.rs393
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs43
-rw-r--r--crates/ra_ide/src/hover.rs126
-rw-r--r--crates/ra_ide/src/impls.rs10
-rw-r--r--crates/ra_ide/src/inlay_hints.rs68
-rw-r--r--crates/ra_ide/src/lib.rs5
-rw-r--r--crates/ra_ide/src/marks.rs9
-rw-r--r--crates/ra_ide/src/parent_module.rs11
-rw-r--r--crates/ra_ide/src/references.rs10
-rw-r--r--crates/ra_ide/src/references/classify.rs29
-rw-r--r--crates/ra_ide/src/references/name_definition.rs6
-rw-r--r--crates/ra_ide/src/references/rename.rs54
-rw-r--r--crates/ra_ide/src/references/search_scope.rs16
-rw-r--r--crates/ra_ide/src/runnables.rs6
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html31
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html6
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs129
32 files changed, 1004 insertions, 409 deletions
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index e6383dd35..e3439ae31 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -11,6 +11,7 @@ doctest = false
11wasm = [] 11wasm = []
12 12
13[dependencies] 13[dependencies]
14either = "1.5"
14format-buf = "1.0.0" 15format-buf = "1.0.0"
15itertools = "0.8.0" 16itertools = "0.8.0"
16join_to_string = "0.1.3" 17join_to_string = "0.1.3"
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index d559dc4d0..2c2b6fa48 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -1,24 +1,26 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_db::SourceDatabase; 3use hir::db::AstDatabase;
4use ra_syntax::{ 4use ra_syntax::{
5 algo::ancestors_at_offset,
6 ast::{self, ArgListOwner}, 5 ast::{self, ArgListOwner},
7 match_ast, AstNode, SyntaxNode, TextUnit, 6 match_ast, AstNode, SyntaxNode,
8}; 7};
9use test_utils::tested_by; 8use test_utils::tested_by;
10 9
11use crate::{db::RootDatabase, CallInfo, FilePosition, FunctionSignature}; 10use crate::{
11 db::RootDatabase, expand::descend_into_macros, CallInfo, FilePosition, FunctionSignature,
12};
12 13
13/// Computes parameter information for the given call expression. 14/// Computes parameter information for the given call expression.
14pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { 15pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
15 let parse = db.parse(position.file_id); 16 let file = db.parse_or_expand(position.file_id.into())?;
16 let syntax = parse.tree().syntax().clone(); 17 let token = file.token_at_offset(position.offset).next()?;
18 let token = descend_into_macros(db, position.file_id, token);
17 19
18 // Find the calling expression and it's NameRef 20 // Find the calling expression and it's NameRef
19 let calling_node = FnCallNode::with_node(&syntax, position.offset)?; 21 let calling_node = FnCallNode::with_node(&token.value.parent())?;
20 let name_ref = calling_node.name_ref()?; 22 let name_ref = calling_node.name_ref()?;
21 let name_ref = hir::Source::new(position.file_id.into(), name_ref.syntax()); 23 let name_ref = token.with_value(name_ref.syntax());
22 24
23 let analyzer = hir::SourceAnalyzer::new(db, name_ref, None); 25 let analyzer = hir::SourceAnalyzer::new(db, name_ref, None);
24 let (mut call_info, has_self) = match &calling_node { 26 let (mut call_info, has_self) = match &calling_node {
@@ -93,8 +95,8 @@ enum FnCallNode {
93} 95}
94 96
95impl FnCallNode { 97impl FnCallNode {
96 fn with_node(syntax: &SyntaxNode, offset: TextUnit) -> Option<FnCallNode> { 98 fn with_node(syntax: &SyntaxNode) -> Option<FnCallNode> {
97 ancestors_at_offset(syntax, offset).find_map(|node| { 99 syntax.ancestors().find_map(|node| {
98 match_ast! { 100 match_ast! {
99 match node { 101 match node {
100 ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) }, 102 ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) },
@@ -589,4 +591,25 @@ fn f() {
589 assert_eq!(info.label(), "foo!()"); 591 assert_eq!(info.label(), "foo!()");
590 assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string())); 592 assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string()));
591 } 593 }
594
595 #[test]
596 fn fn_signature_for_call_in_macro() {
597 let info = call_info(
598 r#"
599 macro_rules! id {
600 ($($tt:tt)*) => { $($tt)* }
601 }
602 fn foo() {
603
604 }
605 id! {
606 fn bar() {
607 foo(<|>);
608 }
609 }
610 "#,
611 );
612
613 assert_eq!(info.label(), "fn foo()");
614 }
592} 615}
diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs
index 4a76d1dd8..387a9cafb 100644
--- a/crates/ra_ide/src/change.rs
+++ b/crates/ra_ide/src/change.rs
@@ -270,7 +270,6 @@ impl RootDatabase {
270 270
271 self.query(hir::db::AstIdMapQuery).sweep(sweep); 271 self.query(hir::db::AstIdMapQuery).sweep(sweep);
272 272
273 self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep);
274 self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); 273 self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep);
275 274
276 self.query(hir::db::ExprScopesQuery).sweep(sweep); 275 self.query(hir::db::ExprScopesQuery).sweep(sweep);
@@ -309,7 +308,6 @@ impl RootDatabase {
309 hir::db::StructDataQuery 308 hir::db::StructDataQuery
310 hir::db::EnumDataQuery 309 hir::db::EnumDataQuery
311 hir::db::TraitDataQuery 310 hir::db::TraitDataQuery
312 hir::db::RawItemsWithSourceMapQuery
313 hir::db::RawItemsQuery 311 hir::db::RawItemsQuery
314 hir::db::CrateDefMapQuery 312 hir::db::CrateDefMapQuery
315 hir::db::GenericParamsQuery 313 hir::db::GenericParamsQuery
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index b6fe48627..294964887 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -27,7 +27,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
27 complete_methods(acc, ctx, &receiver_ty); 27 complete_methods(acc, ctx, &receiver_ty);
28 28
29 // Suggest .await syntax for types that implement Future trait 29 // Suggest .await syntax for types that implement Future trait
30 if ctx.analyzer.impls_future(ctx.db, receiver_ty.into_ty()) { 30 if ctx.analyzer.impls_future(ctx.db, receiver_ty) {
31 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") 31 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
32 .detail("expr.await") 32 .detail("expr.await")
33 .insert_text("await") 33 .insert_text("await")
@@ -217,6 +217,39 @@ mod tests {
217 } 217 }
218 218
219 #[test] 219 #[test]
220 fn test_method_completion_only_fitting_impls() {
221 assert_debug_snapshot!(
222 do_ref_completion(
223 r"
224 struct A<T> {}
225 impl A<u32> {
226 fn the_method(&self) {}
227 }
228 impl A<i32> {
229 fn the_other_method(&self) {}
230 }
231 fn foo(a: A<u32>) {
232 a.<|>
233 }
234 ",
235 ),
236 @r###"
237 [
238 CompletionItem {
239 label: "the_method()",
240 source_range: [243; 243),
241 delete: [243; 243),
242 insert: "the_method()$0",
243 kind: Method,
244 lookup: "the_method",
245 detail: "fn the_method(&self)",
246 },
247 ]
248 "###
249 );
250 }
251
252 #[test]
220 fn test_trait_method_completion() { 253 fn test_trait_method_completion() {
221 assert_debug_snapshot!( 254 assert_debug_snapshot!(
222 do_ref_completion( 255 do_ref_completion(
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs
index 89e0009a1..cc1f7c830 100644
--- a/crates/ra_ide/src/completion/complete_path.rs
+++ b/crates/ra_ide/src/completion/complete_path.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{Adt, Either, HasSource, PathResolution}; 3use hir::{Adt, PathResolution, ScopeDef};
4use ra_syntax::AstNode; 4use ra_syntax::AstNode;
5use test_utils::tested_by; 5use test_utils::tested_by;
6 6
@@ -18,17 +18,15 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
18 match def { 18 match def {
19 hir::ModuleDef::Module(module) => { 19 hir::ModuleDef::Module(module) => {
20 let module_scope = module.scope(ctx.db); 20 let module_scope = module.scope(ctx.db);
21 for (name, def, import) in module_scope { 21 for (name, def) in module_scope {
22 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def { 22 if ctx.use_item_syntax.is_some() {
23 if ctx.use_item_syntax.is_some() { 23 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def {
24 tested_by!(dont_complete_primitive_in_use); 24 tested_by!(dont_complete_primitive_in_use);
25 continue; 25 continue;
26 } 26 }
27 } 27 if let ScopeDef::Unknown = def {
28 if Some(module) == ctx.module { 28 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
29 if let Some(import) = import { 29 if &name_ref.syntax().text() == name.to_string().as_str() {
30 if let Either::A(use_tree) = import.source(ctx.db).value {
31 if use_tree.syntax().text_range().contains_inclusive(ctx.offset) {
32 // for `use self::foo<|>`, don't suggest `foo` as a completion 30 // for `use self::foo<|>`, don't suggest `foo` as a completion
33 tested_by!(dont_complete_current_use); 31 tested_by!(dont_complete_current_use);
34 continue; 32 continue;
@@ -36,6 +34,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
36 } 34 }
37 } 35 }
38 } 36 }
37
39 acc.add_resolution(ctx, name.to_string(), &def); 38 acc.add_resolution(ctx, name.to_string(), &def);
40 } 39 }
41 } 40 }
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 646a30c76..5470dc291 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -12,7 +12,7 @@ use crate::{
12}; 12};
13 13
14pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 14pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
15 if ctx.db.feature_flags.get("completion.enable-postfix") == false { 15 if !ctx.db.feature_flags.get("completion.enable-postfix") {
16 return; 16 return;
17 } 17 }
18 18
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs
index d5739b58a..458d7525e 100644
--- a/crates/ra_ide/src/completion/complete_scope.rs
+++ b/crates/ra_ide/src/completion/complete_scope.rs
@@ -873,4 +873,41 @@ mod tests {
873 "### 873 "###
874 ); 874 );
875 } 875 }
876
877 #[test]
878 fn completes_local_item() {
879 assert_debug_snapshot!(
880 do_reference_completion(
881 "
882 //- /main.rs
883 fn main() {
884 return f<|>;
885 fn frobnicate() {}
886 }
887 "
888 ),
889 @r###"
890 [
891 CompletionItem {
892 label: "frobnicate()",
893 source_range: [23; 24),
894 delete: [23; 24),
895 insert: "frobnicate()$0",
896 kind: Function,
897 lookup: "frobnicate",
898 detail: "fn frobnicate()",
899 },
900 CompletionItem {
901 label: "main()",
902 source_range: [23; 24),
903 delete: [23; 24),
904 insert: "main()$0",
905 kind: Function,
906 lookup: "main",
907 detail: "fn main()",
908 },
909 ]
910 "###
911 )
912 }
876} 913}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index b8345c91d..48d69f7e5 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -19,6 +19,7 @@ pub(crate) struct CompletionContext<'a> {
19 pub(super) offset: TextUnit, 19 pub(super) offset: TextUnit,
20 pub(super) token: SyntaxToken, 20 pub(super) token: SyntaxToken,
21 pub(super) module: Option<hir::Module>, 21 pub(super) module: Option<hir::Module>,
22 pub(super) name_ref_syntax: Option<ast::NameRef>,
22 pub(super) function_syntax: Option<ast::FnDef>, 23 pub(super) function_syntax: Option<ast::FnDef>,
23 pub(super) use_item_syntax: Option<ast::UseItem>, 24 pub(super) use_item_syntax: Option<ast::UseItem>,
24 pub(super) record_lit_syntax: Option<ast::RecordLit>, 25 pub(super) record_lit_syntax: Option<ast::RecordLit>,
@@ -54,13 +55,13 @@ impl<'a> CompletionContext<'a> {
54 let src = hir::ModuleSource::from_position(db, position); 55 let src = hir::ModuleSource::from_position(db, position);
55 let module = hir::Module::from_definition( 56 let module = hir::Module::from_definition(
56 db, 57 db,
57 hir::Source { file_id: position.file_id.into(), value: src }, 58 hir::InFile { file_id: position.file_id.into(), value: src },
58 ); 59 );
59 let token = 60 let token =
60 original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?; 61 original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?;
61 let analyzer = hir::SourceAnalyzer::new( 62 let analyzer = hir::SourceAnalyzer::new(
62 db, 63 db,
63 hir::Source::new(position.file_id.into(), &token.parent()), 64 hir::InFile::new(position.file_id.into(), &token.parent()),
64 Some(position.offset), 65 Some(position.offset),
65 ); 66 );
66 let mut ctx = CompletionContext { 67 let mut ctx = CompletionContext {
@@ -69,6 +70,7 @@ impl<'a> CompletionContext<'a> {
69 token, 70 token,
70 offset: position.offset, 71 offset: position.offset,
71 module, 72 module,
73 name_ref_syntax: None,
72 function_syntax: None, 74 function_syntax: None,
73 use_item_syntax: None, 75 use_item_syntax: None,
74 record_lit_syntax: None, 76 record_lit_syntax: None,
@@ -142,6 +144,8 @@ impl<'a> CompletionContext<'a> {
142 } 144 }
143 145
144 fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) { 146 fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) {
147 self.name_ref_syntax =
148 find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start());
145 let name_range = name_ref.syntax().text_range(); 149 let name_range = name_ref.syntax().text_range();
146 if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { 150 if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() {
147 self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); 151 self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset);
@@ -188,10 +192,9 @@ impl<'a> CompletionContext<'a> {
188 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 192 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
189 self.has_type_args = segment.type_arg_list().is_some(); 193 self.has_type_args = segment.type_arg_list().is_some();
190 194
191 if let Some(mut path) = hir::Path::from_ast(path.clone()) { 195 if let Some(path) = hir::Path::from_ast(path.clone()) {
192 if !path.is_ident() { 196 if let Some(path_prefix) = path.qualifier() {
193 path.segments.pop().unwrap(); 197 self.path_prefix = Some(path_prefix);
194 self.path_prefix = Some(path);
195 return; 198 return;
196 } 199 }
197 } 200 }
@@ -240,16 +243,15 @@ impl<'a> CompletionContext<'a> {
240 .expr() 243 .expr()
241 .map(|e| e.syntax().text_range()) 244 .map(|e| e.syntax().text_range())
242 .and_then(|r| find_node_with_range(original_file.syntax(), r)); 245 .and_then(|r| find_node_with_range(original_file.syntax(), r));
243 self.dot_receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = 246 self.dot_receiver_is_ambiguous_float_literal =
244 &self.dot_receiver 247 if let Some(ast::Expr::Literal(l)) = &self.dot_receiver {
245 { 248 match l.kind() {
246 match l.kind() { 249 ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
247 ast::LiteralKind::FloatNumber { suffix: _ } => l.token().text().ends_with('.'), 250 _ => false,
248 _ => false, 251 }
252 } else {
253 false
249 } 254 }
250 } else {
251 false
252 }
253 } 255 }
254 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { 256 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
255 // As above 257 // As above
diff --git a/crates/ra_ide/src/db.rs b/crates/ra_ide/src/db.rs
index f739ebecd..47d0aed6f 100644
--- a/crates/ra_ide/src/db.rs
+++ b/crates/ra_ide/src/db.rs
@@ -5,7 +5,7 @@ use std::sync::Arc;
5use ra_db::{ 5use ra_db::{
6 salsa::{self, Database, Durability}, 6 salsa::{self, Database, Durability},
7 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, 7 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath,
8 SourceDatabase, SourceDatabaseExt, SourceRootId, 8 SourceDatabase, SourceRootId,
9}; 9};
10use rustc_hash::FxHashMap; 10use rustc_hash::FxHashMap;
11 11
@@ -49,18 +49,6 @@ impl FileLoader for RootDatabase {
49 } 49 }
50} 50}
51 51
52impl hir::debug::HirDebugHelper for RootDatabase {
53 fn crate_name(&self, krate: CrateId) -> Option<String> {
54 self.debug_data.crate_names.get(&krate).cloned()
55 }
56 fn file_path(&self, file_id: FileId) -> Option<String> {
57 let source_root_id = self.file_source_root(file_id);
58 let source_root_path = self.debug_data.root_paths.get(&source_root_id)?;
59 let file_path = self.file_relative_path(file_id);
60 Some(format!("{}/{}", source_root_path, file_path))
61 }
62}
63
64impl salsa::Database for RootDatabase { 52impl salsa::Database for RootDatabase {
65 fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> { 53 fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> {
66 &self.runtime 54 &self.runtime
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index cc1ccab4b..c50a70d99 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -96,7 +96,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
96 }); 96 });
97 let source_file = db.parse(file_id).tree(); 97 let source_file = db.parse(file_id).tree();
98 let src = 98 let src =
99 hir::Source { file_id: file_id.into(), value: hir::ModuleSource::SourceFile(source_file) }; 99 hir::InFile { file_id: file_id.into(), value: hir::ModuleSource::SourceFile(source_file) };
100 if let Some(m) = hir::Module::from_definition(db, src) { 100 if let Some(m) = hir::Module::from_definition(db, src) {
101 m.diagnostics(db, &mut sink); 101 m.diagnostics(db, &mut sink);
102 }; 102 };
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 30617412a..fbe89841b 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -15,7 +15,7 @@ pub use function_signature::FunctionSignature;
15pub use navigation_target::NavigationTarget; 15pub use navigation_target::NavigationTarget;
16pub use structure::{file_structure, StructureNode}; 16pub use structure::{file_structure, StructureNode};
17 17
18pub(crate) use navigation_target::{description_from_symbol, docs_from_symbol, ToNav}; 18pub(crate) use navigation_target::ToNav;
19pub(crate) use short_label::ShortLabel; 19pub(crate) use short_label::ShortLabel;
20 20
21pub(crate) fn function_label(node: &ast::FnDef) -> String { 21pub(crate) fn function_label(node: &ast::FnDef) -> String {
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 6ac60722b..b9ae67828 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -1,11 +1,12 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{AssocItem, Either, FieldSource, HasSource, ModuleSource, Source}; 3use either::Either;
4use hir::{AssocItem, FieldSource, HasSource, InFile, ModuleSource};
4use ra_db::{FileId, SourceDatabase}; 5use ra_db::{FileId, SourceDatabase};
5use ra_syntax::{ 6use ra_syntax::{
6 ast::{self, DocCommentsOwner, NameOwner}, 7 ast::{self, DocCommentsOwner, NameOwner},
7 match_ast, AstNode, SmolStr, 8 match_ast, AstNode, SmolStr,
8 SyntaxKind::{self, BIND_PAT}, 9 SyntaxKind::{self, BIND_PAT, TYPE_PARAM},
9 TextRange, 10 TextRange,
10}; 11};
11 12
@@ -141,7 +142,7 @@ impl NavigationTarget {
141 /// Allows `NavigationTarget` to be created from a `NameOwner` 142 /// Allows `NavigationTarget` to be created from a `NameOwner`
142 pub(crate) fn from_named( 143 pub(crate) fn from_named(
143 db: &RootDatabase, 144 db: &RootDatabase,
144 node: Source<&dyn ast::NameOwner>, 145 node: InFile<&dyn ast::NameOwner>,
145 docs: Option<String>, 146 docs: Option<String>,
146 description: Option<String>, 147 description: Option<String>,
147 ) -> NavigationTarget { 148 ) -> NavigationTarget {
@@ -230,34 +231,20 @@ impl ToNav for hir::Module {
230 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 231 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
231 let src = self.definition_source(db); 232 let src = self.definition_source(db);
232 let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 233 let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default();
233 match &src.value { 234 let syntax = match &src.value {
234 ModuleSource::SourceFile(node) => { 235 ModuleSource::SourceFile(node) => node.syntax(),
235 let frange = original_range(db, src.with_value(node.syntax())); 236 ModuleSource::Module(node) => node.syntax(),
236 237 };
237 NavigationTarget::from_syntax( 238 let frange = original_range(db, src.with_value(syntax));
238 frange.file_id, 239 NavigationTarget::from_syntax(
239 name, 240 frange.file_id,
240 None, 241 name,
241 frange.range, 242 None,
242 node.syntax().kind(), 243 frange.range,
243 None, 244 syntax.kind(),
244 None, 245 None,
245 ) 246 None,
246 } 247 )
247 ModuleSource::Module(node) => {
248 let frange = original_range(db, src.with_value(node.syntax()));
249
250 NavigationTarget::from_syntax(
251 frange.file_id,
252 name,
253 None,
254 frange.range,
255 node.syntax().kind(),
256 node.doc_comment_text(),
257 node.short_label(),
258 )
259 }
260 }
261 } 248 }
262} 249}
263 250
@@ -341,22 +328,43 @@ impl ToNav for hir::AssocItem {
341impl ToNav for hir::Local { 328impl ToNav for hir::Local {
342 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 329 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
343 let src = self.source(db); 330 let src = self.source(db);
344 let (full_range, focus_range) = match src.value { 331 let node = match &src.value {
345 Either::A(it) => { 332 Either::Left(bind_pat) => {
346 (it.syntax().text_range(), it.name().map(|it| it.syntax().text_range())) 333 bind_pat.name().map_or_else(|| bind_pat.syntax().clone(), |it| it.syntax().clone())
347 } 334 }
348 Either::B(it) => (it.syntax().text_range(), Some(it.self_kw_token().text_range())), 335 Either::Right(it) => it.syntax().clone(),
349 }; 336 };
337 let full_range = original_range(db, src.with_value(&node));
350 let name = match self.name(db) { 338 let name = match self.name(db) {
351 Some(it) => it.to_string().into(), 339 Some(it) => it.to_string().into(),
352 None => "".into(), 340 None => "".into(),
353 }; 341 };
354 NavigationTarget { 342 NavigationTarget {
355 file_id: src.file_id.original_file(db), 343 file_id: full_range.file_id,
356 name, 344 name,
357 kind: BIND_PAT, 345 kind: BIND_PAT,
358 full_range, 346 full_range: full_range.range,
359 focus_range, 347 focus_range: None,
348 container_name: None,
349 description: None,
350 docs: None,
351 }
352 }
353}
354
355impl ToNav for hir::TypeParam {
356 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
357 let src = self.source(db);
358 let range = match src.value {
359 Either::Left(it) => it.syntax().text_range(),
360 Either::Right(it) => it.syntax().text_range(),
361 };
362 NavigationTarget {
363 file_id: src.file_id.original_file(db),
364 name: self.name(db).to_string().into(),
365 kind: TYPE_PARAM,
366 full_range: range,
367 focus_range: None,
360 container_name: None, 368 container_name: None,
361 description: None, 369 description: None,
362 docs: None, 370 docs: None,
diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs
index 2f1abf509..7a22bb0a4 100644
--- a/crates/ra_ide/src/expand.rs
+++ b/crates/ra_ide/src/expand.rs
@@ -1,42 +1,71 @@
1//! Utilities to work with files, produced by macros. 1//! Utilities to work with files, produced by macros.
2use std::iter::successors; 2use std::iter::successors;
3 3
4use hir::Source; 4use hir::{InFile, Origin};
5use ra_db::FileId; 5use ra_db::FileId;
6use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken}; 6use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange};
7 7
8use crate::{db::RootDatabase, FileRange}; 8use crate::{db::RootDatabase, FileRange};
9 9
10pub(crate) fn original_range(db: &RootDatabase, node: Source<&SyntaxNode>) -> FileRange { 10pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange {
11 let expansion = match node.file_id.expansion_info(db) { 11 if let Some((range, Origin::Call)) = original_range_and_origin(db, node) {
12 None => { 12 return range;
13 }
14
15 if let Some(expansion) = node.file_id.expansion_info(db) {
16 if let Some(call_node) = expansion.call_node() {
13 return FileRange { 17 return FileRange {
14 file_id: node.file_id.original_file(db), 18 file_id: call_node.file_id.original_file(db),
15 range: node.value.text_range(), 19 range: call_node.value.text_range(),
16 } 20 };
17 } 21 }
18 Some(it) => it, 22 }
19 }; 23
20 // FIXME: the following completely wrong. 24 FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() }
21 // 25}
22 // *First*, we should try to map first and last tokens of node, and, if that 26
23 // fails, return the range of the overall macro expansions. 27fn original_range_and_origin(
24 // 28 db: &RootDatabase,
25 // *Second*, we should handle recurside macro expansions 29 node: InFile<&SyntaxNode>,
26 30) -> Option<(FileRange, Origin)> {
27 let token = node 31 let expansion = node.file_id.expansion_info(db)?;
28 .value 32
29 .descendants_with_tokens() 33 // the input node has only one token ?
30 .filter_map(|it| it.into_token()) 34 let single = node.value.first_token()? == node.value.last_token()?;
31 .find_map(|it| expansion.map_token_up(node.with_value(&it))); 35
32 36 // FIXME: We should handle recurside macro expansions
33 match token { 37 let (range, origin) = node.value.descendants().find_map(|it| {
34 Some(it) => { 38 let first = it.first_token()?;
35 FileRange { file_id: it.file_id.original_file(db), range: it.value.text_range() } 39 let last = it.last_token()?;
40
41 if !single && first == last {
42 return None;
36 } 43 }
37 None => { 44
38 FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } 45 // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens
46 let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?;
47 let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?;
48
49 if first.file_id != last.file_id || first_origin != last_origin {
50 return None;
39 } 51 }
52
53 // FIXME: Add union method in TextRange
54 Some((
55 first.with_value(union_range(first.value.text_range(), last.value.text_range())),
56 first_origin,
57 ))
58 })?;
59
60 return Some((
61 FileRange { file_id: range.file_id.original_file(db), range: range.value },
62 origin,
63 ));
64
65 fn union_range(a: TextRange, b: TextRange) -> TextRange {
66 let start = a.start().min(b.start());
67 let end = a.end().max(b.end());
68 TextRange::from_to(start, end)
40 } 69 }
41} 70}
42 71
@@ -44,8 +73,8 @@ pub(crate) fn descend_into_macros(
44 db: &RootDatabase, 73 db: &RootDatabase,
45 file_id: FileId, 74 file_id: FileId,
46 token: SyntaxToken, 75 token: SyntaxToken,
47) -> Source<SyntaxToken> { 76) -> InFile<SyntaxToken> {
48 let src = Source::new(file_id.into(), token); 77 let src = InFile::new(file_id.into(), token);
49 78
50 successors(Some(src), |token| { 79 successors(Some(src), |token| {
51 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; 80 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index abc602244..bdbc31704 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -22,7 +22,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
22 let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?; 22 let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?;
23 let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?; 23 let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?;
24 24
25 let source = hir::Source::new(position.file_id.into(), mac.syntax()); 25 let source = hir::InFile::new(position.file_id.into(), mac.syntax());
26 let expanded = expand_macro_recur(db, source, source.with_value(&mac))?; 26 let expanded = expand_macro_recur(db, source, source.with_value(&mac))?;
27 27
28 // FIXME: 28 // FIXME:
@@ -34,8 +34,8 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
34 34
35fn expand_macro_recur( 35fn expand_macro_recur(
36 db: &RootDatabase, 36 db: &RootDatabase,
37 source: hir::Source<&SyntaxNode>, 37 source: hir::InFile<&SyntaxNode>,
38 macro_call: hir::Source<&ast::MacroCall>, 38 macro_call: hir::InFile<&ast::MacroCall>,
39) -> Option<SyntaxNode> { 39) -> Option<SyntaxNode> {
40 let analyzer = hir::SourceAnalyzer::new(db, source, None); 40 let analyzer = hir::SourceAnalyzer::new(db, source, None);
41 let expansion = analyzer.expand(db, macro_call)?; 41 let expansion = analyzer.expand(db, macro_call)?;
@@ -46,7 +46,7 @@ fn expand_macro_recur(
46 let mut replaces = FxHashMap::default(); 46 let mut replaces = FxHashMap::default();
47 47
48 for child in children.into_iter() { 48 for child in children.into_iter() {
49 let node = hir::Source::new(macro_file_id, &child); 49 let node = hir::InFile::new(macro_file_id, &child);
50 if let Some(new_node) = expand_macro_recur(db, source, node) { 50 if let Some(new_node) = expand_macro_recur(db, source, node) {
51 // Replace the whole node if it is root 51 // Replace the whole node if it is root
52 // `replace_descendants` will not replace the parent node 52 // `replace_descendants` will not replace the parent node
@@ -86,21 +86,18 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
86 let mut is_next = |f: fn(SyntaxKind) -> bool, default| -> bool { 86 let mut is_next = |f: fn(SyntaxKind) -> bool, default| -> bool {
87 token_iter.peek().map(|it| f(it.kind())).unwrap_or(default) 87 token_iter.peek().map(|it| f(it.kind())).unwrap_or(default)
88 }; 88 };
89 let is_last = |f: fn(SyntaxKind) -> bool, default| -> bool { 89 let is_last =
90 last.map(|it| f(it)).unwrap_or(default) 90 |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
91 };
92 91
93 res += &match token.kind() { 92 res += &match token.kind() {
94 k @ _ if is_text(k) && is_next(|it| !it.is_punct(), true) => { 93 k if is_text(k) && is_next(|it| !it.is_punct(), true) => token.text().to_string() + " ",
95 token.text().to_string() + " "
96 }
97 L_CURLY if is_next(|it| it != R_CURLY, true) => { 94 L_CURLY if is_next(|it| it != R_CURLY, true) => {
98 indent += 1; 95 indent += 1;
99 let leading_space = if is_last(|it| is_text(it), false) { " " } else { "" }; 96 let leading_space = if is_last(is_text, false) { " " } else { "" };
100 format!("{}{{\n{}", leading_space, " ".repeat(indent)) 97 format!("{}{{\n{}", leading_space, " ".repeat(indent))
101 } 98 }
102 R_CURLY if is_last(|it| it != L_CURLY, true) => { 99 R_CURLY if is_last(|it| it != L_CURLY, true) => {
103 indent = indent.checked_sub(1).unwrap_or(0); 100 indent = indent.saturating_sub(1);
104 format!("\n{}}}", " ".repeat(indent)) 101 format!("\n{}}}", " ".repeat(indent))
105 } 102 }
106 R_CURLY => format!("}}\n{}", " ".repeat(indent)), 103 R_CURLY => format!("}}\n{}", " ".repeat(indent)),
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index 4b7bfc0b1..1ec41a117 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -34,6 +34,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
34 ARG_LIST, 34 ARG_LIST,
35 ARRAY_EXPR, 35 ARRAY_EXPR,
36 TUPLE_EXPR, 36 TUPLE_EXPR,
37 TUPLE_TYPE,
37 WHERE_CLAUSE, 38 WHERE_CLAUSE,
38 ]; 39 ];
39 40
@@ -137,7 +138,7 @@ fn extend_ws(root: &SyntaxNode, ws: SyntaxToken, offset: TextUnit) -> TextRange
137 ws.text_range() 138 ws.text_range()
138} 139}
139 140
140fn pick_best<'a>(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken { 141fn pick_best(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken {
141 return if priority(&r) > priority(&l) { r } else { l }; 142 return if priority(&r) > priority(&l) { r } else { l };
142 fn priority(n: &SyntaxToken) -> usize { 143 fn priority(n: &SyntaxToken) -> usize {
143 match n.kind() { 144 match n.kind() {
@@ -174,12 +175,7 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
174 TYPE_BOUND => T![+], 175 TYPE_BOUND => T![+],
175 _ => T![,], 176 _ => T![,],
176 }; 177 };
177 if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Prev) { 178
178 return Some(TextRange::from_to(
179 delimiter_node.text_range().start(),
180 node.text_range().end(),
181 ));
182 }
183 if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Next) { 179 if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Next) {
184 // Include any following whitespace when delimiter is after list item. 180 // Include any following whitespace when delimiter is after list item.
185 let final_node = delimiter_node 181 let final_node = delimiter_node
@@ -190,6 +186,12 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
190 186
191 return Some(TextRange::from_to(node.text_range().start(), final_node.text_range().end())); 187 return Some(TextRange::from_to(node.text_range().start(), final_node.text_range().end()));
192 } 188 }
189 if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Prev) {
190 return Some(TextRange::from_to(
191 delimiter_node.text_range().start(),
192 node.text_range().end(),
193 ));
194 }
193 195
194 None 196 None
195} 197}
@@ -250,14 +252,14 @@ mod tests {
250 fn test_extend_selection_list() { 252 fn test_extend_selection_list() {
251 do_check(r#"fn foo(<|>x: i32) {}"#, &["x", "x: i32"]); 253 do_check(r#"fn foo(<|>x: i32) {}"#, &["x", "x: i32"]);
252 do_check(r#"fn foo(<|>x: i32, y: i32) {}"#, &["x", "x: i32", "x: i32, "]); 254 do_check(r#"fn foo(<|>x: i32, y: i32) {}"#, &["x", "x: i32", "x: i32, "]);
253 do_check(r#"fn foo(<|>x: i32,y: i32) {}"#, &["x", "x: i32", "x: i32,"]); 255 do_check(r#"fn foo(<|>x: i32,y: i32) {}"#, &["x", "x: i32", "x: i32,", "(x: i32,y: i32)"]);
254 do_check(r#"fn foo(x: i32, <|>y: i32) {}"#, &["y", "y: i32", ", y: i32"]); 256 do_check(r#"fn foo(x: i32, <|>y: i32) {}"#, &["y", "y: i32", ", y: i32"]);
255 do_check(r#"fn foo(x: i32, <|>y: i32, ) {}"#, &["y", "y: i32", ", y: i32"]); 257 do_check(r#"fn foo(x: i32, <|>y: i32, ) {}"#, &["y", "y: i32", "y: i32, "]);
256 do_check(r#"fn foo(x: i32,<|>y: i32) {}"#, &["y", "y: i32", ",y: i32"]); 258 do_check(r#"fn foo(x: i32,<|>y: i32) {}"#, &["y", "y: i32", ",y: i32"]);
257 259
258 do_check(r#"const FOO: [usize; 2] = [ 22<|> , 33];"#, &["22", "22 , "]); 260 do_check(r#"const FOO: [usize; 2] = [ 22<|> , 33];"#, &["22", "22 , "]);
259 do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|>];"#, &["33", ", 33"]); 261 do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|>];"#, &["33", ", 33"]);
260 do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|> ,];"#, &["33", ", 33"]); 262 do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|> ,];"#, &["33", "33 ,", "[ 22 , 33 ,]"]);
261 263
262 do_check(r#"fn main() { (1, 2<|>) }"#, &["2", ", 2", "(1, 2)"]); 264 do_check(r#"fn main() { (1, 2<|>) }"#, &["2", ", 2", "(1, 2)"]);
263 265
@@ -276,7 +278,7 @@ const FOO: [usize; 2] = [
276 22 278 22
277 , 33<|>, 279 , 33<|>,
278]"#, 280]"#,
279 &["33", ", 33"], 281 &["33", "33,"],
280 ); 282 );
281 } 283 }
282 284
@@ -424,7 +426,7 @@ fn foo<R>()
424 do_check(r#"fn foo<T>() where T: <|>Copy +Display"#, &["Copy", "Copy +"]); 426 do_check(r#"fn foo<T>() where T: <|>Copy +Display"#, &["Copy", "Copy +"]);
425 do_check(r#"fn foo<T>() where T: <|>Copy+Display"#, &["Copy", "Copy+"]); 427 do_check(r#"fn foo<T>() where T: <|>Copy+Display"#, &["Copy", "Copy+"]);
426 do_check(r#"fn foo<T>() where T: Copy + <|>Display"#, &["Display", "+ Display"]); 428 do_check(r#"fn foo<T>() where T: Copy + <|>Display"#, &["Display", "+ Display"]);
427 do_check(r#"fn foo<T>() where T: Copy + <|>Display + Sync"#, &["Display", "+ Display"]); 429 do_check(r#"fn foo<T>() where T: Copy + <|>Display + Sync"#, &["Display", "Display + "]);
428 do_check(r#"fn foo<T>() where T: Copy +<|>Display"#, &["Display", "+Display"]); 430 do_check(r#"fn foo<T>() where T: Copy +<|>Display"#, &["Display", "+Display"]);
429 } 431 }
430 432
@@ -435,7 +437,7 @@ fn foo<R>()
435 do_check(r#"fn foo<T: <|>Copy +Display>() {}"#, &["Copy", "Copy +"]); 437 do_check(r#"fn foo<T: <|>Copy +Display>() {}"#, &["Copy", "Copy +"]);
436 do_check(r#"fn foo<T: <|>Copy+Display>() {}"#, &["Copy", "Copy+"]); 438 do_check(r#"fn foo<T: <|>Copy+Display>() {}"#, &["Copy", "Copy+"]);
437 do_check(r#"fn foo<T: Copy + <|>Display>() {}"#, &["Display", "+ Display"]); 439 do_check(r#"fn foo<T: Copy + <|>Display>() {}"#, &["Display", "+ Display"]);
438 do_check(r#"fn foo<T: Copy + <|>Display + Sync>() {}"#, &["Display", "+ Display"]); 440 do_check(r#"fn foo<T: Copy + <|>Display + Sync>() {}"#, &["Display", "Display + "]);
439 do_check(r#"fn foo<T: Copy +<|>Display>() {}"#, &["Display", "+Display"]); 441 do_check(r#"fn foo<T: Copy +<|>Display>() {}"#, &["Display", "+Display"]);
440 do_check( 442 do_check(
441 r#"fn foo<T: Copy<|> + Display, U: Copy>() {}"#, 443 r#"fn foo<T: Copy<|> + Display, U: Copy>() {}"#,
@@ -449,4 +451,56 @@ fn foo<R>()
449 ], 451 ],
450 ); 452 );
451 } 453 }
454
455 #[test]
456 fn test_extend_selection_on_tuple_in_type() {
457 do_check(
458 r#"fn main() { let _: (krate, <|>_crate_def_map, module_id) = (); }"#,
459 &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"],
460 );
461 // white space variations
462 do_check(
463 r#"fn main() { let _: (krate,<|>_crate_def_map,module_id) = (); }"#,
464 &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"],
465 );
466 do_check(
467 r#"
468fn main() { let _: (
469 krate,
470 _crate<|>_def_map,
471 module_id
472) = (); }"#,
473 &[
474 "_crate_def_map",
475 "_crate_def_map,",
476 "(\n krate,\n _crate_def_map,\n module_id\n)",
477 ],
478 );
479 }
480
481 #[test]
482 fn test_extend_selection_on_tuple_in_rvalue() {
483 do_check(
484 r#"fn main() { let var = (krate, _crate_def_map<|>, module_id); }"#,
485 &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"],
486 );
487 // white space variations
488 do_check(
489 r#"fn main() { let var = (krate,_crate<|>_def_map,module_id); }"#,
490 &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"],
491 );
492 do_check(
493 r#"
494fn main() { let var = (
495 krate,
496 _crate_def_map<|>,
497 module_id
498); }"#,
499 &[
500 "_crate_def_map",
501 "_crate_def_map,",
502 "(\n krate,\n _crate_def_map,\n module_id\n)",
503 ],
504 );
505 }
452} 506}
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index c10a6c844..79d332e8c 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,9 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{db::AstDatabase, Source}; 3use hir::{db::AstDatabase, InFile};
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, DocCommentsOwner}, 5 ast::{self, DocCommentsOwner},
6 match_ast, AstNode, SyntaxNode, 6 match_ast, AstNode,
7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken, TokenAtOffset,
7}; 9};
8 10
9use crate::{ 11use crate::{
@@ -19,25 +21,33 @@ pub(crate) fn goto_definition(
19 position: FilePosition, 21 position: FilePosition,
20) -> Option<RangeInfo<Vec<NavigationTarget>>> { 22) -> Option<RangeInfo<Vec<NavigationTarget>>> {
21 let file = db.parse_or_expand(position.file_id.into())?; 23 let file = db.parse_or_expand(position.file_id.into())?;
22 let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; 24 let original_token = pick_best(file.token_at_offset(position.offset))?;
23 let token = descend_into_macros(db, position.file_id, token); 25 let token = descend_into_macros(db, position.file_id, original_token.clone());
24 26
25 let res = match_ast! { 27 let nav_targets = match_ast! {
26 match (token.value.parent()) { 28 match (token.value.parent()) {
27 ast::NameRef(name_ref) => { 29 ast::NameRef(name_ref) => {
28 let navs = reference_definition(db, token.with_value(&name_ref)).to_vec(); 30 reference_definition(db, token.with_value(&name_ref)).to_vec()
29 RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec())
30 }, 31 },
31 ast::Name(name) => { 32 ast::Name(name) => {
32 let navs = name_definition(db, token.with_value(&name))?; 33 name_definition(db, token.with_value(&name))?
33 RangeInfo::new(name.syntax().text_range(), navs)
34
35 }, 34 },
36 _ => return None, 35 _ => return None,
37 } 36 }
38 }; 37 };
39 38
40 Some(res) 39 Some(RangeInfo::new(original_token.text_range(), nav_targets))
40}
41
42fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
43 return tokens.max_by_key(priority);
44 fn priority(n: &SyntaxToken) -> usize {
45 match n.kind() {
46 IDENT | INT_NUMBER => 2,
47 kind if kind.is_trivia() => 0,
48 _ => 1,
49 }
50 }
41} 51}
42 52
43#[derive(Debug)] 53#[derive(Debug)]
@@ -58,15 +68,17 @@ impl ReferenceResult {
58 68
59pub(crate) fn reference_definition( 69pub(crate) fn reference_definition(
60 db: &RootDatabase, 70 db: &RootDatabase,
61 name_ref: Source<&ast::NameRef>, 71 name_ref: InFile<&ast::NameRef>,
62) -> ReferenceResult { 72) -> ReferenceResult {
63 use self::ReferenceResult::*; 73 use self::ReferenceResult::*;
64 74
65 let name_kind = classify_name_ref(db, name_ref).map(|d| d.kind); 75 let name_kind = classify_name_ref(db, name_ref).map(|d| d.kind);
66 match name_kind { 76 match name_kind {
67 Some(Macro(mac)) => return Exact(mac.to_nav(db)), 77 Some(Macro(it)) => return Exact(it.to_nav(db)),
68 Some(Field(field)) => return Exact(field.to_nav(db)), 78 Some(Field(it)) => return Exact(it.to_nav(db)),
69 Some(AssocItem(assoc)) => return Exact(assoc.to_nav(db)), 79 Some(TypeParam(it)) => return Exact(it.to_nav(db)),
80 Some(AssocItem(it)) => return Exact(it.to_nav(db)),
81 Some(Local(it)) => return Exact(it.to_nav(db)),
70 Some(Def(def)) => match NavigationTarget::from_def(db, def) { 82 Some(Def(def)) => match NavigationTarget::from_def(db, def) {
71 Some(nav) => return Exact(nav), 83 Some(nav) => return Exact(nav),
72 None => return Approximate(vec![]), 84 None => return Approximate(vec![]),
@@ -77,10 +89,6 @@ pub(crate) fn reference_definition(
77 // us to the actual type 89 // us to the actual type
78 return Exact(imp.to_nav(db)); 90 return Exact(imp.to_nav(db));
79 } 91 }
80 Some(Local(local)) => return Exact(local.to_nav(db)),
81 Some(GenericParam(_)) => {
82 // FIXME: go to the generic param def
83 }
84 None => {} 92 None => {}
85 }; 93 };
86 94
@@ -94,7 +102,7 @@ pub(crate) fn reference_definition(
94 102
95pub(crate) fn name_definition( 103pub(crate) fn name_definition(
96 db: &RootDatabase, 104 db: &RootDatabase,
97 name: Source<&ast::Name>, 105 name: InFile<&ast::Name>,
98) -> Option<Vec<NavigationTarget>> { 106) -> Option<Vec<NavigationTarget>> {
99 let parent = name.value.syntax().parent()?; 107 let parent = name.value.syntax().parent()?;
100 108
@@ -115,7 +123,7 @@ pub(crate) fn name_definition(
115 None 123 None
116} 124}
117 125
118fn named_target(db: &RootDatabase, node: Source<&SyntaxNode>) -> Option<NavigationTarget> { 126fn named_target(db: &RootDatabase, node: InFile<&SyntaxNode>) -> Option<NavigationTarget> {
119 match_ast! { 127 match_ast! {
120 match (node.value) { 128 match (node.value) {
121 ast::StructDef(it) => { 129 ast::StructDef(it) => {
@@ -213,21 +221,44 @@ fn named_target(db: &RootDatabase, node: Source<&SyntaxNode>) -> Option<Navigati
213 221
214#[cfg(test)] 222#[cfg(test)]
215mod tests { 223mod tests {
216 use test_utils::covers; 224 use test_utils::{assert_eq_text, covers};
217 225
218 use crate::mock_analysis::analysis_and_position; 226 use crate::mock_analysis::analysis_and_position;
219 227
220 fn check_goto(fixture: &str, expected: &str) { 228 fn check_goto(fixture: &str, expected: &str, expected_range: &str) {
221 let (analysis, pos) = analysis_and_position(fixture); 229 let (analysis, pos) = analysis_and_position(fixture);
222 230
223 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; 231 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info;
224 assert_eq!(navs.len(), 1); 232 assert_eq!(navs.len(), 1);
233
225 let nav = navs.pop().unwrap(); 234 let nav = navs.pop().unwrap();
235 let file_text = analysis.file_text(nav.file_id()).unwrap();
236
237 let mut actual = file_text[nav.full_range()].to_string();
238 if let Some(focus) = nav.focus_range() {
239 actual += "|";
240 actual += &file_text[focus];
241 }
242
243 if !expected_range.contains("...") {
244 test_utils::assert_eq_text!(&actual, expected_range);
245 } else {
246 let mut parts = expected_range.split("...");
247 let prefix = parts.next().unwrap();
248 let suffix = parts.next().unwrap();
249 assert!(
250 actual.starts_with(prefix) && actual.ends_with(suffix),
251 "\nExpected: {}\n Actual: {}\n",
252 expected_range,
253 actual
254 );
255 }
256
226 nav.assert_match(expected); 257 nav.assert_match(expected);
227 } 258 }
228 259
229 #[test] 260 #[test]
230 fn goto_definition_works_in_items() { 261 fn goto_def_in_items() {
231 check_goto( 262 check_goto(
232 " 263 "
233 //- /lib.rs 264 //- /lib.rs
@@ -235,6 +266,20 @@ mod tests {
235 enum E { X(Foo<|>) } 266 enum E { X(Foo<|>) }
236 ", 267 ",
237 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", 268 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
269 "struct Foo;|Foo",
270 );
271 }
272
273 #[test]
274 fn goto_def_at_start_of_item() {
275 check_goto(
276 "
277 //- /lib.rs
278 struct Foo;
279 enum E { X(<|>Foo) }
280 ",
281 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
282 "struct Foo;|Foo",
238 ); 283 );
239 } 284 }
240 285
@@ -247,61 +292,65 @@ mod tests {
247 mod a; 292 mod a;
248 mod b; 293 mod b;
249 enum E { X(Foo<|>) } 294 enum E { X(Foo<|>) }
295
250 //- /a.rs 296 //- /a.rs
251 struct Foo; 297 struct Foo;
298
252 //- /b.rs 299 //- /b.rs
253 struct Foo; 300 struct Foo;
254 ", 301 ",
255 "Foo STRUCT_DEF FileId(2) [0; 11) [7; 10)", 302 "Foo STRUCT_DEF FileId(2) [0; 11) [7; 10)",
303 "struct Foo;|Foo",
256 ); 304 );
257 } 305 }
258 306
259 #[test] 307 #[test]
260 fn goto_definition_works_for_module_declaration() { 308 fn goto_def_for_module_declaration() {
261 check_goto( 309 check_goto(
262 " 310 "
263 //- /lib.rs 311 //- /lib.rs
264 mod <|>foo; 312 mod <|>foo;
313
265 //- /foo.rs 314 //- /foo.rs
266 // empty 315 // empty
267 ", 316 ",
268 "foo SOURCE_FILE FileId(2) [0; 10)", 317 "foo SOURCE_FILE FileId(2) [0; 10)",
318 "// empty\n\n",
269 ); 319 );
270 320
271 check_goto( 321 check_goto(
272 " 322 "
273 //- /lib.rs 323 //- /lib.rs
274 mod <|>foo; 324 mod <|>foo;
325
275 //- /foo/mod.rs 326 //- /foo/mod.rs
276 // empty 327 // empty
277 ", 328 ",
278 "foo SOURCE_FILE FileId(2) [0; 10)", 329 "foo SOURCE_FILE FileId(2) [0; 10)",
330 "// empty\n\n",
279 ); 331 );
280 } 332 }
281 333
282 #[test] 334 #[test]
283 fn goto_definition_works_for_macros() { 335 fn goto_def_for_macros() {
284 covers!(goto_definition_works_for_macros); 336 covers!(goto_def_for_macros);
285 check_goto( 337 check_goto(
286 " 338 "
287 //- /lib.rs 339 //- /lib.rs
288 macro_rules! foo { 340 macro_rules! foo { () => { () } }
289 () => {
290 {}
291 };
292 }
293 341
294 fn bar() { 342 fn bar() {
295 <|>foo!(); 343 <|>foo!();
296 } 344 }
297 ", 345 ",
298 "foo MACRO_CALL FileId(1) [0; 50) [13; 16)", 346 "foo MACRO_CALL FileId(1) [0; 33) [13; 16)",
347 "macro_rules! foo { () => { () } }|foo",
299 ); 348 );
300 } 349 }
301 350
302 #[test] 351 #[test]
303 fn goto_definition_works_for_macros_from_other_crates() { 352 fn goto_def_for_macros_from_other_crates() {
304 covers!(goto_definition_works_for_macros); 353 covers!(goto_def_for_macros);
305 check_goto( 354 check_goto(
306 " 355 "
307 //- /lib.rs 356 //- /lib.rs
@@ -312,18 +361,15 @@ mod tests {
312 361
313 //- /foo/lib.rs 362 //- /foo/lib.rs
314 #[macro_export] 363 #[macro_export]
315 macro_rules! foo { 364 macro_rules! foo { () => { () } }
316 () => {
317 {}
318 };
319 }
320 ", 365 ",
321 "foo MACRO_CALL FileId(2) [0; 66) [29; 32)", 366 "foo MACRO_CALL FileId(2) [0; 49) [29; 32)",
367 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
322 ); 368 );
323 } 369 }
324 370
325 #[test] 371 #[test]
326 fn goto_definition_works_for_macros_in_use_tree() { 372 fn goto_def_for_macros_in_use_tree() {
327 check_goto( 373 check_goto(
328 " 374 "
329 //- /lib.rs 375 //- /lib.rs
@@ -331,18 +377,15 @@ mod tests {
331 377
332 //- /foo/lib.rs 378 //- /foo/lib.rs
333 #[macro_export] 379 #[macro_export]
334 macro_rules! foo { 380 macro_rules! foo { () => { () } }
335 () => {
336 {}
337 };
338 }
339 ", 381 ",
340 "foo MACRO_CALL FileId(2) [0; 66) [29; 32)", 382 "foo MACRO_CALL FileId(2) [0; 49) [29; 32)",
383 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
341 ); 384 );
342 } 385 }
343 386
344 #[test] 387 #[test]
345 fn goto_definition_works_for_macro_defined_fn_with_arg() { 388 fn goto_def_for_macro_defined_fn_with_arg() {
346 check_goto( 389 check_goto(
347 " 390 "
348 //- /lib.rs 391 //- /lib.rs
@@ -350,20 +393,19 @@ mod tests {
350 ($name:ident) => (fn $name() {}) 393 ($name:ident) => (fn $name() {})
351 } 394 }
352 395
353 define_fn!( 396 define_fn!(foo);
354 foo
355 )
356 397
357 fn bar() { 398 fn bar() {
358 <|>foo(); 399 <|>foo();
359 } 400 }
360 ", 401 ",
361 "foo FN_DEF FileId(1) [80; 83) [80; 83)", 402 "foo FN_DEF FileId(1) [64; 80) [75; 78)",
403 "define_fn!(foo);|foo",
362 ); 404 );
363 } 405 }
364 406
365 #[test] 407 #[test]
366 fn goto_definition_works_for_macro_defined_fn_no_arg() { 408 fn goto_def_for_macro_defined_fn_no_arg() {
367 check_goto( 409 check_goto(
368 " 410 "
369 //- /lib.rs 411 //- /lib.rs
@@ -377,32 +419,70 @@ mod tests {
377 <|>foo(); 419 <|>foo();
378 } 420 }
379 ", 421 ",
380 "foo FN_DEF FileId(1) [39; 42) [39; 42)", 422 "foo FN_DEF FileId(1) [51; 64) [51; 64)",
423 "define_fn!();|define_fn!();",
381 ); 424 );
382 } 425 }
383 426
384 #[test] 427 #[test]
385 fn goto_definition_works_for_methods() { 428 fn goto_definition_works_for_macro_inside_pattern() {
386 covers!(goto_definition_works_for_methods); 429 check_goto(
430 "
431 //- /lib.rs
432 macro_rules! foo {() => {0}}
433
434 fn bar() {
435 match (0,1) {
436 (<|>foo!(), _) => {}
437 }
438 }
439 ",
440 "foo MACRO_CALL FileId(1) [0; 28) [13; 16)",
441 "macro_rules! foo {() => {0}}|foo",
442 );
443 }
444
445 #[test]
446 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
447 check_goto(
448 "
449 //- /lib.rs
450 macro_rules! foo {() => {0}}
451
452 fn bar() {
453 match 0 {
454 <|>foo!() => {}
455 }
456 }
457 ",
458 "foo MACRO_CALL FileId(1) [0; 28) [13; 16)",
459 "macro_rules! foo {() => {0}}|foo",
460 );
461 }
462
463 #[test]
464 fn goto_def_for_methods() {
465 covers!(goto_def_for_methods);
387 check_goto( 466 check_goto(
388 " 467 "
389 //- /lib.rs 468 //- /lib.rs
390 struct Foo; 469 struct Foo;
391 impl Foo { 470 impl Foo {
392 fn frobnicate(&self) { } 471 fn frobnicate(&self) { }
393 } 472 }
394 473
395 fn bar(foo: &Foo) { 474 fn bar(foo: &Foo) {
396 foo.frobnicate<|>(); 475 foo.frobnicate<|>();
397 } 476 }
398 ", 477 ",
399 "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)", 478 "frobnicate FN_DEF FileId(1) [27; 51) [30; 40)",
479 "fn frobnicate(&self) { }|frobnicate",
400 ); 480 );
401 } 481 }
402 482
403 #[test] 483 #[test]
404 fn goto_definition_works_for_fields() { 484 fn goto_def_for_fields() {
405 covers!(goto_definition_works_for_fields); 485 covers!(goto_def_for_fields);
406 check_goto( 486 check_goto(
407 " 487 "
408 //- /lib.rs 488 //- /lib.rs
@@ -415,12 +495,13 @@ mod tests {
415 } 495 }
416 ", 496 ",
417 "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", 497 "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)",
498 "spam: u32|spam",
418 ); 499 );
419 } 500 }
420 501
421 #[test] 502 #[test]
422 fn goto_definition_works_for_record_fields() { 503 fn goto_def_for_record_fields() {
423 covers!(goto_definition_works_for_record_fields); 504 covers!(goto_def_for_record_fields);
424 check_goto( 505 check_goto(
425 " 506 "
426 //- /lib.rs 507 //- /lib.rs
@@ -435,29 +516,48 @@ mod tests {
435 } 516 }
436 ", 517 ",
437 "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", 518 "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)",
519 "spam: u32|spam",
520 );
521 }
522
523 #[test]
524 fn goto_for_tuple_fields() {
525 check_goto(
526 "
527 //- /lib.rs
528 struct Foo(u32);
529
530 fn bar() {
531 let foo = Foo(0);
532 foo.<|>0;
533 }
534 ",
535 "TUPLE_FIELD_DEF FileId(1) [11; 14)",
536 "u32",
438 ); 537 );
439 } 538 }
440 539
441 #[test] 540 #[test]
442 fn goto_definition_works_for_ufcs_inherent_methods() { 541 fn goto_def_for_ufcs_inherent_methods() {
443 check_goto( 542 check_goto(
444 " 543 "
445 //- /lib.rs 544 //- /lib.rs
446 struct Foo; 545 struct Foo;
447 impl Foo { 546 impl Foo {
448 fn frobnicate() { } 547 fn frobnicate() { }
449 } 548 }
450 549
451 fn bar(foo: &Foo) { 550 fn bar(foo: &Foo) {
452 Foo::frobnicate<|>(); 551 Foo::frobnicate<|>();
453 } 552 }
454 ", 553 ",
455 "frobnicate FN_DEF FileId(1) [27; 47) [30; 40)", 554 "frobnicate FN_DEF FileId(1) [27; 46) [30; 40)",
555 "fn frobnicate() { }|frobnicate",
456 ); 556 );
457 } 557 }
458 558
459 #[test] 559 #[test]
460 fn goto_definition_works_for_ufcs_trait_methods_through_traits() { 560 fn goto_def_for_ufcs_trait_methods_through_traits() {
461 check_goto( 561 check_goto(
462 " 562 "
463 //- /lib.rs 563 //- /lib.rs
@@ -470,11 +570,12 @@ mod tests {
470 } 570 }
471 ", 571 ",
472 "frobnicate FN_DEF FileId(1) [16; 32) [19; 29)", 572 "frobnicate FN_DEF FileId(1) [16; 32) [19; 29)",
573 "fn frobnicate();|frobnicate",
473 ); 574 );
474 } 575 }
475 576
476 #[test] 577 #[test]
477 fn goto_definition_works_for_ufcs_trait_methods_through_self() { 578 fn goto_def_for_ufcs_trait_methods_through_self() {
478 check_goto( 579 check_goto(
479 " 580 "
480 //- /lib.rs 581 //- /lib.rs
@@ -489,6 +590,7 @@ mod tests {
489 } 590 }
490 ", 591 ",
491 "frobnicate FN_DEF FileId(1) [30; 46) [33; 43)", 592 "frobnicate FN_DEF FileId(1) [30; 46) [33; 43)",
593 "fn frobnicate();|frobnicate",
492 ); 594 );
493 } 595 }
494 596
@@ -505,6 +607,7 @@ mod tests {
505 } 607 }
506 ", 608 ",
507 "impl IMPL_BLOCK FileId(1) [12; 73)", 609 "impl IMPL_BLOCK FileId(1) [12; 73)",
610 "impl Foo {...}",
508 ); 611 );
509 612
510 check_goto( 613 check_goto(
@@ -518,6 +621,7 @@ mod tests {
518 } 621 }
519 ", 622 ",
520 "impl IMPL_BLOCK FileId(1) [12; 73)", 623 "impl IMPL_BLOCK FileId(1) [12; 73)",
624 "impl Foo {...}",
521 ); 625 );
522 626
523 check_goto( 627 check_goto(
@@ -531,6 +635,7 @@ mod tests {
531 } 635 }
532 ", 636 ",
533 "impl IMPL_BLOCK FileId(1) [15; 75)", 637 "impl IMPL_BLOCK FileId(1) [15; 75)",
638 "impl Foo {...}",
534 ); 639 );
535 640
536 check_goto( 641 check_goto(
@@ -543,6 +648,7 @@ mod tests {
543 } 648 }
544 ", 649 ",
545 "impl IMPL_BLOCK FileId(1) [15; 62)", 650 "impl IMPL_BLOCK FileId(1) [15; 62)",
651 "impl Foo {...}",
546 ); 652 );
547 } 653 }
548 654
@@ -562,6 +668,7 @@ mod tests {
562 } 668 }
563 ", 669 ",
564 "impl IMPL_BLOCK FileId(1) [49; 115)", 670 "impl IMPL_BLOCK FileId(1) [49; 115)",
671 "impl Make for Foo {...}",
565 ); 672 );
566 673
567 check_goto( 674 check_goto(
@@ -578,17 +685,19 @@ mod tests {
578 } 685 }
579 ", 686 ",
580 "impl IMPL_BLOCK FileId(1) [49; 115)", 687 "impl IMPL_BLOCK FileId(1) [49; 115)",
688 "impl Make for Foo {...}",
581 ); 689 );
582 } 690 }
583 691
584 #[test] 692 #[test]
585 fn goto_definition_works_when_used_on_definition_name_itself() { 693 fn goto_def_when_used_on_definition_name_itself() {
586 check_goto( 694 check_goto(
587 " 695 "
588 //- /lib.rs 696 //- /lib.rs
589 struct Foo<|> { value: u32 } 697 struct Foo<|> { value: u32 }
590 ", 698 ",
591 "Foo STRUCT_DEF FileId(1) [0; 25) [7; 10)", 699 "Foo STRUCT_DEF FileId(1) [0; 25) [7; 10)",
700 "struct Foo { value: u32 }|Foo",
592 ); 701 );
593 702
594 check_goto( 703 check_goto(
@@ -599,15 +708,16 @@ mod tests {
599 } 708 }
600 "#, 709 "#,
601 "field RECORD_FIELD_DEF FileId(1) [17; 30) [17; 22)", 710 "field RECORD_FIELD_DEF FileId(1) [17; 30) [17; 22)",
711 "field: string|field",
602 ); 712 );
603 713
604 check_goto( 714 check_goto(
605 " 715 "
606 //- /lib.rs 716 //- /lib.rs
607 fn foo_test<|>() { 717 fn foo_test<|>() { }
608 }
609 ", 718 ",
610 "foo_test FN_DEF FileId(1) [0; 17) [3; 11)", 719 "foo_test FN_DEF FileId(1) [0; 17) [3; 11)",
720 "fn foo_test() { }|foo_test",
611 ); 721 );
612 722
613 check_goto( 723 check_goto(
@@ -618,6 +728,7 @@ mod tests {
618 } 728 }
619 ", 729 ",
620 "Foo ENUM_DEF FileId(1) [0; 25) [5; 8)", 730 "Foo ENUM_DEF FileId(1) [0; 25) [5; 8)",
731 "enum Foo {...}|Foo",
621 ); 732 );
622 733
623 check_goto( 734 check_goto(
@@ -630,22 +741,25 @@ mod tests {
630 } 741 }
631 ", 742 ",
632 "Variant2 ENUM_VARIANT FileId(1) [29; 37) [29; 37)", 743 "Variant2 ENUM_VARIANT FileId(1) [29; 37) [29; 37)",
744 "Variant2|Variant2",
633 ); 745 );
634 746
635 check_goto( 747 check_goto(
636 r#" 748 r#"
637 //- /lib.rs 749 //- /lib.rs
638 static inner<|>: &str = ""; 750 static INNER<|>: &str = "";
639 "#, 751 "#,
640 "inner STATIC_DEF FileId(1) [0; 24) [7; 12)", 752 "INNER STATIC_DEF FileId(1) [0; 24) [7; 12)",
753 "static INNER: &str = \"\";|INNER",
641 ); 754 );
642 755
643 check_goto( 756 check_goto(
644 r#" 757 r#"
645 //- /lib.rs 758 //- /lib.rs
646 const inner<|>: &str = ""; 759 const INNER<|>: &str = "";
647 "#, 760 "#,
648 "inner CONST_DEF FileId(1) [0; 23) [6; 11)", 761 "INNER CONST_DEF FileId(1) [0; 23) [6; 11)",
762 "const INNER: &str = \"\";|INNER",
649 ); 763 );
650 764
651 check_goto( 765 check_goto(
@@ -654,24 +768,25 @@ mod tests {
654 type Thing<|> = Option<()>; 768 type Thing<|> = Option<()>;
655 "#, 769 "#,
656 "Thing TYPE_ALIAS_DEF FileId(1) [0; 24) [5; 10)", 770 "Thing TYPE_ALIAS_DEF FileId(1) [0; 24) [5; 10)",
771 "type Thing = Option<()>;|Thing",
657 ); 772 );
658 773
659 check_goto( 774 check_goto(
660 r#" 775 r#"
661 //- /lib.rs 776 //- /lib.rs
662 trait Foo<|> { 777 trait Foo<|> { }
663 }
664 "#, 778 "#,
665 "Foo TRAIT_DEF FileId(1) [0; 13) [6; 9)", 779 "Foo TRAIT_DEF FileId(1) [0; 13) [6; 9)",
780 "trait Foo { }|Foo",
666 ); 781 );
667 782
668 check_goto( 783 check_goto(
669 r#" 784 r#"
670 //- /lib.rs 785 //- /lib.rs
671 mod bar<|> { 786 mod bar<|> { }
672 }
673 "#, 787 "#,
674 "bar MODULE FileId(1) [0; 11) [4; 7)", 788 "bar MODULE FileId(1) [0; 11) [4; 7)",
789 "mod bar { }|bar",
675 ); 790 );
676 } 791 }
677 792
@@ -689,8 +804,128 @@ mod tests {
689 fo<|>o(); 804 fo<|>o();
690 } 805 }
691 } 806 }
807 mod confuse_index { fn foo(); }
692 ", 808 ",
693 "foo FN_DEF FileId(1) [52; 63) [55; 58)", 809 "foo FN_DEF FileId(1) [52; 63) [55; 58)",
810 "fn foo() {}|foo",
694 ); 811 );
695 } 812 }
813
814 #[test]
815 fn goto_through_format() {
816 check_goto(
817 "
818 //- /lib.rs
819 #[macro_export]
820 macro_rules! format {
821 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
822 }
823 #[rustc_builtin_macro]
824 #[macro_export]
825 macro_rules! format_args {
826 ($fmt:expr) => ({ /* compiler built-in */ });
827 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
828 }
829 pub mod __export {
830 pub use crate::format_args;
831 fn foo() {} // for index confusion
832 }
833 fn foo() -> i8 {}
834 fn test() {
835 format!(\"{}\", fo<|>o())
836 }
837 ",
838 "foo FN_DEF FileId(1) [398; 415) [401; 404)",
839 "fn foo() -> i8 {}|foo",
840 );
841 }
842
843 #[test]
844 fn goto_for_type_param() {
845 check_goto(
846 "
847 //- /lib.rs
848 struct Foo<T> {
849 t: <|>T,
850 }
851 ",
852 "T TYPE_PARAM FileId(1) [11; 12)",
853 "T",
854 );
855 }
856
857 #[test]
858 fn goto_within_macro() {
859 check_goto(
860 "
861 //- /lib.rs
862 macro_rules! id {
863 ($($tt:tt)*) => ($($tt)*)
864 }
865
866 fn foo() {
867 let x = 1;
868 id!({
869 let y = <|>x;
870 let z = y;
871 });
872 }
873 ",
874 "x BIND_PAT FileId(1) [69; 70)",
875 "x",
876 );
877
878 check_goto(
879 "
880 //- /lib.rs
881 macro_rules! id {
882 ($($tt:tt)*) => ($($tt)*)
883 }
884
885 fn foo() {
886 let x = 1;
887 id!({
888 let y = x;
889 let z = <|>y;
890 });
891 }
892 ",
893 "y BIND_PAT FileId(1) [98; 99)",
894 "y",
895 );
896 }
897
898 #[test]
899 fn goto_def_in_local_fn() {
900 check_goto(
901 "
902 //- /lib.rs
903 fn main() {
904 fn foo() {
905 let x = 92;
906 <|>x;
907 }
908 }
909 ",
910 "x BIND_PAT FileId(1) [39; 40)",
911 "x",
912 );
913 }
914
915 #[test]
916 fn goto_def_for_field_init_shorthand() {
917 covers!(goto_def_for_field_init_shorthand);
918 check_goto(
919 "
920 //- /lib.rs
921 struct Foo { x: i32 }
922 fn main() {
923 let x = 92;
924 Foo { x<|> };
925 }
926 ",
927 "x RECORD_FIELD_DEF FileId(1) [13; 19) [13; 14)",
928 "x: i32|x",
929 )
930 }
696} 931}
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index 992a08809..ce8b6c72a 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -1,7 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::db::AstDatabase; 3use hir::db::AstDatabase;
4use ra_syntax::{ast, AstNode}; 4use ra_syntax::{ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
5 5
6use crate::{ 6use crate::{
7 db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget, 7 db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget,
@@ -13,7 +13,7 @@ pub(crate) fn goto_type_definition(
13 position: FilePosition, 13 position: FilePosition,
14) -> Option<RangeInfo<Vec<NavigationTarget>>> { 14) -> Option<RangeInfo<Vec<NavigationTarget>>> {
15 let file = db.parse_or_expand(position.file_id.into())?; 15 let file = db.parse_or_expand(position.file_id.into())?;
16 let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; 16 let token = pick_best(file.token_at_offset(position.offset))?;
17 let token = descend_into_macros(db, position.file_id, token); 17 let token = descend_into_macros(db, position.file_id, token);
18 18
19 let node = token.value.ancestors().find_map(|token| { 19 let node = token.value.ancestors().find_map(|token| {
@@ -41,6 +41,17 @@ pub(crate) fn goto_type_definition(
41 Some(RangeInfo::new(node.text_range(), vec![nav])) 41 Some(RangeInfo::new(node.text_range(), vec![nav]))
42} 42}
43 43
44fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
45 return tokens.max_by_key(priority);
46 fn priority(n: &SyntaxToken) -> usize {
47 match n.kind() {
48 IDENT | INT_NUMBER => 2,
49 kind if kind.is_trivia() => 0,
50 _ => 1,
51 }
52 }
53}
54
44#[cfg(test)] 55#[cfg(test)]
45mod tests { 56mod tests {
46 use crate::mock_analysis::analysis_and_position; 57 use crate::mock_analysis::analysis_and_position;
@@ -102,4 +113,32 @@ mod tests {
102 "Foo STRUCT_DEF FileId(1) [52; 65) [59; 62)", 113 "Foo STRUCT_DEF FileId(1) [52; 65) [59; 62)",
103 ); 114 );
104 } 115 }
116
117 #[test]
118 fn goto_type_definition_for_param() {
119 check_goto(
120 "
121 //- /lib.rs
122 struct Foo;
123 fn foo(<|>f: Foo) {}
124 ",
125 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
126 );
127 }
128
129 #[test]
130 fn goto_type_definition_for_tuple_field() {
131 check_goto(
132 "
133 //- /lib.rs
134 struct Foo;
135 struct Bar(Foo);
136 fn foo() {
137 let bar = Bar(Foo);
138 bar.<|>0;
139 }
140 ",
141 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
142 );
143 }
105} 144}
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 260a7b869..35e39f965 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -6,14 +6,13 @@ use ra_syntax::{
6 algo::find_covering_element, 6 algo::find_covering_element,
7 ast::{self, DocCommentsOwner}, 7 ast::{self, DocCommentsOwner},
8 match_ast, AstNode, 8 match_ast, AstNode,
9 SyntaxKind::*,
10 SyntaxToken, TokenAtOffset,
9}; 11};
10 12
11use crate::{ 13use crate::{
12 db::RootDatabase, 14 db::RootDatabase,
13 display::{ 15 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
14 description_from_symbol, docs_from_symbol, macro_label, rust_code_markup,
15 rust_code_markup_with_doc, ShortLabel,
16 },
17 expand::descend_into_macros, 16 expand::descend_into_macros,
18 references::{classify_name, classify_name_ref, NameKind, NameKind::*}, 17 references::{classify_name, classify_name_ref, NameKind, NameKind::*},
19 FilePosition, FileRange, RangeInfo, 18 FilePosition, FileRange, RangeInfo,
@@ -93,11 +92,7 @@ fn hover_text(docs: Option<String>, desc: Option<String>) -> Option<String> {
93 } 92 }
94} 93}
95 94
96fn hover_text_from_name_kind( 95fn hover_text_from_name_kind(db: &RootDatabase, name_kind: NameKind) -> Option<String> {
97 db: &RootDatabase,
98 name_kind: NameKind,
99 no_fallback: &mut bool,
100) -> Option<String> {
101 return match name_kind { 96 return match name_kind {
102 Macro(it) => { 97 Macro(it) => {
103 let src = it.source(db); 98 let src = it.source(db);
@@ -133,12 +128,8 @@ fn hover_text_from_name_kind(
133 hir::ModuleDef::TypeAlias(it) => from_def_source(db, it), 128 hir::ModuleDef::TypeAlias(it) => from_def_source(db, it),
134 hir::ModuleDef::BuiltinType(it) => Some(it.to_string()), 129 hir::ModuleDef::BuiltinType(it) => Some(it.to_string()),
135 }, 130 },
136 Local(_) => { 131 Local(_) => None,
137 // Hover for these shows type names 132 TypeParam(_) | SelfType(_) => {
138 *no_fallback = true;
139 None
140 }
141 GenericParam(_) | SelfType(_) => {
142 // FIXME: Hover for generic param 133 // FIXME: Hover for generic param
143 None 134 None
144 } 135 }
@@ -156,68 +147,55 @@ fn hover_text_from_name_kind(
156 147
157pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { 148pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
158 let file = db.parse_or_expand(position.file_id.into())?; 149 let file = db.parse_or_expand(position.file_id.into())?;
159 let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?; 150 let token = pick_best(file.token_at_offset(position.offset))?;
160 let token = descend_into_macros(db, position.file_id, token); 151 let token = descend_into_macros(db, position.file_id, token);
161 152
162 let mut res = HoverResult::new(); 153 let mut res = HoverResult::new();
163 154
164 let mut range = match_ast! { 155 if let Some((range, name_kind)) = match_ast! {
165 match (token.value.parent()) { 156 match (token.value.parent()) {
166 ast::NameRef(name_ref) => { 157 ast::NameRef(name_ref) => {
167 let mut no_fallback = false; 158 classify_name_ref(db, token.with_value(&name_ref)).map(|d| (name_ref.syntax().text_range(), d.kind))
168 if let Some(name_kind) =
169 classify_name_ref(db, token.with_value(&name_ref)).map(|d| d.kind)
170 {
171 res.extend(hover_text_from_name_kind(db, name_kind, &mut no_fallback))
172 }
173
174 if res.is_empty() && !no_fallback {
175 // Fallback index based approach:
176 let symbols = crate::symbol_index::index_resolve(db, &name_ref);
177 for sym in symbols {
178 let docs = docs_from_symbol(db, &sym);
179 let desc = description_from_symbol(db, &sym);
180 res.extend(hover_text(docs, desc));
181 }
182 }
183
184 if !res.is_empty() {
185 Some(name_ref.syntax().text_range())
186 } else {
187 None
188 }
189 }, 159 },
190 ast::Name(name) => { 160 ast::Name(name) => {
191 if let Some(name_kind) = classify_name(db, token.with_value(&name)).map(|d| d.kind) { 161 classify_name(db, token.with_value(&name)).map(|d| (name.syntax().text_range(), d.kind))
192 res.extend(hover_text_from_name_kind(db, name_kind, &mut true));
193 }
194
195 if !res.is_empty() {
196 Some(name.syntax().text_range())
197 } else {
198 None
199 }
200 }, 162 },
201 _ => None, 163 _ => None,
202 } 164 }
203 }; 165 } {
166 res.extend(hover_text_from_name_kind(db, name_kind));
204 167
205 if range.is_none() { 168 if !res.is_empty() {
206 let node = token.value.ancestors().find(|n| { 169 return Some(RangeInfo::new(range, res));
207 ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some() 170 }
208 })?; 171 }
209 let frange = FileRange { file_id: position.file_id, range: node.text_range() };
210 res.extend(type_of(db, frange).map(rust_code_markup));
211 range = Some(node.text_range());
212 };
213 172
214 let range = range?; 173 let node = token
174 .value
175 .ancestors()
176 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
177 let frange = FileRange { file_id: position.file_id, range: node.text_range() };
178 res.extend(type_of(db, frange).map(rust_code_markup));
215 if res.is_empty() { 179 if res.is_empty() {
216 return None; 180 return None;
217 } 181 }
182 let range = node.text_range();
183
218 Some(RangeInfo::new(range, res)) 184 Some(RangeInfo::new(range, res))
219} 185}
220 186
187fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
188 return tokens.max_by_key(priority);
189 fn priority(n: &SyntaxToken) -> usize {
190 match n.kind() {
191 IDENT | INT_NUMBER => 3,
192 L_PAREN | R_PAREN => 2,
193 kind if kind.is_trivia() => 0,
194 _ => 1,
195 }
196 }
197}
198
221pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> { 199pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
222 let parse = db.parse(frange.file_id); 200 let parse = db.parse(frange.file_id);
223 let leaf_node = find_covering_element(parse.tree().syntax(), frange.range); 201 let leaf_node = find_covering_element(parse.tree().syntax(), frange.range);
@@ -227,7 +205,7 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
227 .take_while(|it| it.text_range() == leaf_node.text_range()) 205 .take_while(|it| it.text_range() == leaf_node.text_range())
228 .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?; 206 .find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?;
229 let analyzer = 207 let analyzer =
230 hir::SourceAnalyzer::new(db, hir::Source::new(frange.file_id.into(), &node), None); 208 hir::SourceAnalyzer::new(db, hir::InFile::new(frange.file_id.into(), &node), None);
231 let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e)) 209 let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
232 { 210 {
233 ty 211 ty
@@ -236,7 +214,7 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
236 } else { 214 } else {
237 return None; 215 return None;
238 }; 216 };
239 Some(ty.display(db).to_string()) 217 Some(ty.display_truncated(db, None).to_string())
240} 218}
241 219
242#[cfg(test)] 220#[cfg(test)]
@@ -300,7 +278,7 @@ mod tests {
300 &["pub fn foo() -> u32"], 278 &["pub fn foo() -> u32"],
301 ); 279 );
302 280
303 // Multiple results 281 // Multiple candidates but results are ambiguous.
304 check_hover_result( 282 check_hover_result(
305 r#" 283 r#"
306 //- /a.rs 284 //- /a.rs
@@ -321,7 +299,7 @@ mod tests {
321 let foo_test = fo<|>o(); 299 let foo_test = fo<|>o();
322 } 300 }
323 "#, 301 "#,
324 &["pub fn foo() -> &str", "pub fn foo() -> u32", "pub fn foo(a: u32, b: u32)"], 302 &["{unknown}"],
325 ); 303 );
326 } 304 }
327 305
@@ -411,6 +389,23 @@ mod tests {
411 } 389 }
412 390
413 #[test] 391 #[test]
392 fn hover_omits_default_generic_types() {
393 check_hover_result(
394 r#"
395//- /main.rs
396struct Test<K, T = u8> {
397 k: K,
398 t: T,
399}
400
401fn main() {
402 let zz<|> = Test { t: 23, k: 33 };
403}"#,
404 &["Test<i32>"],
405 );
406 }
407
408 #[test]
414 fn hover_some() { 409 fn hover_some() {
415 let (analysis, position) = single_file_with_position( 410 let (analysis, position) = single_file_with_position(
416 " 411 "
@@ -505,6 +500,13 @@ fn func(foo: i32) { if true { <|>foo; }; }
505 } 500 }
506 501
507 #[test] 502 #[test]
503 fn hover_for_param_edge() {
504 let (analysis, position) = single_file_with_position("fn func(<|>foo: i32) {}");
505 let hover = analysis.hover(position).unwrap().unwrap();
506 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32"));
507 }
508
509 #[test]
508 fn test_type_of_for_function() { 510 fn test_type_of_for_function() {
509 let (analysis, range) = single_file_with_range( 511 let (analysis, range) = single_file_with_range(
510 " 512 "
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs
index aa480e399..9b165ee2a 100644
--- a/crates/ra_ide/src/impls.rs
+++ b/crates/ra_ide/src/impls.rs
@@ -16,7 +16,7 @@ pub(crate) fn goto_implementation(
16 let src = hir::ModuleSource::from_position(db, position); 16 let src = hir::ModuleSource::from_position(db, position);
17 let module = hir::Module::from_definition( 17 let module = hir::Module::from_definition(
18 db, 18 db,
19 hir::Source { file_id: position.file_id.into(), value: src }, 19 hir::InFile { file_id: position.file_id.into(), value: src },
20 )?; 20 )?;
21 21
22 if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { 22 if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) {
@@ -42,15 +42,15 @@ fn impls_for_def(
42) -> Option<Vec<NavigationTarget>> { 42) -> Option<Vec<NavigationTarget>> {
43 let ty = match node { 43 let ty = match node {
44 ast::NominalDef::StructDef(def) => { 44 ast::NominalDef::StructDef(def) => {
45 let src = hir::Source { file_id: position.file_id.into(), value: def.clone() }; 45 let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() };
46 hir::Struct::from_source(db, src)?.ty(db) 46 hir::Struct::from_source(db, src)?.ty(db)
47 } 47 }
48 ast::NominalDef::EnumDef(def) => { 48 ast::NominalDef::EnumDef(def) => {
49 let src = hir::Source { file_id: position.file_id.into(), value: def.clone() }; 49 let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() };
50 hir::Enum::from_source(db, src)?.ty(db) 50 hir::Enum::from_source(db, src)?.ty(db)
51 } 51 }
52 ast::NominalDef::UnionDef(def) => { 52 ast::NominalDef::UnionDef(def) => {
53 let src = hir::Source { file_id: position.file_id.into(), value: def.clone() }; 53 let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() };
54 hir::Union::from_source(db, src)?.ty(db) 54 hir::Union::from_source(db, src)?.ty(db)
55 } 55 }
56 }; 56 };
@@ -73,7 +73,7 @@ fn impls_for_trait(
73 node: &ast::TraitDef, 73 node: &ast::TraitDef,
74 module: hir::Module, 74 module: hir::Module,
75) -> Option<Vec<NavigationTarget>> { 75) -> Option<Vec<NavigationTarget>> {
76 let src = hir::Source { file_id: position.file_id.into(), value: node.clone() }; 76 let src = hir::InFile { file_id: position.file_id.into(), value: node.clone() };
77 let tr = hir::Trait::from_source(db, src)?; 77 let tr = hir::Trait::from_source(db, src)?;
78 78
79 let krate = module.krate(); 79 let krate = module.krate();
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 45149bf0c..c5e406977 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -1,12 +1,15 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use crate::{db::RootDatabase, FileId};
4use hir::{HirDisplay, SourceAnalyzer}; 3use hir::{HirDisplay, SourceAnalyzer};
4use once_cell::unsync::Lazy;
5use ra_prof::profile;
5use ra_syntax::{ 6use ra_syntax::{
6 ast::{self, AstNode, TypeAscriptionOwner}, 7 ast::{self, AstNode, TypeAscriptionOwner},
7 match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, 8 match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange,
8}; 9};
9 10
11use crate::{db::RootDatabase, FileId};
12
10#[derive(Debug, PartialEq, Eq)] 13#[derive(Debug, PartialEq, Eq)]
11pub enum InlayKind { 14pub enum InlayKind {
12 TypeHint, 15 TypeHint,
@@ -27,7 +30,7 @@ pub(crate) fn inlay_hints(
27) -> Vec<InlayHint> { 30) -> Vec<InlayHint> {
28 file.syntax() 31 file.syntax()
29 .descendants() 32 .descendants()
30 .map(|node| get_inlay_hints(db, file_id, &node, max_inlay_hint_length).unwrap_or_default()) 33 .flat_map(|node| get_inlay_hints(db, file_id, &node, max_inlay_hint_length))
31 .flatten() 34 .flatten()
32 .collect() 35 .collect()
33} 36}
@@ -38,7 +41,9 @@ fn get_inlay_hints(
38 node: &SyntaxNode, 41 node: &SyntaxNode,
39 max_inlay_hint_length: Option<usize>, 42 max_inlay_hint_length: Option<usize>,
40) -> Option<Vec<InlayHint>> { 43) -> Option<Vec<InlayHint>> {
41 let analyzer = SourceAnalyzer::new(db, hir::Source::new(file_id.into(), node), None); 44 let _p = profile("get_inlay_hints");
45 let analyzer =
46 Lazy::new(|| SourceAnalyzer::new(db, hir::InFile::new(file_id.into(), node), None));
42 match_ast! { 47 match_ast! {
43 match node { 48 match node {
44 ast::LetStmt(it) => { 49 ast::LetStmt(it) => {
@@ -122,18 +127,11 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> {
122 127
123 while let Some(maybe_leaf_pat) = pats_to_process.pop_front() { 128 while let Some(maybe_leaf_pat) = pats_to_process.pop_front() {
124 match &maybe_leaf_pat { 129 match &maybe_leaf_pat {
125 ast::Pat::BindPat(bind_pat) => { 130 ast::Pat::BindPat(bind_pat) => match bind_pat.pat() {
126 if let Some(pat) = bind_pat.pat() { 131 Some(pat) => pats_to_process.push_back(pat),
127 pats_to_process.push_back(pat); 132 _ => leaf_pats.push(maybe_leaf_pat),
128 } else { 133 },
129 leaf_pats.push(maybe_leaf_pat); 134 ast::Pat::TuplePat(tuple_pat) => pats_to_process.extend(tuple_pat.args()),
130 }
131 }
132 ast::Pat::TuplePat(tuple_pat) => {
133 for arg_pat in tuple_pat.args() {
134 pats_to_process.push_back(arg_pat);
135 }
136 }
137 ast::Pat::RecordPat(record_pat) => { 135 ast::Pat::RecordPat(record_pat) => {
138 if let Some(pat_list) = record_pat.record_field_pat_list() { 136 if let Some(pat_list) = record_pat.record_field_pat_list() {
139 pats_to_process.extend( 137 pats_to_process.extend(
@@ -151,10 +149,9 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> {
151 } 149 }
152 } 150 }
153 ast::Pat::TupleStructPat(tuple_struct_pat) => { 151 ast::Pat::TupleStructPat(tuple_struct_pat) => {
154 for arg_pat in tuple_struct_pat.args() { 152 pats_to_process.extend(tuple_struct_pat.args())
155 pats_to_process.push_back(arg_pat);
156 }
157 } 153 }
154 ast::Pat::RefPat(ref_pat) => pats_to_process.extend(ref_pat.pat()),
158 _ => (), 155 _ => (),
159 } 156 }
160 } 157 }
@@ -163,9 +160,36 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> {
163 160
164#[cfg(test)] 161#[cfg(test)]
165mod tests { 162mod tests {
166 use crate::mock_analysis::single_file;
167 use insta::assert_debug_snapshot; 163 use insta::assert_debug_snapshot;
168 164
165 use crate::mock_analysis::single_file;
166
167 #[test]
168 fn default_generic_types_should_not_be_displayed() {
169 let (analysis, file_id) = single_file(
170 r#"
171struct Test<K, T = u8> {
172 k: K,
173 t: T,
174}
175
176fn main() {
177 let zz = Test { t: 23, k: 33 };
178}"#,
179 );
180
181 assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###"
182 [
183 InlayHint {
184 range: [69; 71),
185 kind: TypeHint,
186 label: "Test<i32>",
187 },
188 ]
189 "###
190 );
191 }
192
169 #[test] 193 #[test]
170 fn let_statement() { 194 fn let_statement() {
171 let (analysis, file_id) = single_file( 195 let (analysis, file_id) = single_file(
@@ -202,6 +226,7 @@ fn main() {
202 226
203 let test = (42, 'a'); 227 let test = (42, 'a');
204 let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); 228 let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5));
229 let &x = &92;
205}"#, 230}"#,
206 ); 231 );
207 232
@@ -257,6 +282,11 @@ fn main() {
257 kind: TypeHint, 282 kind: TypeHint,
258 label: "f64", 283 label: "f64",
259 }, 284 },
285 InlayHint {
286 range: [627; 628),
287 kind: TypeHint,
288 label: "i32",
289 },
260 ] 290 ]
261 "### 291 "###
262 ); 292 );
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index d1bff4a76..779a81b2c 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -422,6 +422,11 @@ impl Analysis {
422 self.with_db(|db| parent_module::crate_for(db, file_id)) 422 self.with_db(|db| parent_module::crate_for(db, file_id))
423 } 423 }
424 424
425 /// Returns the edition of the given crate.
426 pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> {
427 self.with_db(|db| db.crate_graph().edition(crate_id))
428 }
429
425 /// Returns the root file of the given crate. 430 /// Returns the root file of the given crate.
426 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { 431 pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> {
427 self.with_db(|db| db.crate_graph().crate_root(crate_id)) 432 self.with_db(|db| db.crate_graph().crate_root(crate_id))
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs
index 848ae4dc7..077a44473 100644
--- a/crates/ra_ide/src/marks.rs
+++ b/crates/ra_ide/src/marks.rs
@@ -3,10 +3,11 @@
3test_utils::marks!( 3test_utils::marks!(
4 inserts_angle_brackets_for_generics 4 inserts_angle_brackets_for_generics
5 inserts_parens_for_function_calls 5 inserts_parens_for_function_calls
6 goto_definition_works_for_macros 6 goto_def_for_macros
7 goto_definition_works_for_methods 7 goto_def_for_methods
8 goto_definition_works_for_fields 8 goto_def_for_fields
9 goto_definition_works_for_record_fields 9 goto_def_for_record_fields
10 goto_def_for_field_init_shorthand
10 call_info_bad_offset 11 call_info_bad_offset
11 dont_complete_current_use 12 dont_complete_current_use
12 dont_complete_primitive_in_use 13 dont_complete_primitive_in_use
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs
index 6027e7d54..f5a788c07 100644
--- a/crates/ra_ide/src/parent_module.rs
+++ b/crates/ra_ide/src/parent_module.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_db::{CrateId, FileId, FilePosition}; 3use ra_db::{CrateId, FileId, FilePosition, SourceDatabase};
4 4
5use crate::{db::RootDatabase, NavigationTarget}; 5use crate::{db::RootDatabase, NavigationTarget};
6 6
@@ -10,7 +10,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
10 let src = hir::ModuleSource::from_position(db, position); 10 let src = hir::ModuleSource::from_position(db, position);
11 let module = match hir::Module::from_definition( 11 let module = match hir::Module::from_definition(
12 db, 12 db,
13 hir::Source { file_id: position.file_id.into(), value: src }, 13 hir::InFile { file_id: position.file_id.into(), value: src },
14 ) { 14 ) {
15 None => return Vec::new(), 15 None => return Vec::new(),
16 Some(it) => it, 16 Some(it) => it,
@@ -21,15 +21,16 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
21 21
22/// Returns `Vec` for the same reason as `parent_module` 22/// Returns `Vec` for the same reason as `parent_module`
23pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { 23pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
24 let src = hir::ModuleSource::from_file_id(db, file_id); 24 let source_file = db.parse(file_id).tree();
25 let src = hir::ModuleSource::SourceFile(source_file);
25 let module = 26 let module =
26 match hir::Module::from_definition(db, hir::Source { file_id: file_id.into(), value: src }) 27 match hir::Module::from_definition(db, hir::InFile { file_id: file_id.into(), value: src })
27 { 28 {
28 Some(it) => it, 29 Some(it) => it,
29 None => return Vec::new(), 30 None => return Vec::new(),
30 }; 31 };
31 let krate = module.krate(); 32 let krate = module.krate();
32 vec![krate.crate_id()] 33 vec![krate.into()]
33} 34}
34 35
35#[cfg(test)] 36#[cfg(test)]
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 21a1ea69e..e3ecde50d 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -14,7 +14,7 @@ mod name_definition;
14mod rename; 14mod rename;
15mod search_scope; 15mod search_scope;
16 16
17use hir::Source; 17use hir::InFile;
18use once_cell::unsync::Lazy; 18use once_cell::unsync::Lazy;
19use ra_db::{SourceDatabase, SourceDatabaseExt}; 19use ra_db::{SourceDatabase, SourceDatabaseExt};
20use ra_prof::profile; 20use ra_prof::profile;
@@ -85,7 +85,7 @@ pub(crate) fn find_all_refs(
85 NameKind::Def(def) => NavigationTarget::from_def(db, def)?, 85 NameKind::Def(def) => NavigationTarget::from_def(db, def)?,
86 NameKind::SelfType(imp) => imp.to_nav(db), 86 NameKind::SelfType(imp) => imp.to_nav(db),
87 NameKind::Local(local) => local.to_nav(db), 87 NameKind::Local(local) => local.to_nav(db),
88 NameKind::GenericParam(_) => return None, 88 NameKind::TypeParam(_) => return None,
89 }; 89 };
90 90
91 let search_scope = { 91 let search_scope = {
@@ -107,12 +107,12 @@ fn find_name<'a>(
107 position: FilePosition, 107 position: FilePosition,
108) -> Option<RangeInfo<(String, NameDefinition)>> { 108) -> Option<RangeInfo<(String, NameDefinition)>> {
109 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { 109 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) {
110 let def = classify_name(db, Source::new(position.file_id.into(), &name))?; 110 let def = classify_name(db, InFile::new(position.file_id.into(), &name))?;
111 let range = name.syntax().text_range(); 111 let range = name.syntax().text_range();
112 return Some(RangeInfo::new(range, (name.text().to_string(), def))); 112 return Some(RangeInfo::new(range, (name.text().to_string(), def)));
113 } 113 }
114 let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; 114 let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?;
115 let def = classify_name_ref(db, Source::new(position.file_id.into(), &name_ref))?; 115 let def = classify_name_ref(db, InFile::new(position.file_id.into(), &name_ref))?;
116 let range = name_ref.syntax().text_range(); 116 let range = name_ref.syntax().text_range();
117 Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) 117 Some(RangeInfo::new(range, (name_ref.text().to_string(), def)))
118} 118}
@@ -144,7 +144,7 @@ fn process_definition(
144 continue; 144 continue;
145 } 145 }
146 } 146 }
147 if let Some(d) = classify_name_ref(db, Source::new(file_id.into(), &name_ref)) { 147 if let Some(d) = classify_name_ref(db, InFile::new(file_id.into(), &name_ref)) {
148 if d == def { 148 if d == def {
149 refs.push(FileRange { file_id, range }); 149 refs.push(FileRange { file_id, range });
150 } 150 }
diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs
index 5cea805ec..3483a7176 100644
--- a/crates/ra_ide/src/references/classify.rs
+++ b/crates/ra_ide/src/references/classify.rs
@@ -1,6 +1,6 @@
1//! Functions that are used to classify an element from its definition or reference. 1//! Functions that are used to classify an element from its definition or reference.
2 2
3use hir::{FromSource, Module, ModuleSource, PathResolution, Source, SourceAnalyzer}; 3use hir::{FromSource, InFile, Module, ModuleSource, PathResolution, SourceAnalyzer};
4use ra_prof::profile; 4use ra_prof::profile;
5use ra_syntax::{ast, match_ast, AstNode}; 5use ra_syntax::{ast, match_ast, AstNode};
6use test_utils::tested_by; 6use test_utils::tested_by;
@@ -11,7 +11,7 @@ use super::{
11}; 11};
12use crate::db::RootDatabase; 12use crate::db::RootDatabase;
13 13
14pub(crate) fn classify_name(db: &RootDatabase, name: Source<&ast::Name>) -> Option<NameDefinition> { 14pub(crate) fn classify_name(db: &RootDatabase, name: InFile<&ast::Name>) -> Option<NameDefinition> {
15 let _p = profile("classify_name"); 15 let _p = profile("classify_name");
16 let parent = name.value.syntax().parent()?; 16 let parent = name.value.syntax().parent()?;
17 17
@@ -110,6 +110,15 @@ pub(crate) fn classify_name(db: &RootDatabase, name: Source<&ast::Name>) -> Opti
110 kind: NameKind::Macro(def), 110 kind: NameKind::Macro(def),
111 }) 111 })
112 }, 112 },
113 ast::TypeParam(it) => {
114 let src = name.with_value(it);
115 let def = hir::TypeParam::from_source(db, src)?;
116 Some(NameDefinition {
117 visibility: None,
118 container: def.module(db),
119 kind: NameKind::TypeParam(def),
120 })
121 },
113 _ => None, 122 _ => None,
114 } 123 }
115 } 124 }
@@ -117,7 +126,7 @@ pub(crate) fn classify_name(db: &RootDatabase, name: Source<&ast::Name>) -> Opti
117 126
118pub(crate) fn classify_name_ref( 127pub(crate) fn classify_name_ref(
119 db: &RootDatabase, 128 db: &RootDatabase,
120 name_ref: Source<&ast::NameRef>, 129 name_ref: InFile<&ast::NameRef>,
121) -> Option<NameDefinition> { 130) -> Option<NameDefinition> {
122 let _p = profile("classify_name_ref"); 131 let _p = profile("classify_name_ref");
123 132
@@ -125,21 +134,22 @@ pub(crate) fn classify_name_ref(
125 let analyzer = SourceAnalyzer::new(db, name_ref.map(|it| it.syntax()), None); 134 let analyzer = SourceAnalyzer::new(db, name_ref.map(|it| it.syntax()), None);
126 135
127 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { 136 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
128 tested_by!(goto_definition_works_for_methods); 137 tested_by!(goto_def_for_methods);
129 if let Some(func) = analyzer.resolve_method_call(&method_call) { 138 if let Some(func) = analyzer.resolve_method_call(&method_call) {
130 return Some(from_assoc_item(db, func.into())); 139 return Some(from_assoc_item(db, func.into()));
131 } 140 }
132 } 141 }
133 142
134 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 143 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
135 tested_by!(goto_definition_works_for_fields); 144 tested_by!(goto_def_for_fields);
136 if let Some(field) = analyzer.resolve_field(&field_expr) { 145 if let Some(field) = analyzer.resolve_field(&field_expr) {
137 return Some(from_struct_field(db, field)); 146 return Some(from_struct_field(db, field));
138 } 147 }
139 } 148 }
140 149
141 if let Some(record_field) = ast::RecordField::cast(parent.clone()) { 150 if let Some(record_field) = ast::RecordField::cast(parent.clone()) {
142 tested_by!(goto_definition_works_for_record_fields); 151 tested_by!(goto_def_for_record_fields);
152 tested_by!(goto_def_for_field_init_shorthand);
143 if let Some(field_def) = analyzer.resolve_record_field(&record_field) { 153 if let Some(field_def) = analyzer.resolve_record_field(&record_field) {
144 return Some(from_struct_field(db, field_def)); 154 return Some(from_struct_field(db, field_def));
145 } 155 }
@@ -151,7 +161,7 @@ pub(crate) fn classify_name_ref(
151 let visibility = None; 161 let visibility = None;
152 162
153 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { 163 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
154 tested_by!(goto_definition_works_for_macros); 164 tested_by!(goto_def_for_macros);
155 if let Some(macro_def) = analyzer.resolve_macro_call(db, name_ref.with_value(&macro_call)) { 165 if let Some(macro_def) = analyzer.resolve_macro_call(db, name_ref.with_value(&macro_call)) {
156 let kind = NameKind::Macro(macro_def); 166 let kind = NameKind::Macro(macro_def);
157 return Some(NameDefinition { kind, container, visibility }); 167 return Some(NameDefinition { kind, container, visibility });
@@ -168,9 +178,8 @@ pub(crate) fn classify_name_ref(
168 let kind = NameKind::Local(local); 178 let kind = NameKind::Local(local);
169 Some(NameDefinition { kind, container, visibility: None }) 179 Some(NameDefinition { kind, container, visibility: None })
170 } 180 }
171 PathResolution::GenericParam(par) => { 181 PathResolution::TypeParam(par) => {
172 // FIXME: get generic param def 182 let kind = NameKind::TypeParam(par);
173 let kind = NameKind::GenericParam(par);
174 Some(NameDefinition { kind, container, visibility }) 183 Some(NameDefinition { kind, container, visibility })
175 } 184 }
176 PathResolution::Macro(def) => { 185 PathResolution::Macro(def) => {
diff --git a/crates/ra_ide/src/references/name_definition.rs b/crates/ra_ide/src/references/name_definition.rs
index 10d3a2364..8c67c8863 100644
--- a/crates/ra_ide/src/references/name_definition.rs
+++ b/crates/ra_ide/src/references/name_definition.rs
@@ -4,8 +4,8 @@
4//! Note that the reference search is possible for not all of the classified items. 4//! Note that the reference search is possible for not all of the classified items.
5 5
6use hir::{ 6use hir::{
7 Adt, AssocItem, GenericParam, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, 7 Adt, AssocItem, HasSource, ImplBlock, Local, MacroDef, Module, ModuleDef, StructField,
8 StructField, VariantDef, 8 TypeParam, VariantDef,
9}; 9};
10use ra_syntax::{ast, ast::VisibilityOwner}; 10use ra_syntax::{ast, ast::VisibilityOwner};
11 11
@@ -19,7 +19,7 @@ pub enum NameKind {
19 Def(ModuleDef), 19 Def(ModuleDef),
20 SelfType(ImplBlock), 20 SelfType(ImplBlock),
21 Local(Local), 21 Local(Local),
22 GenericParam(GenericParam), 22 TypeParam(TypeParam),
23} 23}
24 24
25#[derive(PartialEq, Eq)] 25#[derive(PartialEq, Eq)]
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index d58496049..b804d5f6d 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -2,7 +2,7 @@
2 2
3use hir::ModuleSource; 3use hir::ModuleSource;
4use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; 4use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt};
5use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode}; 5use ra_syntax::{algo::find_node_at_offset, ast, tokenize, AstNode, SyntaxKind, SyntaxNode};
6use ra_text_edit::TextEdit; 6use ra_text_edit::TextEdit;
7 7
8use crate::{ 8use crate::{
@@ -17,6 +17,13 @@ pub(crate) fn rename(
17 position: FilePosition, 17 position: FilePosition,
18 new_name: &str, 18 new_name: &str,
19) -> Option<RangeInfo<SourceChange>> { 19) -> Option<RangeInfo<SourceChange>> {
20 let tokens = tokenize(new_name);
21 if tokens.len() != 1
22 || (tokens[0].kind != SyntaxKind::IDENT && tokens[0].kind != SyntaxKind::UNDERSCORE)
23 {
24 return None;
25 }
26
20 let parse = db.parse(position.file_id); 27 let parse = db.parse(position.file_id);
21 if let Some((ast_name, ast_module)) = 28 if let Some((ast_name, ast_module)) =
22 find_name_and_module_at_offset(parse.tree().syntax(), position) 29 find_name_and_module_at_offset(parse.tree().syntax(), position)
@@ -55,7 +62,7 @@ fn rename_mod(
55) -> Option<SourceChange> { 62) -> Option<SourceChange> {
56 let mut source_file_edits = Vec::new(); 63 let mut source_file_edits = Vec::new();
57 let mut file_system_edits = Vec::new(); 64 let mut file_system_edits = Vec::new();
58 let module_src = hir::Source { file_id: position.file_id.into(), value: ast_module.clone() }; 65 let module_src = hir::InFile { file_id: position.file_id.into(), value: ast_module.clone() };
59 if let Some(module) = hir::Module::from_declaration(db, module_src) { 66 if let Some(module) = hir::Module::from_declaration(db, module_src) {
60 let src = module.definition_source(db); 67 let src = module.definition_source(db);
61 let file_id = src.file_id.original_file(db); 68 let file_id = src.file_id.original_file(db);
@@ -124,6 +131,49 @@ mod tests {
124 }; 131 };
125 132
126 #[test] 133 #[test]
134 fn test_rename_to_underscore() {
135 test_rename(
136 r#"
137 fn main() {
138 let i<|> = 1;
139 }"#,
140 "_",
141 r#"
142 fn main() {
143 let _ = 1;
144 }"#,
145 );
146 }
147
148 #[test]
149 fn test_rename_to_raw_identifier() {
150 test_rename(
151 r#"
152 fn main() {
153 let i<|> = 1;
154 }"#,
155 "r#fn",
156 r#"
157 fn main() {
158 let r#fn = 1;
159 }"#,
160 );
161 }
162
163 #[test]
164 fn test_rename_to_invalid_identifier() {
165 let (analysis, position) = single_file_with_position(
166 "
167 fn main() {
168 let i<|> = 1;
169 }",
170 );
171 let new_name = "invalid!";
172 let source_change = analysis.rename(position, new_name).unwrap();
173 assert!(source_change.is_none());
174 }
175
176 #[test]
127 fn test_rename_for_local() { 177 fn test_rename_for_local() {
128 test_rename( 178 test_rename(
129 r#" 179 r#"
diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs
index f5c9589f4..241dd358f 100644
--- a/crates/ra_ide/src/references/search_scope.rs
+++ b/crates/ra_ide/src/references/search_scope.rs
@@ -5,7 +5,7 @@
5use std::mem; 5use std::mem;
6 6
7use hir::{DefWithBody, HasSource, ModuleSource}; 7use hir::{DefWithBody, HasSource, ModuleSource};
8use ra_db::{FileId, SourceDatabase, SourceDatabaseExt}; 8use ra_db::{FileId, SourceDatabaseExt};
9use ra_prof::profile; 9use ra_prof::profile;
10use ra_syntax::{AstNode, TextRange}; 10use ra_syntax::{AstNode, TextRange};
11use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
@@ -120,15 +120,11 @@ impl NameDefinition {
120 } 120 }
121 if vis.as_str() == "pub" { 121 if vis.as_str() == "pub" {
122 let krate = self.container.krate(); 122 let krate = self.container.krate();
123 let crate_graph = db.crate_graph(); 123 for rev_dep in krate.reverse_dependencies(db) {
124 for crate_id in crate_graph.iter() { 124 let root_file = rev_dep.root_file(db);
125 let mut crate_deps = crate_graph.dependencies(crate_id); 125 let source_root_id = db.file_source_root(root_file);
126 if crate_deps.any(|dep| dep.crate_id() == krate.crate_id()) { 126 let source_root = db.source_root(source_root_id);
127 let root_file = crate_graph.crate_root(crate_id); 127 res.extend(source_root.walk().map(|id| (id, None)));
128 let source_root_id = db.file_source_root(root_file);
129 let source_root = db.source_root(source_root_id);
130 res.extend(source_root.walk().map(|id| (id, None)));
131 }
132 } 128 }
133 return SearchScope::new(res); 129 return SearchScope::new(res);
134 } 130 }
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 8039a5164..e213e1a06 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::Source; 3use hir::InFile;
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_db::SourceDatabase; 5use ra_db::SourceDatabase;
6use ra_syntax::{ 6use ra_syntax::{
@@ -66,8 +66,8 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti
66 return None; 66 return None;
67 } 67 }
68 let range = module.syntax().text_range(); 68 let range = module.syntax().text_range();
69 let src = hir::ModuleSource::from_child_node(db, Source::new(file_id.into(), &module.syntax())); 69 let src = hir::ModuleSource::from_child_node(db, InFile::new(file_id.into(), &module.syntax()));
70 let module = hir::Module::from_definition(db, Source::new(file_id.into(), src))?; 70 let module = hir::Module::from_definition(db, InFile::new(file_id.into(), src))?;
71 71
72 let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); 72 let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::");
73 Some(Runnable { range, kind: RunnableKind::TestMod { path } }) 73 Some(Runnable { range, kind: RunnableKind::TestMod { path } })
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index b39c4d371..a097cf8e8 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -5,12 +5,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
5 5
6.comment { color: #7F9F7F; } 6.comment { color: #7F9F7F; }
7.string { color: #CC9393; } 7.string { color: #CC9393; }
8.field { color: #94BFF3; }
8.function { color: #93E0E3; } 9.function { color: #93E0E3; }
9.parameter { color: #94BFF3; } 10.parameter { color: #94BFF3; }
10.builtin { color: #DD6718; }
11.text { color: #DCDCCC; } 11.text { color: #DCDCCC; }
12.type { color: #7CB8BB; }
13.type\.builtin { color: #8CD0D3; }
14.type\.param { color: #20999D; }
12.attribute { color: #94BFF3; } 15.attribute { color: #94BFF3; }
13.literal { color: #BFEBBF; } 16.literal { color: #BFEBBF; }
17.literal\.numeric { color: #6A8759; }
14.macro { color: #94BFF3; } 18.macro { color: #94BFF3; }
15.variable { color: #DCDCCC; } 19.variable { color: #DCDCCC; }
16.variable\.mut { color: #DCDCCC; text-decoration: underline; } 20.variable\.mut { color: #DCDCCC; text-decoration: underline; }
@@ -21,28 +25,37 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
21</style> 25</style>
22<pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span> 26<pre><code><span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span>
23<span class="keyword">struct</span> <span class="type">Foo</span> { 27<span class="keyword">struct</span> <span class="type">Foo</span> {
24 <span class="keyword">pub</span> <span class="field">x</span>: <span class="type">i32</span>, 28 <span class="keyword">pub</span> <span class="field">x</span>: <span class="type.builtin">i32</span>,
25 <span class="keyword">pub</span> <span class="field">y</span>: <span class="type">i32</span>, 29 <span class="keyword">pub</span> <span class="field">y</span>: <span class="type.builtin">i32</span>,
26} 30}
27 31
28<span class="keyword">fn</span> <span class="function">foo</span>&lt;<span class="type">T</span>&gt;() -&gt; <span class="type">T</span> { 32<span class="keyword">fn</span> <span class="function">foo</span>&lt;<span class="type.param">T</span>&gt;() -&gt; <span class="type.param">T</span> {
29 <span class="macro">unimplemented</span><span class="macro">!</span>(); 33 <span class="macro">unimplemented</span><span class="macro">!</span>();
30 <span class="function">foo</span>::&lt;<span class="type">i32</span>&gt;(); 34 <span class="function">foo</span>::&lt;<span class="type.builtin">i32</span>&gt;();
31} 35}
32 36
33<span class="comment">// comment</span> 37<span class="comment">// comment</span>
34<span class="keyword">fn</span> <span class="function">main</span>() { 38<span class="keyword">fn</span> <span class="function">main</span>() {
35 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal">92</span>); 39 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>);
36 40
37 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>(); 41 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>();
38 <span class="keyword.control">if</span> <span class="keyword">true</span> { 42 <span class="keyword.control">if</span> <span class="keyword">true</span> {
39 <span class="variable.mut">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field">x</span>: <span class="literal">0</span>, <span class="field">y</span>: <span class="literal">1</span> }); 43 <span class="keyword">let</span> <span class="variable">x</span> = <span class="literal.numeric">92</span>;
44 <span class="variable.mut">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="literal.numeric">1</span> });
40 } 45 }
41 <span class="keyword.unsafe">unsafe</span> { <span class="variable.mut">vec</span>.<span class="text">set_len</span>(<span class="literal">0</span>); } 46 <span class="keyword.unsafe">unsafe</span> { <span class="variable.mut">vec</span>.<span class="text">set_len</span>(<span class="literal.numeric">0</span>); }
42 47
43 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">x</span> = <span class="literal">42</span>; 48 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">x</span> = <span class="literal.numeric">42</span>;
44 <span class="keyword">let</span> <span class="variable.mut">y</span> = &<span class="keyword">mut</span> <span class="variable.mut">x</span>; 49 <span class="keyword">let</span> <span class="variable.mut">y</span> = &<span class="keyword">mut</span> <span class="variable.mut">x</span>;
45 <span class="keyword">let</span> <span class="variable">z</span> = &<span class="variable.mut">y</span>; 50 <span class="keyword">let</span> <span class="variable">z</span> = &<span class="variable.mut">y</span>;
46 51
47 <span class="variable.mut">y</span>; 52 <span class="variable.mut">y</span>;
53}
54
55<span class="keyword">enum</span> <span class="type">E</span>&lt;<span class="type.param">X</span>&gt; {
56 <span class="constant">V</span>(<span class="type.param">X</span>)
57}
58
59<span class="keyword">impl</span>&lt;<span class="type.param">X</span>&gt; <span class="type">E</span>&lt;<span class="type.param">X</span>&gt; {
60 <span class="keyword">fn</span> <span class="function">new</span>&lt;<span class="type.param">T</span>&gt;() -&gt; <span class="type">E</span>&lt;<span class="type.param">T</span>&gt; {}
48}</code></pre> \ No newline at end of file 61}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 79f11ea80..110556c09 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -5,12 +5,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
5 5
6.comment { color: #7F9F7F; } 6.comment { color: #7F9F7F; }
7.string { color: #CC9393; } 7.string { color: #CC9393; }
8.field { color: #94BFF3; }
8.function { color: #93E0E3; } 9.function { color: #93E0E3; }
9.parameter { color: #94BFF3; } 10.parameter { color: #94BFF3; }
10.builtin { color: #DD6718; }
11.text { color: #DCDCCC; } 11.text { color: #DCDCCC; }
12.type { color: #7CB8BB; }
13.type\.builtin { color: #8CD0D3; }
14.type\.param { color: #20999D; }
12.attribute { color: #94BFF3; } 15.attribute { color: #94BFF3; }
13.literal { color: #BFEBBF; } 16.literal { color: #BFEBBF; }
17.literal\.numeric { color: #6A8759; }
14.macro { color: #94BFF3; } 18.macro { color: #94BFF3; }
15.variable { color: #DCDCCC; } 19.variable { color: #DCDCCC; }
16.variable\.mut { color: #DCDCCC; text-decoration: underline; } 20.variable\.mut { color: #DCDCCC; text-decoration: underline; }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 9a3e4c82f..0228ee7e9 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -2,7 +2,7 @@
2 2
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::{FxHashMap, FxHashSet};
4 4
5use hir::{Name, Source}; 5use hir::{InFile, Name};
6use ra_db::SourceDatabase; 6use ra_db::SourceDatabase;
7use ra_prof::profile; 7use ra_prof::profile;
8use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; 8use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T};
@@ -16,6 +16,34 @@ use crate::{
16 FileId, 16 FileId,
17}; 17};
18 18
19pub mod tags {
20 pub(crate) const FIELD: &str = "field";
21 pub(crate) const FUNCTION: &str = "function";
22 pub(crate) const MODULE: &str = "module";
23 pub(crate) const TYPE: &str = "type";
24 pub(crate) const CONSTANT: &str = "constant";
25 pub(crate) const MACRO: &str = "macro";
26 pub(crate) const VARIABLE: &str = "variable";
27 pub(crate) const VARIABLE_MUT: &str = "variable.mut";
28 pub(crate) const TEXT: &str = "text";
29
30 pub(crate) const TYPE_BUILTIN: &str = "type.builtin";
31 pub(crate) const TYPE_SELF: &str = "type.self";
32 pub(crate) const TYPE_PARAM: &str = "type.param";
33 pub(crate) const TYPE_LIFETIME: &str = "type.lifetime";
34
35 pub(crate) const LITERAL_BYTE: &str = "literal.byte";
36 pub(crate) const LITERAL_NUMERIC: &str = "literal.numeric";
37 pub(crate) const LITERAL_CHAR: &str = "literal.char";
38 pub(crate) const LITERAL_COMMENT: &str = "comment";
39 pub(crate) const LITERAL_STRING: &str = "string";
40 pub(crate) const LITERAL_ATTRIBUTE: &str = "attribute";
41
42 pub(crate) const KEYWORD_UNSAFE: &str = "keyword.unsafe";
43 pub(crate) const KEYWORD_CONTROL: &str = "keyword.control";
44 pub(crate) const KEYWORD: &str = "keyword";
45}
46
19#[derive(Debug)] 47#[derive(Debug)]
20pub struct HighlightedRange { 48pub struct HighlightedRange {
21 pub range: TextRange, 49 pub range: TextRange,
@@ -71,17 +99,16 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
71 bindings_shadow_count.clear(); 99 bindings_shadow_count.clear();
72 continue; 100 continue;
73 } 101 }
74 COMMENT => "comment", 102 COMMENT => tags::LITERAL_COMMENT,
75 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", 103 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING,
76 ATTR => "attribute", 104 ATTR => tags::LITERAL_ATTRIBUTE,
105 // Special-case field init shorthand
106 NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD,
107 NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => continue,
77 NAME_REF => { 108 NAME_REF => {
78 if node.ancestors().any(|it| it.kind() == ATTR) {
79 continue;
80 }
81
82 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); 109 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
83 let name_kind = 110 let name_kind =
84 classify_name_ref(db, Source::new(file_id.into(), &name_ref)).map(|d| d.kind); 111 classify_name_ref(db, InFile::new(file_id.into(), &name_ref)).map(|d| d.kind);
85 112
86 if let Some(Local(local)) = &name_kind { 113 if let Some(Local(local)) = &name_kind {
87 if let Some(name) = local.name(db) { 114 if let Some(name) = local.name(db) {
@@ -90,12 +117,12 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
90 } 117 }
91 }; 118 };
92 119
93 name_kind.map_or("text", |it| highlight_name(db, it)) 120 name_kind.map_or(tags::TEXT, |it| highlight_name(db, it))
94 } 121 }
95 NAME => { 122 NAME => {
96 let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); 123 let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
97 let name_kind = 124 let name_kind =
98 classify_name(db, Source::new(file_id.into(), &name)).map(|d| d.kind); 125 classify_name(db, InFile::new(file_id.into(), &name)).map(|d| d.kind);
99 126
100 if let Some(Local(local)) = &name_kind { 127 if let Some(Local(local)) = &name_kind {
101 if let Some(name) = local.name(db) { 128 if let Some(name) = local.name(db) {
@@ -107,18 +134,21 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
107 134
108 match name_kind { 135 match name_kind {
109 Some(name_kind) => highlight_name(db, name_kind), 136 Some(name_kind) => highlight_name(db, name_kind),
110 None => name.syntax().parent().map_or("function", |x| match x.kind() { 137 None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() {
111 TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => "type", 138 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE,
112 RECORD_FIELD_DEF => "field", 139 TYPE_PARAM => tags::TYPE_PARAM,
113 _ => "function", 140 RECORD_FIELD_DEF => tags::FIELD,
141 _ => tags::FUNCTION,
114 }), 142 }),
115 } 143 }
116 } 144 }
117 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", 145 INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC,
118 LIFETIME => "parameter", 146 BYTE => tags::LITERAL_BYTE,
119 T![unsafe] => "keyword.unsafe", 147 CHAR => tags::LITERAL_CHAR,
120 k if is_control_keyword(k) => "keyword.control", 148 LIFETIME => tags::TYPE_LIFETIME,
121 k if k.is_keyword() => "keyword", 149 T![unsafe] => tags::KEYWORD_UNSAFE,
150 k if is_control_keyword(k) => tags::KEYWORD_CONTROL,
151 k if k.is_keyword() => tags::KEYWORD,
122 _ => { 152 _ => {
123 if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) { 153 if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) {
124 if let Some(path) = macro_call.path() { 154 if let Some(path) = macro_call.path() {
@@ -135,7 +165,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
135 } 165 }
136 res.push(HighlightedRange { 166 res.push(HighlightedRange {
137 range: TextRange::from_to(range_start, range_end), 167 range: TextRange::from_to(range_start, range_end),
138 tag: "macro", 168 tag: tags::MACRO,
139 binding_hash: None, 169 binding_hash: None,
140 }) 170 })
141 } 171 }
@@ -211,29 +241,27 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
211 241
212fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str { 242fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str {
213 match name_kind { 243 match name_kind {
214 Macro(_) => "macro", 244 Macro(_) => tags::MACRO,
215 Field(_) => "field", 245 Field(_) => tags::FIELD,
216 AssocItem(hir::AssocItem::Function(_)) => "function", 246 AssocItem(hir::AssocItem::Function(_)) => tags::FUNCTION,
217 AssocItem(hir::AssocItem::Const(_)) => "constant", 247 AssocItem(hir::AssocItem::Const(_)) => tags::CONSTANT,
218 AssocItem(hir::AssocItem::TypeAlias(_)) => "type", 248 AssocItem(hir::AssocItem::TypeAlias(_)) => tags::TYPE,
219 Def(hir::ModuleDef::Module(_)) => "module", 249 Def(hir::ModuleDef::Module(_)) => tags::MODULE,
220 Def(hir::ModuleDef::Function(_)) => "function", 250 Def(hir::ModuleDef::Function(_)) => tags::FUNCTION,
221 Def(hir::ModuleDef::Adt(_)) => "type", 251 Def(hir::ModuleDef::Adt(_)) => tags::TYPE,
222 Def(hir::ModuleDef::EnumVariant(_)) => "constant", 252 Def(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT,
223 Def(hir::ModuleDef::Const(_)) => "constant", 253 Def(hir::ModuleDef::Const(_)) => tags::CONSTANT,
224 Def(hir::ModuleDef::Static(_)) => "constant", 254 Def(hir::ModuleDef::Static(_)) => tags::CONSTANT,
225 Def(hir::ModuleDef::Trait(_)) => "type", 255 Def(hir::ModuleDef::Trait(_)) => tags::TYPE,
226 Def(hir::ModuleDef::TypeAlias(_)) => "type", 256 Def(hir::ModuleDef::TypeAlias(_)) => tags::TYPE,
227 Def(hir::ModuleDef::BuiltinType(_)) => "type", 257 Def(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN,
228 SelfType(_) => "type", 258 SelfType(_) => tags::TYPE_SELF,
229 GenericParam(_) => "type", 259 TypeParam(_) => tags::TYPE_PARAM,
230 Local(local) => { 260 Local(local) => {
231 if local.is_mut(db) { 261 if local.is_mut(db) || local.ty(db).is_mutable_reference() {
232 "variable.mut" 262 tags::VARIABLE_MUT
233 } else if local.ty(db).is_mutable_reference() {
234 "variable.mut"
235 } else { 263 } else {
236 "variable" 264 tags::VARIABLE
237 } 265 }
238 } 266 }
239 } 267 }
@@ -251,12 +279,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
251 279
252.comment { color: #7F9F7F; } 280.comment { color: #7F9F7F; }
253.string { color: #CC9393; } 281.string { color: #CC9393; }
282.field { color: #94BFF3; }
254.function { color: #93E0E3; } 283.function { color: #93E0E3; }
255.parameter { color: #94BFF3; } 284.parameter { color: #94BFF3; }
256.builtin { color: #DD6718; }
257.text { color: #DCDCCC; } 285.text { color: #DCDCCC; }
286.type { color: #7CB8BB; }
287.type\\.builtin { color: #8CD0D3; }
288.type\\.param { color: #20999D; }
258.attribute { color: #94BFF3; } 289.attribute { color: #94BFF3; }
259.literal { color: #BFEBBF; } 290.literal { color: #BFEBBF; }
291.literal\\.numeric { color: #6A8759; }
260.macro { color: #94BFF3; } 292.macro { color: #94BFF3; }
261.variable { color: #DCDCCC; } 293.variable { color: #DCDCCC; }
262.variable\\.mut { color: #DCDCCC; text-decoration: underline; } 294.variable\\.mut { color: #DCDCCC; text-decoration: underline; }
@@ -293,7 +325,8 @@ fn main() {
293 325
294 let mut vec = Vec::new(); 326 let mut vec = Vec::new();
295 if true { 327 if true {
296 vec.push(Foo { x: 0, y: 1 }); 328 let x = 92;
329 vec.push(Foo { x, y: 1 });
297 } 330 }
298 unsafe { vec.set_len(0); } 331 unsafe { vec.set_len(0); }
299 332
@@ -303,6 +336,14 @@ fn main() {
303 336
304 y; 337 y;
305} 338}
339
340enum E<X> {
341 V(X)
342}
343
344impl<X> E<X> {
345 fn new<T>() -> E<T> {}
346}
306"# 347"#
307 .trim(), 348 .trim(),
308 ); 349 );