aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/annotations.rs16
-rw-r--r--crates/ide/src/diagnostics.rs163
-rw-r--r--crates/ide/src/diagnostics/unlinked_file.rs156
-rw-r--r--crates/ide/src/file_structure.rs228
-rw-r--r--crates/ide/src/goto_definition.rs11
-rw-r--r--crates/ide/src/join_lines.rs4
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/ide/src/references.rs26
-rw-r--r--crates/ide/src/references/rename.rs3
-rw-r--r--crates/ide/src/runnables.rs55
-rw-r--r--crates/ide/src/typing.rs3
11 files changed, 601 insertions, 68 deletions
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index 2e8e82b70..8e0a8fd8d 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -11,7 +11,7 @@ use crate::{
11 goto_implementation::goto_implementation, 11 goto_implementation::goto_implementation,
12 references::find_all_refs, 12 references::find_all_refs,
13 runnables::{runnables, Runnable}, 13 runnables::{runnables, Runnable},
14 NavigationTarget, RunnableKind, 14 NavigationTarget, RunnableKind, StructureNodeKind,
15}; 15};
16 16
17// Feature: Annotations 17// Feature: Annotations
@@ -80,15 +80,17 @@ pub(crate) fn annotations(
80 .filter(|node| { 80 .filter(|node| {
81 matches!( 81 matches!(
82 node.kind, 82 node.kind,
83 SymbolKind::Trait 83 StructureNodeKind::SymbolKind(SymbolKind::Trait)
84 | SymbolKind::Struct 84 | StructureNodeKind::SymbolKind(SymbolKind::Struct)
85 | SymbolKind::Enum 85 | StructureNodeKind::SymbolKind(SymbolKind::Enum)
86 | SymbolKind::Union 86 | StructureNodeKind::SymbolKind(SymbolKind::Union)
87 | SymbolKind::Const 87 | StructureNodeKind::SymbolKind(SymbolKind::Const)
88 ) 88 )
89 }) 89 })
90 .for_each(|node| { 90 .for_each(|node| {
91 if config.annotate_impls && node.kind != SymbolKind::Const { 91 if config.annotate_impls
92 && node.kind != StructureNodeKind::SymbolKind(SymbolKind::Const)
93 {
92 annotations.push(Annotation { 94 annotations.push(Annotation {
93 range: node.node_range, 95 range: node.node_range,
94 kind: AnnotationKind::HasImpls { 96 kind: AnnotationKind::HasImpls {
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index fe32f39b6..22697a537 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -6,6 +6,7 @@
6 6
7mod fixes; 7mod fixes;
8mod field_shorthand; 8mod field_shorthand;
9mod unlinked_file;
9 10
10use std::cell::RefCell; 11use std::cell::RefCell;
11 12
@@ -22,6 +23,7 @@ use syntax::{
22 SyntaxNode, SyntaxNodePtr, TextRange, 23 SyntaxNode, SyntaxNodePtr, TextRange,
23}; 24};
24use text_edit::TextEdit; 25use text_edit::TextEdit;
26use unlinked_file::UnlinkedFile;
25 27
26use crate::{FileId, Label, SourceChange}; 28use crate::{FileId, Label, SourceChange};
27 29
@@ -156,6 +158,18 @@ pub(crate) fn diagnostics(
156 .with_code(Some(d.code())), 158 .with_code(Some(d.code())),
157 ); 159 );
158 }) 160 })
161 .on::<UnlinkedFile, _>(|d| {
162 // Override severity and mark as unused.
163 res.borrow_mut().push(
164 Diagnostic::hint(
165 sema.diagnostics_display_range(d.display_source()).range,
166 d.message(),
167 )
168 .with_unused(true)
169 .with_fix(d.fix(&sema))
170 .with_code(Some(d.code())),
171 );
172 })
159 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { 173 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
160 // Use more accurate position if available. 174 // Use more accurate position if available.
161 let display_range = d 175 let display_range = d
@@ -197,9 +211,13 @@ pub(crate) fn diagnostics(
197 ); 211 );
198 }); 212 });
199 213
200 if let Some(m) = sema.to_module_def(file_id) { 214 match sema.to_module_def(file_id) {
201 m.diagnostics(db, &mut sink); 215 Some(m) => m.diagnostics(db, &mut sink),
202 }; 216 None => {
217 sink.push(UnlinkedFile { file_id, node: SyntaxNodePtr::new(&parse.tree().syntax()) });
218 }
219 }
220
203 drop(sink); 221 drop(sink);
204 res.into_inner() 222 res.into_inner()
205} 223}
@@ -307,6 +325,17 @@ mod tests {
307 ); 325 );
308 } 326 }
309 327
328 /// Checks that there's a diagnostic *without* fix at `$0`.
329 fn check_no_fix(ra_fixture: &str) {
330 let (analysis, file_position) = fixture::position(ra_fixture);
331 let diagnostic = analysis
332 .diagnostics(&DiagnosticsConfig::default(), file_position.file_id)
333 .unwrap()
334 .pop()
335 .unwrap();
336 assert!(diagnostic.fix.is_none(), "got a fix when none was expected: {:?}", diagnostic);
337 }
338
310 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 339 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
311 /// apply to the file containing the cursor. 340 /// apply to the file containing the cursor.
312 pub(crate) fn check_no_diagnostics(ra_fixture: &str) { 341 pub(crate) fn check_no_diagnostics(ra_fixture: &str) {
@@ -975,4 +1004,132 @@ impl TestStruct {
975 1004
976 check_fix(input, expected); 1005 check_fix(input, expected);
977 } 1006 }
1007
1008 #[test]
1009 fn unlinked_file_prepend_first_item() {
1010 cov_mark::check!(unlinked_file_prepend_before_first_item);
1011 check_fix(
1012 r#"
1013//- /main.rs
1014fn f() {}
1015//- /foo.rs
1016$0
1017"#,
1018 r#"
1019mod foo;
1020
1021fn f() {}
1022"#,
1023 );
1024 }
1025
1026 #[test]
1027 fn unlinked_file_append_mod() {
1028 cov_mark::check!(unlinked_file_append_to_existing_mods);
1029 check_fix(
1030 r#"
1031//- /main.rs
1032//! Comment on top
1033
1034mod preexisting;
1035
1036mod preexisting2;
1037
1038struct S;
1039
1040mod preexisting_bottom;)
1041//- /foo.rs
1042$0
1043"#,
1044 r#"
1045//! Comment on top
1046
1047mod preexisting;
1048
1049mod preexisting2;
1050mod foo;
1051
1052struct S;
1053
1054mod preexisting_bottom;)
1055"#,
1056 );
1057 }
1058
1059 #[test]
1060 fn unlinked_file_insert_in_empty_file() {
1061 cov_mark::check!(unlinked_file_empty_file);
1062 check_fix(
1063 r#"
1064//- /main.rs
1065//- /foo.rs
1066$0
1067"#,
1068 r#"
1069mod foo;
1070"#,
1071 );
1072 }
1073
1074 #[test]
1075 fn unlinked_file_old_style_modrs() {
1076 check_fix(
1077 r#"
1078//- /main.rs
1079mod submod;
1080//- /submod/mod.rs
1081// in mod.rs
1082//- /submod/foo.rs
1083$0
1084"#,
1085 r#"
1086// in mod.rs
1087mod foo;
1088"#,
1089 );
1090 }
1091
1092 #[test]
1093 fn unlinked_file_new_style_mod() {
1094 check_fix(
1095 r#"
1096//- /main.rs
1097mod submod;
1098//- /submod.rs
1099//- /submod/foo.rs
1100$0
1101"#,
1102 r#"
1103mod foo;
1104"#,
1105 );
1106 }
1107
1108 #[test]
1109 fn unlinked_file_with_cfg_off() {
1110 cov_mark::check!(unlinked_file_skip_fix_when_mod_already_exists);
1111 check_no_fix(
1112 r#"
1113//- /main.rs
1114#[cfg(never)]
1115mod foo;
1116
1117//- /foo.rs
1118$0
1119"#,
1120 );
1121 }
1122
1123 #[test]
1124 fn unlinked_file_with_cfg_on() {
1125 check_no_diagnostics(
1126 r#"
1127//- /main.rs
1128#[cfg(not(never))]
1129mod foo;
1130
1131//- /foo.rs
1132"#,
1133 );
1134 }
978} 1135}
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs
new file mode 100644
index 000000000..c5741bf6b
--- /dev/null
+++ b/crates/ide/src/diagnostics/unlinked_file.rs
@@ -0,0 +1,156 @@
1//! Diagnostic emitted for files that aren't part of any crate.
2
3use hir::{
4 db::DefDatabase,
5 diagnostics::{Diagnostic, DiagnosticCode},
6 InFile,
7};
8use ide_db::{
9 base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
10 source_change::SourceChange,
11 RootDatabase,
12};
13use syntax::{
14 ast::{self, ModuleItemOwner, NameOwner},
15 AstNode, SyntaxNodePtr,
16};
17use text_edit::TextEdit;
18
19use crate::Fix;
20
21use super::fixes::DiagnosticWithFix;
22
23#[derive(Debug)]
24pub(crate) struct UnlinkedFile {
25 pub(crate) file_id: FileId,
26 pub(crate) node: SyntaxNodePtr,
27}
28
29impl Diagnostic for UnlinkedFile {
30 fn code(&self) -> DiagnosticCode {
31 DiagnosticCode("unlinked-file")
32 }
33
34 fn message(&self) -> String {
35 "file not included in module tree".to_string()
36 }
37
38 fn display_source(&self) -> InFile<SyntaxNodePtr> {
39 InFile::new(self.file_id.into(), self.node.clone())
40 }
41
42 fn as_any(&self) -> &(dyn std::any::Any + Send + 'static) {
43 self
44 }
45}
46
47impl DiagnosticWithFix for UnlinkedFile {
48 fn fix(&self, sema: &hir::Semantics<RootDatabase>) -> Option<Fix> {
49 // If there's an existing module that could add a `mod` item to include the unlinked file,
50 // suggest that as a fix.
51
52 let source_root = sema.db.source_root(sema.db.file_source_root(self.file_id));
53 let our_path = source_root.path_for_file(&self.file_id)?;
54 let module_name = our_path.name_and_extension()?.0;
55
56 // Candidates to look for:
57 // - `mod.rs` in the same folder
58 // - we also check `main.rs` and `lib.rs`
59 // - `$dir.rs` in the parent folder, where `$dir` is the directory containing `self.file_id`
60 let parent = our_path.parent()?;
61 let mut paths =
62 vec![parent.join("mod.rs")?, parent.join("main.rs")?, parent.join("lib.rs")?];
63
64 // `submod/bla.rs` -> `submod.rs`
65 if let Some(newmod) = (|| {
66 let name = parent.name_and_extension()?.0;
67 parent.parent()?.join(&format!("{}.rs", name))
68 })() {
69 paths.push(newmod);
70 }
71
72 for path in &paths {
73 if let Some(parent_id) = source_root.file_for_path(path) {
74 for krate in sema.db.relevant_crates(*parent_id).iter() {
75 let crate_def_map = sema.db.crate_def_map(*krate);
76 for (_, module) in crate_def_map.modules() {
77 if module.origin.is_inline() {
78 // We don't handle inline `mod parent {}`s, they use different paths.
79 continue;
80 }
81
82 if module.origin.file_id() == Some(*parent_id) {
83 return make_fix(sema.db, *parent_id, module_name, self.file_id);
84 }
85 }
86 }
87 }
88 }
89
90 None
91 }
92}
93
94fn make_fix(
95 db: &RootDatabase,
96 parent_file_id: FileId,
97 new_mod_name: &str,
98 added_file_id: FileId,
99) -> Option<Fix> {
100 fn is_outline_mod(item: &ast::Item) -> bool {
101 matches!(item, ast::Item::Module(m) if m.item_list().is_none())
102 }
103
104 let mod_decl = format!("mod {};", new_mod_name);
105 let ast: ast::SourceFile = db.parse(parent_file_id).tree();
106
107 let mut builder = TextEdit::builder();
108
109 // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's
110 // probably `#[cfg]`d out).
111 for item in ast.items() {
112 if let ast::Item::Module(m) = item {
113 if let Some(name) = m.name() {
114 if m.item_list().is_none() && name.to_string() == new_mod_name {
115 cov_mark::hit!(unlinked_file_skip_fix_when_mod_already_exists);
116 return None;
117 }
118 }
119 }
120 }
121
122 // If there are existing `mod m;` items, append after them (after the first group of them, rather).
123 match ast
124 .items()
125 .skip_while(|item| !is_outline_mod(item))
126 .take_while(|item| is_outline_mod(item))
127 .last()
128 {
129 Some(last) => {
130 cov_mark::hit!(unlinked_file_append_to_existing_mods);
131 builder.insert(last.syntax().text_range().end(), format!("\n{}", mod_decl));
132 }
133 None => {
134 // Prepend before the first item in the file.
135 match ast.items().next() {
136 Some(item) => {
137 cov_mark::hit!(unlinked_file_prepend_before_first_item);
138 builder.insert(item.syntax().text_range().start(), format!("{}\n\n", mod_decl));
139 }
140 None => {
141 // No items in the file, so just append at the end.
142 cov_mark::hit!(unlinked_file_empty_file);
143 builder.insert(ast.syntax().text_range().end(), format!("{}\n", mod_decl));
144 }
145 }
146 }
147 }
148
149 let edit = builder.finish();
150 let trigger_range = db.parse(added_file_id).tree().syntax().text_range();
151 Some(Fix::new(
152 &format!("Insert `{}`", mod_decl),
153 SourceChange::from_text_edit(parent_file_id, edit),
154 trigger_range,
155 ))
156}
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs
index 26793bdb4..9f879a66e 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -1,7 +1,8 @@
1use ide_db::SymbolKind; 1use ide_db::SymbolKind;
2use syntax::{ 2use syntax::{
3 ast::{self, AttrsOwner, GenericParamsOwner, NameOwner}, 3 ast::{self, AttrsOwner, GenericParamsOwner, NameOwner},
4 match_ast, AstNode, SourceFile, SyntaxNode, TextRange, WalkEvent, 4 match_ast, AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, TextRange,
5 WalkEvent,
5}; 6};
6 7
7#[derive(Debug, Clone)] 8#[derive(Debug, Clone)]
@@ -10,11 +11,17 @@ pub struct StructureNode {
10 pub label: String, 11 pub label: String,
11 pub navigation_range: TextRange, 12 pub navigation_range: TextRange,
12 pub node_range: TextRange, 13 pub node_range: TextRange,
13 pub kind: SymbolKind, 14 pub kind: StructureNodeKind,
14 pub detail: Option<String>, 15 pub detail: Option<String>,
15 pub deprecated: bool, 16 pub deprecated: bool,
16} 17}
17 18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
20pub enum StructureNodeKind {
21 SymbolKind(SymbolKind),
22 Region,
23}
24
18// Feature: File Structure 25// Feature: File Structure
19// 26//
20// Provides a tree of the symbols defined in the file. Can be used to 27// Provides a tree of the symbols defined in the file. Can be used to
@@ -32,34 +39,46 @@ pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
32 let mut res = Vec::new(); 39 let mut res = Vec::new();
33 let mut stack = Vec::new(); 40 let mut stack = Vec::new();
34 41
35 for event in file.syntax().preorder() { 42 for event in file.syntax().preorder_with_tokens() {
36 match event { 43 match event {
37 WalkEvent::Enter(node) => { 44 WalkEvent::Enter(NodeOrToken::Node(node)) => {
38 if let Some(mut symbol) = structure_node(&node) { 45 if let Some(mut symbol) = structure_node(&node) {
39 symbol.parent = stack.last().copied(); 46 symbol.parent = stack.last().copied();
40 stack.push(res.len()); 47 stack.push(res.len());
41 res.push(symbol); 48 res.push(symbol);
42 } 49 }
43 } 50 }
44 WalkEvent::Leave(node) => { 51 WalkEvent::Leave(NodeOrToken::Node(node)) => {
45 if structure_node(&node).is_some() { 52 if structure_node(&node).is_some() {
46 stack.pop().unwrap(); 53 stack.pop().unwrap();
47 } 54 }
48 } 55 }
56 WalkEvent::Enter(NodeOrToken::Token(token)) => {
57 if let Some(mut symbol) = structure_token(token) {
58 symbol.parent = stack.last().copied();
59 stack.push(res.len());
60 res.push(symbol);
61 }
62 }
63 WalkEvent::Leave(NodeOrToken::Token(token)) => {
64 if structure_token(token).is_some() {
65 stack.pop().unwrap();
66 }
67 }
49 } 68 }
50 } 69 }
51 res 70 res
52} 71}
53 72
54fn structure_node(node: &SyntaxNode) -> Option<StructureNode> { 73fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
55 fn decl<N: NameOwner + AttrsOwner>(node: N, kind: SymbolKind) -> Option<StructureNode> { 74 fn decl<N: NameOwner + AttrsOwner>(node: N, kind: StructureNodeKind) -> Option<StructureNode> {
56 decl_with_detail(&node, None, kind) 75 decl_with_detail(&node, None, kind)
57 } 76 }
58 77
59 fn decl_with_type_ref<N: NameOwner + AttrsOwner>( 78 fn decl_with_type_ref<N: NameOwner + AttrsOwner>(
60 node: &N, 79 node: &N,
61 type_ref: Option<ast::Type>, 80 type_ref: Option<ast::Type>,
62 kind: SymbolKind, 81 kind: StructureNodeKind,
63 ) -> Option<StructureNode> { 82 ) -> Option<StructureNode> {
64 let detail = type_ref.map(|type_ref| { 83 let detail = type_ref.map(|type_ref| {
65 let mut detail = String::new(); 84 let mut detail = String::new();
@@ -72,7 +91,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
72 fn decl_with_detail<N: NameOwner + AttrsOwner>( 91 fn decl_with_detail<N: NameOwner + AttrsOwner>(
73 node: &N, 92 node: &N,
74 detail: Option<String>, 93 detail: Option<String>,
75 kind: SymbolKind, 94 kind: StructureNodeKind,
76 ) -> Option<StructureNode> { 95 ) -> Option<StructureNode> {
77 let name = node.name()?; 96 let name = node.name()?;
78 97
@@ -120,18 +139,18 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
120 collapse_ws(ret_type.syntax(), &mut detail); 139 collapse_ws(ret_type.syntax(), &mut detail);
121 } 140 }
122 141
123 decl_with_detail(&it, Some(detail), SymbolKind::Function) 142 decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(SymbolKind::Function))
124 }, 143 },
125 ast::Struct(it) => decl(it, SymbolKind::Struct), 144 ast::Struct(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Struct)),
126 ast::Union(it) => decl(it, SymbolKind::Union), 145 ast::Union(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Union)),
127 ast::Enum(it) => decl(it, SymbolKind::Enum), 146 ast::Enum(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)),
128 ast::Variant(it) => decl(it, SymbolKind::Variant), 147 ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)),
129 ast::Trait(it) => decl(it, SymbolKind::Trait), 148 ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)),
130 ast::Module(it) => decl(it, SymbolKind::Module), 149 ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)),
131 ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), SymbolKind::TypeAlias), 150 ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)),
132 ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), SymbolKind::Field), 151 ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Field)),
133 ast::Const(it) => decl_with_type_ref(&it, it.ty(), SymbolKind::Const), 152 ast::Const(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Const)),
134 ast::Static(it) => decl_with_type_ref(&it, it.ty(), SymbolKind::Static), 153 ast::Static(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Static)),
135 ast::Impl(it) => { 154 ast::Impl(it) => {
136 let target_type = it.self_ty()?; 155 let target_type = it.self_ty()?;
137 let target_trait = it.trait_(); 156 let target_trait = it.trait_();
@@ -147,18 +166,38 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
147 label, 166 label,
148 navigation_range: target_type.syntax().text_range(), 167 navigation_range: target_type.syntax().text_range(),
149 node_range: it.syntax().text_range(), 168 node_range: it.syntax().text_range(),
150 kind: SymbolKind::Impl, 169 kind: StructureNodeKind::SymbolKind(SymbolKind::Impl),
151 detail: None, 170 detail: None,
152 deprecated: false, 171 deprecated: false,
153 }; 172 };
154 Some(node) 173 Some(node)
155 }, 174 },
156 ast::MacroRules(it) => decl(it, SymbolKind::Macro), 175 ast::MacroRules(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)),
157 _ => None, 176 _ => None,
158 } 177 }
159 } 178 }
160} 179}
161 180
181fn structure_token(token: SyntaxToken) -> Option<StructureNode> {
182 if let Some(comment) = ast::Comment::cast(token) {
183 let text = comment.text().trim();
184
185 if let Some(region_name) = text.strip_prefix("// region:").map(str::trim) {
186 return Some(StructureNode {
187 parent: None,
188 label: region_name.to_string(),
189 navigation_range: comment.syntax().text_range(),
190 node_range: comment.syntax().text_range(),
191 kind: StructureNodeKind::Region,
192 detail: None,
193 deprecated: false,
194 });
195 }
196 }
197
198 None
199}
200
162#[cfg(test)] 201#[cfg(test)]
163mod tests { 202mod tests {
164 use expect_test::{expect, Expect}; 203 use expect_test::{expect, Expect};
@@ -217,6 +256,16 @@ fn obsolete() {}
217 256
218#[deprecated(note = "for awhile")] 257#[deprecated(note = "for awhile")]
219fn very_obsolete() {} 258fn very_obsolete() {}
259
260// region: Some region name
261// endregion
262
263// region: dontpanic
264mod m {
265fn f() {}
266// endregion
267fn g() {}
268}
220"#, 269"#,
221 expect![[r#" 270 expect![[r#"
222 [ 271 [
@@ -225,7 +274,9 @@ fn very_obsolete() {}
225 label: "Foo", 274 label: "Foo",
226 navigation_range: 8..11, 275 navigation_range: 8..11,
227 node_range: 1..26, 276 node_range: 1..26,
228 kind: Struct, 277 kind: SymbolKind(
278 Struct,
279 ),
229 detail: None, 280 detail: None,
230 deprecated: false, 281 deprecated: false,
231 }, 282 },
@@ -236,7 +287,9 @@ fn very_obsolete() {}
236 label: "x", 287 label: "x",
237 navigation_range: 18..19, 288 navigation_range: 18..19,
238 node_range: 18..24, 289 node_range: 18..24,
239 kind: Field, 290 kind: SymbolKind(
291 Field,
292 ),
240 detail: Some( 293 detail: Some(
241 "i32", 294 "i32",
242 ), 295 ),
@@ -247,7 +300,9 @@ fn very_obsolete() {}
247 label: "m", 300 label: "m",
248 navigation_range: 32..33, 301 navigation_range: 32..33,
249 node_range: 28..158, 302 node_range: 28..158,
250 kind: Module, 303 kind: SymbolKind(
304 Module,
305 ),
251 detail: None, 306 detail: None,
252 deprecated: false, 307 deprecated: false,
253 }, 308 },
@@ -258,7 +313,9 @@ fn very_obsolete() {}
258 label: "bar1", 313 label: "bar1",
259 navigation_range: 43..47, 314 navigation_range: 43..47,
260 node_range: 40..52, 315 node_range: 40..52,
261 kind: Function, 316 kind: SymbolKind(
317 Function,
318 ),
262 detail: Some( 319 detail: Some(
263 "fn()", 320 "fn()",
264 ), 321 ),
@@ -271,7 +328,9 @@ fn very_obsolete() {}
271 label: "bar2", 328 label: "bar2",
272 navigation_range: 60..64, 329 navigation_range: 60..64,
273 node_range: 57..81, 330 node_range: 57..81,
274 kind: Function, 331 kind: SymbolKind(
332 Function,
333 ),
275 detail: Some( 334 detail: Some(
276 "fn<T>(t: T) -> T", 335 "fn<T>(t: T) -> T",
277 ), 336 ),
@@ -284,7 +343,9 @@ fn very_obsolete() {}
284 label: "bar3", 343 label: "bar3",
285 navigation_range: 89..93, 344 navigation_range: 89..93,
286 node_range: 86..156, 345 node_range: 86..156,
287 kind: Function, 346 kind: SymbolKind(
347 Function,
348 ),
288 detail: Some( 349 detail: Some(
289 "fn<A, B>(a: A, b: B) -> Vec< u32 >", 350 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
290 ), 351 ),
@@ -295,7 +356,9 @@ fn very_obsolete() {}
295 label: "E", 356 label: "E",
296 navigation_range: 165..166, 357 navigation_range: 165..166,
297 node_range: 160..180, 358 node_range: 160..180,
298 kind: Enum, 359 kind: SymbolKind(
360 Enum,
361 ),
299 detail: None, 362 detail: None,
300 deprecated: false, 363 deprecated: false,
301 }, 364 },
@@ -306,7 +369,9 @@ fn very_obsolete() {}
306 label: "X", 369 label: "X",
307 navigation_range: 169..170, 370 navigation_range: 169..170,
308 node_range: 169..170, 371 node_range: 169..170,
309 kind: Variant, 372 kind: SymbolKind(
373 Variant,
374 ),
310 detail: None, 375 detail: None,
311 deprecated: false, 376 deprecated: false,
312 }, 377 },
@@ -317,7 +382,9 @@ fn very_obsolete() {}
317 label: "Y", 382 label: "Y",
318 navigation_range: 172..173, 383 navigation_range: 172..173,
319 node_range: 172..178, 384 node_range: 172..178,
320 kind: Variant, 385 kind: SymbolKind(
386 Variant,
387 ),
321 detail: None, 388 detail: None,
322 deprecated: false, 389 deprecated: false,
323 }, 390 },
@@ -326,7 +393,9 @@ fn very_obsolete() {}
326 label: "T", 393 label: "T",
327 navigation_range: 186..187, 394 navigation_range: 186..187,
328 node_range: 181..193, 395 node_range: 181..193,
329 kind: TypeAlias, 396 kind: SymbolKind(
397 TypeAlias,
398 ),
330 detail: Some( 399 detail: Some(
331 "()", 400 "()",
332 ), 401 ),
@@ -337,7 +406,9 @@ fn very_obsolete() {}
337 label: "S", 406 label: "S",
338 navigation_range: 201..202, 407 navigation_range: 201..202,
339 node_range: 194..213, 408 node_range: 194..213,
340 kind: Static, 409 kind: SymbolKind(
410 Static,
411 ),
341 detail: Some( 412 detail: Some(
342 "i32", 413 "i32",
343 ), 414 ),
@@ -348,7 +419,9 @@ fn very_obsolete() {}
348 label: "C", 419 label: "C",
349 navigation_range: 220..221, 420 navigation_range: 220..221,
350 node_range: 214..232, 421 node_range: 214..232,
351 kind: Const, 422 kind: SymbolKind(
423 Const,
424 ),
352 detail: Some( 425 detail: Some(
353 "i32", 426 "i32",
354 ), 427 ),
@@ -359,7 +432,9 @@ fn very_obsolete() {}
359 label: "impl E", 432 label: "impl E",
360 navigation_range: 239..240, 433 navigation_range: 239..240,
361 node_range: 234..243, 434 node_range: 234..243,
362 kind: Impl, 435 kind: SymbolKind(
436 Impl,
437 ),
363 detail: None, 438 detail: None,
364 deprecated: false, 439 deprecated: false,
365 }, 440 },
@@ -368,7 +443,9 @@ fn very_obsolete() {}
368 label: "impl fmt::Debug for E", 443 label: "impl fmt::Debug for E",
369 navigation_range: 265..266, 444 navigation_range: 265..266,
370 node_range: 245..269, 445 node_range: 245..269,
371 kind: Impl, 446 kind: SymbolKind(
447 Impl,
448 ),
372 detail: None, 449 detail: None,
373 deprecated: false, 450 deprecated: false,
374 }, 451 },
@@ -377,7 +454,9 @@ fn very_obsolete() {}
377 label: "mc", 454 label: "mc",
378 navigation_range: 284..286, 455 navigation_range: 284..286,
379 node_range: 271..303, 456 node_range: 271..303,
380 kind: Macro, 457 kind: SymbolKind(
458 Macro,
459 ),
381 detail: None, 460 detail: None,
382 deprecated: false, 461 deprecated: false,
383 }, 462 },
@@ -386,7 +465,9 @@ fn very_obsolete() {}
386 label: "mcexp", 465 label: "mcexp",
387 navigation_range: 334..339, 466 navigation_range: 334..339,
388 node_range: 305..356, 467 node_range: 305..356,
389 kind: Macro, 468 kind: SymbolKind(
469 Macro,
470 ),
390 detail: None, 471 detail: None,
391 deprecated: false, 472 deprecated: false,
392 }, 473 },
@@ -395,7 +476,9 @@ fn very_obsolete() {}
395 label: "mcexp", 476 label: "mcexp",
396 navigation_range: 387..392, 477 navigation_range: 387..392,
397 node_range: 358..409, 478 node_range: 358..409,
398 kind: Macro, 479 kind: SymbolKind(
480 Macro,
481 ),
399 detail: None, 482 detail: None,
400 deprecated: false, 483 deprecated: false,
401 }, 484 },
@@ -404,7 +487,9 @@ fn very_obsolete() {}
404 label: "obsolete", 487 label: "obsolete",
405 navigation_range: 428..436, 488 navigation_range: 428..436,
406 node_range: 411..441, 489 node_range: 411..441,
407 kind: Function, 490 kind: SymbolKind(
491 Function,
492 ),
408 detail: Some( 493 detail: Some(
409 "fn()", 494 "fn()",
410 ), 495 ),
@@ -415,12 +500,75 @@ fn very_obsolete() {}
415 label: "very_obsolete", 500 label: "very_obsolete",
416 navigation_range: 481..494, 501 navigation_range: 481..494,
417 node_range: 443..499, 502 node_range: 443..499,
418 kind: Function, 503 kind: SymbolKind(
504 Function,
505 ),
419 detail: Some( 506 detail: Some(
420 "fn()", 507 "fn()",
421 ), 508 ),
422 deprecated: true, 509 deprecated: true,
423 }, 510 },
511 StructureNode {
512 parent: None,
513 label: "Some region name",
514 navigation_range: 501..528,
515 node_range: 501..528,
516 kind: Region,
517 detail: None,
518 deprecated: false,
519 },
520 StructureNode {
521 parent: None,
522 label: "m",
523 navigation_range: 568..569,
524 node_range: 543..606,
525 kind: SymbolKind(
526 Module,
527 ),
528 detail: None,
529 deprecated: false,
530 },
531 StructureNode {
532 parent: Some(
533 20,
534 ),
535 label: "dontpanic",
536 navigation_range: 543..563,
537 node_range: 543..563,
538 kind: Region,
539 detail: None,
540 deprecated: false,
541 },
542 StructureNode {
543 parent: Some(
544 20,
545 ),
546 label: "f",
547 navigation_range: 575..576,
548 node_range: 572..581,
549 kind: SymbolKind(
550 Function,
551 ),
552 detail: Some(
553 "fn()",
554 ),
555 deprecated: false,
556 },
557 StructureNode {
558 parent: Some(
559 20,
560 ),
561 label: "g",
562 navigation_range: 598..599,
563 node_range: 582..604,
564 kind: SymbolKind(
565 Function,
566 ),
567 detail: Some(
568 "fn()",
569 ),
570 deprecated: false,
571 },
424 ] 572 ]
425 "#]], 573 "#]],
426 ); 574 );
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index abed1969e..e8f31e4b1 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1161,4 +1161,15 @@ struct S;
1161"#, 1161"#,
1162 ) 1162 )
1163 } 1163 }
1164
1165 #[test]
1166 fn goto_incomplete_field() {
1167 check(
1168 r#"
1169struct A { a: u32 }
1170 //^
1171fn foo() { A { a$0: }; }
1172"#,
1173 )
1174 }
1164} 1175}
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 20a920ddb..d571ed559 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -218,7 +218,7 @@ mod tests {
218 let result = join_lines(&file, range); 218 let result = join_lines(&file, range);
219 219
220 let actual = { 220 let actual = {
221 let mut actual = before.to_string(); 221 let mut actual = before;
222 result.apply(&mut actual); 222 result.apply(&mut actual);
223 actual 223 actual
224 }; 224 };
@@ -622,7 +622,7 @@ fn foo() {
622 let parse = SourceFile::parse(&before); 622 let parse = SourceFile::parse(&before);
623 let result = join_lines(&parse.tree(), sel); 623 let result = join_lines(&parse.tree(), sel);
624 let actual = { 624 let actual = {
625 let mut actual = before.to_string(); 625 let mut actual = before;
626 result.apply(&mut actual); 626 result.apply(&mut actual);
627 actual 627 actual
628 }; 628 };
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index a8b169e87..662da5a96 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -71,7 +71,7 @@ pub use crate::{
71 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, 71 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity},
72 display::navigation_target::NavigationTarget, 72 display::navigation_target::NavigationTarget,
73 expand_macro::ExpandedMacro, 73 expand_macro::ExpandedMacro,
74 file_structure::StructureNode, 74 file_structure::{StructureNode, StructureNodeKind},
75 folding_ranges::{Fold, FoldKind}, 75 folding_ranges::{Fold, FoldKind},
76 hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult}, 76 hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult},
77 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, 77 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
@@ -101,7 +101,7 @@ pub use ide_db::{
101 search::{ReferenceAccess, SearchScope}, 101 search::{ReferenceAccess, SearchScope},
102 source_change::{FileSystemEdit, SourceChange}, 102 source_change::{FileSystemEdit, SourceChange},
103 symbol_index::Query, 103 symbol_index::Query,
104 RootDatabase, 104 RootDatabase, SymbolKind,
105}; 105};
106pub use ide_ssr::SsrError; 106pub use ide_ssr::SsrError;
107pub use syntax::{TextRange, TextSize}; 107pub use syntax::{TextRange, TextSize};
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index fef70533d..ec7c7686d 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -29,7 +29,7 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget};
29 29
30#[derive(Debug, Clone)] 30#[derive(Debug, Clone)]
31pub struct ReferenceSearchResult { 31pub struct ReferenceSearchResult {
32 pub declaration: Declaration, 32 pub declaration: Option<Declaration>,
33 pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceAccess>)>>, 33 pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceAccess>)>>,
34} 34}
35 35
@@ -91,10 +91,10 @@ pub(crate) fn find_all_refs(
91 _ => {} 91 _ => {}
92 } 92 }
93 } 93 }
94 let nav = def.try_to_nav(sema.db)?; 94 let declaration = def.try_to_nav(sema.db).map(|nav| {
95 let decl_range = nav.focus_or_full_range(); 95 let decl_range = nav.focus_or_full_range();
96 96 Declaration { nav, access: decl_access(&def, &syntax, decl_range) }
97 let declaration = Declaration { nav, access: decl_access(&def, &syntax, decl_range) }; 97 });
98 let references = usages 98 let references = usages
99 .into_iter() 99 .into_iter()
100 .map(|(file_id, refs)| { 100 .map(|(file_id, refs)| {
@@ -1004,8 +1004,7 @@ impl Foo {
1004 let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap(); 1004 let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap();
1005 1005
1006 let mut actual = String::new(); 1006 let mut actual = String::new();
1007 { 1007 if let Some(decl) = refs.declaration {
1008 let decl = refs.declaration;
1009 format_to!(actual, "{}", decl.nav.debug_render()); 1008 format_to!(actual, "{}", decl.nav.debug_render());
1010 if let Some(access) = decl.access { 1009 if let Some(access) = decl.access {
1011 format_to!(actual, " {:?}", access) 1010 format_to!(actual, " {:?}", access)
@@ -1258,4 +1257,17 @@ fn main() {
1258 "#]], 1257 "#]],
1259 ); 1258 );
1260 } 1259 }
1260
1261 #[test]
1262 fn test_primitives() {
1263 check(
1264 r#"
1265fn foo(_: bool) -> bo$0ol { true }
1266"#,
1267 expect![[r#"
1268 FileId(0) 10..14
1269 FileId(0) 19..23
1270 "#]],
1271 );
1272 }
1261} 1273}
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 1e378279d..5340b638a 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -510,7 +510,8 @@ fn source_edit_from_def(
510 def: Definition, 510 def: Definition,
511 new_name: &str, 511 new_name: &str,
512) -> RenameResult<(FileId, TextEdit)> { 512) -> RenameResult<(FileId, TextEdit)> {
513 let nav = def.try_to_nav(sema.db).unwrap(); 513 let nav =
514 def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?;
514 515
515 let mut replacement_text = String::new(); 516 let mut replacement_text = String::new();
516 let mut repl_range = nav.focus_or_full_range(); 517 let mut repl_range = nav.focus_or_full_range();
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index faa91541e..27d35de5b 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -2,7 +2,7 @@ use std::fmt;
2 2
3use ast::NameOwner; 3use ast::NameOwner;
4use cfg::CfgExpr; 4use cfg::CfgExpr;
5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; 5use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
6use ide_assists::utils::test_related_attribute; 6use ide_assists::utils::test_related_attribute;
7use ide_db::{ 7use ide_db::{
8 base_db::{FilePosition, FileRange}, 8 base_db::{FilePosition, FileRange},
@@ -340,11 +340,21 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op
340 // FIXME: this also looks very wrong 340 // FIXME: this also looks very wrong
341 if let Some(assoc_def) = assoc_def { 341 if let Some(assoc_def) = assoc_def {
342 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { 342 if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) {
343 if let Some(adt) = imp.target_ty(sema.db).as_adt() { 343 let ty = imp.target_ty(sema.db);
344 let name = adt.name(sema.db).to_string(); 344 if let Some(adt) = ty.as_adt() {
345 let name = adt.name(sema.db);
345 let idx = path.rfind(':').map_or(0, |idx| idx + 1); 346 let idx = path.rfind(':').map_or(0, |idx| idx + 1);
346 let (prefix, suffix) = path.split_at(idx); 347 let (prefix, suffix) = path.split_at(idx);
347 return format!("{}{}::{}", prefix, name, suffix); 348 let mut ty_params = ty.type_parameters().peekable();
349 let params = if ty_params.peek().is_some() {
350 format!(
351 "<{}>",
352 ty_params.format_with(", ", |ty, cb| cb(&ty.display(sema.db)))
353 )
354 } else {
355 String::new()
356 };
357 return format!("{}{}{}::{}", prefix, name, params, suffix);
348 } 358 }
349 } 359 }
350 } 360 }
@@ -1406,4 +1416,41 @@ mod tests {
1406 "#]], 1416 "#]],
1407 ); 1417 );
1408 } 1418 }
1419
1420 #[test]
1421 fn doc_test_type_params() {
1422 check(
1423 r#"
1424//- /lib.rs
1425$0
1426struct Foo<T, U>;
1427
1428impl<T, U> Foo<T, U> {
1429 /// ```rust
1430 /// ````
1431 fn t() {}
1432}
1433"#,
1434 &[&DOCTEST],
1435 expect![[r#"
1436 [
1437 Runnable {
1438 nav: NavigationTarget {
1439 file_id: FileId(
1440 0,
1441 ),
1442 full_range: 47..85,
1443 name: "t",
1444 },
1445 kind: DocTest {
1446 test_id: Path(
1447 "Foo<T, U>::t",
1448 ),
1449 },
1450 cfg: None,
1451 },
1452 ]
1453 "#]],
1454 );
1455 }
1409} 1456}
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index e3c3aebac..a718faf63 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -145,9 +145,8 @@ mod tests {
145 use super::*; 145 use super::*;
146 146
147 fn do_type_char(char_typed: char, before: &str) -> Option<String> { 147 fn do_type_char(char_typed: char, before: &str) -> Option<String> {
148 let (offset, before) = extract_offset(before); 148 let (offset, mut before) = extract_offset(before);
149 let edit = TextEdit::insert(offset, char_typed.to_string()); 149 let edit = TextEdit::insert(offset, char_typed.to_string());
150 let mut before = before.to_string();
151 edit.apply(&mut before); 150 edit.apply(&mut before);
152 let parse = SourceFile::parse(&before); 151 let parse = SourceFile::parse(&before);
153 on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { 152 on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| {