aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/lib.rs3
-rw-r--r--crates/hir/src/source_analyzer.rs5
-rw-r--r--crates/hir_def/src/body.rs19
-rw-r--r--crates/hir_def/src/body/lower.rs24
-rw-r--r--crates/hir_expand/src/builtin_derive.rs2
-rw-r--r--crates/hir_ty/src/infer.rs6
-rw-r--r--crates/hir_ty/src/infer/expr.rs5
-rw-r--r--crates/ide/Cargo.toml1
-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
-rw-r--r--crates/ide_completion/src/completions/postfix/format_like.rs2
-rw-r--r--crates/ide_completion/src/item.rs37
-rw-r--r--crates/ide_completion/src/render.rs79
-rw-r--r--crates/ide_db/src/defs.rs2
-rw-r--r--crates/ide_db/src/search.rs16
-rw-r--r--crates/mbe/src/syntax_bridge.rs8
-rw-r--r--crates/proc_macro_api/src/version.rs4
-rw-r--r--crates/project_model/src/rustc_cfg.rs2
-rw-r--r--crates/rust-analyzer/src/handlers.rs22
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs4
-rw-r--r--crates/rust-analyzer/src/to_proto.rs14
30 files changed, 767 insertions, 157 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index c0810c69b..25e5bfb01 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -266,8 +266,7 @@ impl ModuleDef {
266 } 266 }
267 267
268 pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> { 268 pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> {
269 let mut segments = Vec::new(); 269 let mut segments = vec![self.name(db)?.to_string()];
270 segments.push(self.name(db)?.to_string());
271 for m in self.module(db)?.path_to_root(db) { 270 for m in self.module(db)?.path_to_root(db) {
272 segments.extend(m.name(db).map(|it| it.to_string())) 271 segments.extend(m.name(db).map(|it| it.to_string()))
273 } 272 }
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index d546512cb..4d59293e9 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -161,8 +161,9 @@ impl SourceAnalyzer {
161 db: &dyn HirDatabase, 161 db: &dyn HirDatabase,
162 field: &ast::RecordExprField, 162 field: &ast::RecordExprField,
163 ) -> Option<(Field, Option<Local>)> { 163 ) -> Option<(Field, Option<Local>)> {
164 let expr = field.expr()?; 164 let expr_id =
165 let expr_id = self.expr_id(db, &expr)?; 165 self.body_source_map.as_ref()?.node_field(InFile::new(self.file_id, field))?;
166
166 let local = if field.name_ref().is_some() { 167 let local = if field.name_ref().is_some() {
167 None 168 None
168 } else { 169 } else {
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 19c4eb521..8bcc350ce 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -253,11 +253,18 @@ pub type LabelSource = InFile<LabelPtr>;
253pub struct BodySourceMap { 253pub struct BodySourceMap {
254 expr_map: FxHashMap<ExprSource, ExprId>, 254 expr_map: FxHashMap<ExprSource, ExprId>,
255 expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>, 255 expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
256
256 pat_map: FxHashMap<PatSource, PatId>, 257 pat_map: FxHashMap<PatSource, PatId>,
257 pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, 258 pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
259
258 label_map: FxHashMap<LabelSource, LabelId>, 260 label_map: FxHashMap<LabelSource, LabelId>,
259 label_map_back: ArenaMap<LabelId, LabelSource>, 261 label_map_back: ArenaMap<LabelId, LabelSource>,
260 field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>, 262
263 /// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
264 /// Instead, we use id of expression (`92`) to identify the field.
265 field_map: FxHashMap<InFile<AstPtr<ast::RecordExprField>>, ExprId>,
266 field_map_back: FxHashMap<ExprId, InFile<AstPtr<ast::RecordExprField>>>,
267
261 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, 268 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
262 269
263 /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in 270 /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
@@ -337,6 +344,8 @@ impl Index<LabelId> for Body {
337 } 344 }
338} 345}
339 346
347// FIXME: Change `node_` prefix to something more reasonable.
348// Perhaps `expr_syntax` and `expr_id`?
340impl BodySourceMap { 349impl BodySourceMap {
341 pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> { 350 pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
342 self.expr_map_back[expr].clone() 351 self.expr_map_back[expr].clone()
@@ -375,8 +384,12 @@ impl BodySourceMap {
375 self.label_map.get(&src).cloned() 384 self.label_map.get(&src).cloned()
376 } 385 }
377 386
378 pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> { 387 pub fn field_syntax(&self, expr: ExprId) -> InFile<AstPtr<ast::RecordExprField>> {
379 self.field_map[&(expr, field)].clone() 388 self.field_map_back[&expr].clone()
389 }
390 pub fn node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId> {
391 let src = node.map(|it| AstPtr::new(it));
392 self.field_map.get(&src).cloned()
380 } 393 }
381 394
382 pub(crate) fn add_diagnostics(&self, _db: &dyn DefDatabase, sink: &mut DiagnosticSink<'_>) { 395 pub(crate) fn add_diagnostics(&self, _db: &dyn DefDatabase, sink: &mut DiagnosticSink<'_>) {
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 8c8eb8007..8934ae6c9 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -379,23 +379,22 @@ impl ExprCollector<'_> {
379 } 379 }
380 ast::Expr::RecordExpr(e) => { 380 ast::Expr::RecordExpr(e) => {
381 let path = e.path().and_then(|path| self.expander.parse_path(path)); 381 let path = e.path().and_then(|path| self.expander.parse_path(path));
382 let mut field_ptrs = Vec::new();
383 let record_lit = if let Some(nfl) = e.record_expr_field_list() { 382 let record_lit = if let Some(nfl) = e.record_expr_field_list() {
384 let fields = nfl 383 let fields = nfl
385 .fields() 384 .fields()
386 .inspect(|field| field_ptrs.push(AstPtr::new(field)))
387 .filter_map(|field| { 385 .filter_map(|field| {
388 self.check_cfg(&field)?; 386 self.check_cfg(&field)?;
389 387
390 let name = field.field_name()?.as_name(); 388 let name = field.field_name()?.as_name();
391 389
392 Some(RecordLitField { 390 let expr = match field.expr() {
393 name, 391 Some(e) => self.collect_expr(e),
394 expr: match field.expr() { 392 None => self.missing_expr(),
395 Some(e) => self.collect_expr(e), 393 };
396 None => self.missing_expr(), 394 let src = self.expander.to_source(AstPtr::new(&field));
397 }, 395 self.source_map.field_map.insert(src.clone(), expr);
398 }) 396 self.source_map.field_map_back.insert(expr, src);
397 Some(RecordLitField { name, expr })
399 }) 398 })
400 .collect(); 399 .collect();
401 let spread = nfl.spread().map(|s| self.collect_expr(s)); 400 let spread = nfl.spread().map(|s| self.collect_expr(s));
@@ -404,12 +403,7 @@ impl ExprCollector<'_> {
404 Expr::RecordLit { path, fields: Vec::new(), spread: None } 403 Expr::RecordLit { path, fields: Vec::new(), spread: None }
405 }; 404 };
406 405
407 let res = self.alloc_expr(record_lit, syntax_ptr); 406 self.alloc_expr(record_lit, syntax_ptr)
408 for (i, ptr) in field_ptrs.into_iter().enumerate() {
409 let src = self.expander.to_source(ptr);
410 self.source_map.field_map.insert((res, i), src);
411 }
412 res
413 } 407 }
414 ast::Expr::FieldExpr(e) => { 408 ast::Expr::FieldExpr(e) => {
415 let expr = self.collect_expr_opt(e.expr()); 409 let expr = self.collect_expr_opt(e.expr());
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index dfdb9cf59..5e908b223 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -108,7 +108,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
108} 108}
109 109
110fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> { 110fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
111 let mut result = Vec::<tt::TokenTree>::new(); 111 let mut result = Vec::<tt::TokenTree>::with_capacity(n * 2);
112 result.push( 112 result.push(
113 tt::Leaf::Punct(tt::Punct { 113 tt::Leaf::Punct(tt::Punct {
114 char: '<', 114 char: '<',
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 6dfe53902..9c385b845 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -802,7 +802,7 @@ mod diagnostics {
802 802
803 #[derive(Debug, PartialEq, Eq, Clone)] 803 #[derive(Debug, PartialEq, Eq, Clone)]
804 pub(super) enum InferenceDiagnostic { 804 pub(super) enum InferenceDiagnostic {
805 NoSuchField { expr: ExprId, field: usize }, 805 NoSuchField { expr: ExprId },
806 BreakOutsideOfLoop { expr: ExprId }, 806 BreakOutsideOfLoop { expr: ExprId },
807 } 807 }
808 808
@@ -814,9 +814,9 @@ mod diagnostics {
814 sink: &mut DiagnosticSink, 814 sink: &mut DiagnosticSink,
815 ) { 815 ) {
816 match self { 816 match self {
817 InferenceDiagnostic::NoSuchField { expr, field } => { 817 InferenceDiagnostic::NoSuchField { expr } => {
818 let (_, source_map) = db.body_with_source_map(owner); 818 let (_, source_map) = db.body_with_source_map(owner);
819 let field = source_map.field_syntax(*expr, *field); 819 let field = source_map.field_syntax(*expr);
820 sink.push(NoSuchField { file: field.file_id, field: field.value }) 820 sink.push(NoSuchField { file: field.file_id, field: field.value })
821 } 821 }
822 InferenceDiagnostic::BreakOutsideOfLoop { expr } => { 822 InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 8c58a1b6c..f40dec17f 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -405,14 +405,13 @@ impl<'a> InferenceContext<'a> {
405 let substs = ty.substs().cloned().unwrap_or_else(Substs::empty); 405 let substs = ty.substs().cloned().unwrap_or_else(Substs::empty);
406 let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default(); 406 let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default();
407 let variant_data = def_id.map(|it| variant_data(self.db.upcast(), it)); 407 let variant_data = def_id.map(|it| variant_data(self.db.upcast(), it));
408 for (field_idx, field) in fields.iter().enumerate() { 408 for field in fields.iter() {
409 let field_def = 409 let field_def =
410 variant_data.as_ref().and_then(|it| match it.field(&field.name) { 410 variant_data.as_ref().and_then(|it| match it.field(&field.name) {
411 Some(local_id) => Some(FieldId { parent: def_id.unwrap(), local_id }), 411 Some(local_id) => Some(FieldId { parent: def_id.unwrap(), local_id }),
412 None => { 412 None => {
413 self.push_diagnostic(InferenceDiagnostic::NoSuchField { 413 self.push_diagnostic(InferenceDiagnostic::NoSuchField {
414 expr: tgt_expr, 414 expr: field.expr,
415 field: field_idx,
416 }); 415 });
417 None 416 None
418 } 417 }
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index 107bd8432..6ae7c9e6c 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -38,3 +38,4 @@ hir = { path = "../hir", version = "0.0.0" }
38[dev-dependencies] 38[dev-dependencies]
39test_utils = { path = "../test_utils" } 39test_utils = { path = "../test_utils" }
40expect-test = "1.1" 40expect-test = "1.1"
41cov-mark = "1.1.0"
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| {
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
index 3afc63021..cee4eec10 100644
--- a/crates/ide_completion/src/completions/postfix/format_like.rs
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -59,7 +59,7 @@ pub(crate) fn add_format_like_completions(
59/// Checks whether provided item is a string literal. 59/// Checks whether provided item is a string literal.
60fn string_literal_contents(item: &ast::String) -> Option<String> { 60fn string_literal_contents(item: &ast::String) -> Option<String> {
61 let item = item.text(); 61 let item = item.text();
62 if item.len() >= 2 && item.starts_with("\"") && item.ends_with("\"") { 62 if item.len() >= 2 && item.starts_with('\"') && item.ends_with('\"') {
63 return Some(item[1..item.len() - 1].to_owned()); 63 return Some(item[1..item.len() - 1].to_owned());
64 } 64 }
65 65
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 3febab32b..9a4b5217a 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -144,6 +144,21 @@ pub struct CompletionRelevance {
144 /// } 144 /// }
145 /// ``` 145 /// ```
146 pub exact_type_match: bool, 146 pub exact_type_match: bool,
147 /// This is set in cases like these:
148 ///
149 /// ```
150 /// fn foo(bar: u32) {
151 /// $0 // `bar` is local
152 /// }
153 /// ```
154 ///
155 /// ```
156 /// fn foo() {
157 /// let bar = 0;
158 /// $0 // `bar` is local
159 /// }
160 /// ```
161 pub is_local: bool,
147} 162}
148 163
149impl CompletionRelevance { 164impl CompletionRelevance {
@@ -163,6 +178,9 @@ impl CompletionRelevance {
163 score += 1; 178 score += 1;
164 } 179 }
165 if self.exact_type_match { 180 if self.exact_type_match {
181 score += 3;
182 }
183 if self.is_local {
166 score += 1; 184 score += 1;
167 } 185 }
168 186
@@ -551,9 +569,24 @@ mod tests {
551 vec![CompletionRelevance::default()], 569 vec![CompletionRelevance::default()],
552 vec![ 570 vec![
553 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() }, 571 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },
554 CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }, 572 CompletionRelevance { is_local: true, ..CompletionRelevance::default() },
555 ], 573 ],
556 vec![CompletionRelevance { exact_name_match: true, exact_type_match: true }], 574 vec![CompletionRelevance {
575 exact_name_match: true,
576 is_local: true,
577 ..CompletionRelevance::default()
578 }],
579 vec![CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }],
580 vec![CompletionRelevance {
581 exact_name_match: true,
582 exact_type_match: true,
583 ..CompletionRelevance::default()
584 }],
585 vec![CompletionRelevance {
586 exact_name_match: true,
587 exact_type_match: true,
588 is_local: true,
589 }],
557 ]; 590 ];
558 591
559 check_relevance_score_ordered(expected_relevance_order); 592 check_relevance_score_ordered(expected_relevance_order);
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index fcb8115fb..905f0b197 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -157,9 +157,7 @@ impl<'a> Render<'a> {
157 .set_documentation(field.docs(self.ctx.db())) 157 .set_documentation(field.docs(self.ctx.db()))
158 .set_deprecated(is_deprecated); 158 .set_deprecated(is_deprecated);
159 159
160 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { 160 item.set_relevance(compute_relevance(&self.ctx, &ty, &name.to_string()));
161 item.set_relevance(relevance);
162 }
163 161
164 item.build() 162 item.build()
165 } 163 }
@@ -254,9 +252,9 @@ impl<'a> Render<'a> {
254 if let ScopeDef::Local(local) = resolution { 252 if let ScopeDef::Local(local) = resolution {
255 let ty = local.ty(self.ctx.db()); 253 let ty = local.ty(self.ctx.db());
256 254
257 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { 255 let mut relevance = compute_relevance(&self.ctx, &ty, &local_name);
258 item.set_relevance(relevance); 256 relevance.is_local = true;
259 } 257 item.set_relevance(relevance);
260 258
261 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { 259 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() {
262 if ty != expected_type { 260 if ty != expected_type {
@@ -328,12 +326,15 @@ impl<'a> Render<'a> {
328 } 326 }
329} 327}
330 328
331fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> { 329fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance {
332 let (expected_name, expected_type) = ctx.expected_name_and_type()?;
333 let mut res = CompletionRelevance::default(); 330 let mut res = CompletionRelevance::default();
334 res.exact_type_match = ty == &expected_type; 331
335 res.exact_name_match = name == &expected_name; 332 if let Some((expected_name, expected_type)) = ctx.expected_name_and_type() {
336 Some(res) 333 res.exact_type_match = ty == &expected_type;
334 res.exact_name_match = name == &expected_name;
335 }
336
337 res
337} 338}
338 339
339fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { 340fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool {
@@ -343,6 +344,7 @@ fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -
343#[cfg(test)] 344#[cfg(test)]
344mod tests { 345mod tests {
345 use expect_test::{expect, Expect}; 346 use expect_test::{expect, Expect};
347 use itertools::Itertools;
346 348
347 use crate::{ 349 use crate::{
348 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 350 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
@@ -355,15 +357,17 @@ mod tests {
355 } 357 }
356 358
357 fn check_relevance(ra_fixture: &str, expect: Expect) { 359 fn check_relevance(ra_fixture: &str, expect: Expect) {
358 fn display_relevance(relevance: CompletionRelevance) -> &'static str { 360 fn display_relevance(relevance: CompletionRelevance) -> String {
359 match relevance { 361 let relevance_factors = vec![
360 CompletionRelevance { exact_type_match: true, exact_name_match: true } => { 362 (relevance.exact_type_match, "type"),
361 "[type+name]" 363 (relevance.exact_name_match, "name"),
362 } 364 (relevance.is_local, "local"),
363 CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]", 365 ]
364 CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]", 366 .into_iter()
365 CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]", 367 .filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
366 } 368 .join("+");
369
370 format!("[{}]", relevance_factors)
367 } 371 }
368 372
369 let actual = get_all_items(TEST_CONFIG, ra_fixture) 373 let actual = get_all_items(TEST_CONFIG, ra_fixture)
@@ -918,7 +922,7 @@ struct WorldSnapshot { _f: () };
918fn go(world: &WorldSnapshot) { go(w$0) } 922fn go(world: &WorldSnapshot) { go(w$0) }
919"#, 923"#,
920 expect![[r#" 924 expect![[r#"
921 lc world [type+name] 925 lc world [type+name+local]
922 st WorldSnapshot [] 926 st WorldSnapshot []
923 fn go(…) [] 927 fn go(…) []
924 "#]], 928 "#]],
@@ -933,7 +937,7 @@ struct Foo;
933fn f(foo: &Foo) { f(foo, w$0) } 937fn f(foo: &Foo) { f(foo, w$0) }
934"#, 938"#,
935 expect![[r#" 939 expect![[r#"
936 lc foo [] 940 lc foo [local]
937 st Foo [] 941 st Foo []
938 fn f(…) [] 942 fn f(…) []
939 "#]], 943 "#]],
@@ -998,6 +1002,7 @@ fn main() {
998 relevance: CompletionRelevance { 1002 relevance: CompletionRelevance {
999 exact_name_match: true, 1003 exact_name_match: true,
1000 exact_type_match: false, 1004 exact_type_match: false,
1005 is_local: true,
1001 }, 1006 },
1002 ref_match: "&mut ", 1007 ref_match: "&mut ",
1003 }, 1008 },
@@ -1037,9 +1042,9 @@ fn main() {
1037} 1042}
1038 "#, 1043 "#,
1039 expect![[r#" 1044 expect![[r#"
1040 lc m [] 1045 lc m [local]
1041 lc t [] 1046 lc t [local]
1042 lc &t [type] 1047 lc &t [type+local]
1043 st T [] 1048 st T []
1044 st S [] 1049 st S []
1045 fn main() [] 1050 fn main() []
@@ -1091,9 +1096,9 @@ fn main() {
1091} 1096}
1092 "#, 1097 "#,
1093 expect![[r#" 1098 expect![[r#"
1094 lc m [] 1099 lc m [local]
1095 lc t [] 1100 lc t [local]
1096 lc &mut t [type] 1101 lc &mut t [type+local]
1097 tt DerefMut [] 1102 tt DerefMut []
1098 tt Deref [] 1103 tt Deref []
1099 fn foo(…) [] 1104 fn foo(…) []
@@ -1103,4 +1108,22 @@ fn main() {
1103 "#]], 1108 "#]],
1104 ) 1109 )
1105 } 1110 }
1111
1112 #[test]
1113 fn locals() {
1114 check_relevance(
1115 r#"
1116fn foo(bar: u32) {
1117 let baz = 0;
1118
1119 f$0
1120}
1121"#,
1122 expect![[r#"
1123 lc baz [local]
1124 lc bar [local]
1125 fn foo(…) []
1126 "#]],
1127 );
1128 }
1106} 1129}
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index ff612b7d0..f86e5ce93 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -70,7 +70,7 @@ impl Definition {
70 hir::ModuleDef::Static(it) => it.name(db)?, 70 hir::ModuleDef::Static(it) => it.name(db)?,
71 hir::ModuleDef::Trait(it) => it.name(db), 71 hir::ModuleDef::Trait(it) => it.name(db),
72 hir::ModuleDef::TypeAlias(it) => it.name(db), 72 hir::ModuleDef::TypeAlias(it) => it.name(db),
73 hir::ModuleDef::BuiltinType(_) => return None, 73 hir::ModuleDef::BuiltinType(it) => it.name(),
74 }, 74 },
75 Definition::SelfType(_) => return None, 75 Definition::SelfType(_) => return None,
76 Definition::Local(it) => it.name(db)?, 76 Definition::Local(it) => it.name(db)?,
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index fa18703e1..d00a8b6e7 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -6,7 +6,7 @@
6 6
7use std::{convert::TryInto, mem}; 7use std::{convert::TryInto, mem};
8 8
9use base_db::{FileId, FileRange, SourceDatabaseExt}; 9use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
10use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; 10use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility};
11use once_cell::unsync::Lazy; 11use once_cell::unsync::Lazy;
12use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
@@ -138,6 +138,20 @@ impl IntoIterator for SearchScope {
138impl Definition { 138impl Definition {
139 fn search_scope(&self, db: &RootDatabase) -> SearchScope { 139 fn search_scope(&self, db: &RootDatabase) -> SearchScope {
140 let _p = profile::span("search_scope"); 140 let _p = profile::span("search_scope");
141
142 if let Definition::ModuleDef(hir::ModuleDef::BuiltinType(_)) = self {
143 let mut res = FxHashMap::default();
144
145 let graph = db.crate_graph();
146 for krate in graph.iter() {
147 let root_file = graph[krate].root_file_id;
148 let source_root_id = db.file_source_root(root_file);
149 let source_root = db.source_root(source_root_id);
150 res.extend(source_root.iter().map(|id| (id, None)));
151 }
152 return SearchScope::new(res);
153 }
154
141 let module = match self.module(db) { 155 let module = match self.module(db) {
142 Some(it) => it, 156 Some(it) => it,
143 None => return SearchScope::empty(), 157 None => return SearchScope::empty(),
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index aacae1026..b715ebfc4 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -222,14 +222,10 @@ fn convert_doc_comment(token: &syntax::SyntaxToken) -> Option<Vec<tt::TokenTree>
222 let doc = comment.kind().doc?; 222 let doc = comment.kind().doc?;
223 223
224 // Make `doc="\" Comments\"" 224 // Make `doc="\" Comments\""
225 let mut meta_tkns = Vec::new(); 225 let meta_tkns = vec![mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)];
226 meta_tkns.push(mk_ident("doc"));
227 meta_tkns.push(mk_punct('='));
228 meta_tkns.push(mk_doc_literal(&comment));
229 226
230 // Make `#![]` 227 // Make `#![]`
231 let mut token_trees = Vec::new(); 228 let mut token_trees = vec![mk_punct('#')];
232 token_trees.push(mk_punct('#'));
233 if let ast::CommentPlacement::Inner = doc { 229 if let ast::CommentPlacement::Inner = doc {
234 token_trees.push(mk_punct('!')); 230 token_trees.push(mk_punct('!'));
235 } 231 }
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs
index 11a7fb59a..b903658fb 100644
--- a/crates/proc_macro_api/src/version.rs
+++ b/crates/proc_macro_api/src/version.rs
@@ -33,7 +33,7 @@ pub(crate) fn read_info(dylib_path: &Path) -> io::Result<RustCInfo> {
33 } 33 }
34 34
35 let version_part = items.next().ok_or(err!("no version string"))?; 35 let version_part = items.next().ok_or(err!("no version string"))?;
36 let mut version_parts = version_part.split("-"); 36 let mut version_parts = version_part.split('-');
37 let version = version_parts.next().ok_or(err!("no version"))?; 37 let version = version_parts.next().ok_or(err!("no version"))?;
38 let channel = version_parts.next().unwrap_or_default().to_string(); 38 let channel = version_parts.next().unwrap_or_default().to_string();
39 39
@@ -51,7 +51,7 @@ pub(crate) fn read_info(dylib_path: &Path) -> io::Result<RustCInfo> {
51 let date = date[0..date.len() - 2].to_string(); 51 let date = date[0..date.len() - 2].to_string();
52 52
53 let version_numbers = version 53 let version_numbers = version
54 .split(".") 54 .split('.')
55 .map(|it| it.parse::<usize>()) 55 .map(|it| it.parse::<usize>())
56 .collect::<Result<Vec<_>, _>>() 56 .collect::<Result<Vec<_>, _>>()
57 .map_err(|_| err!("version number error"))?; 57 .map_err(|_| err!("version number error"))?;
diff --git a/crates/project_model/src/rustc_cfg.rs b/crates/project_model/src/rustc_cfg.rs
index 4a7bd8ae3..312708575 100644
--- a/crates/project_model/src/rustc_cfg.rs
+++ b/crates/project_model/src/rustc_cfg.rs
@@ -6,7 +6,7 @@ use crate::{cfg_flag::CfgFlag, utf8_stdout};
6 6
7pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> { 7pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> {
8 let _p = profile::span("rustc_cfg::get"); 8 let _p = profile::span("rustc_cfg::get");
9 let mut res = Vec::new(); 9 let mut res = Vec::with_capacity(6 * 2 + 1);
10 10
11 // Some nightly-only cfgs, which are required for stdlib 11 // Some nightly-only cfgs, which are required for stdlib
12 res.push(CfgFlag::Atom("target_thread_local".into())); 12 res.push(CfgFlag::Atom("target_thread_local".into()));
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 706a39dab..6fb7da79c 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -289,7 +289,7 @@ pub(crate) fn handle_document_symbol(
289 let doc_symbol = lsp_types::DocumentSymbol { 289 let doc_symbol = lsp_types::DocumentSymbol {
290 name: symbol.label, 290 name: symbol.label,
291 detail: symbol.detail, 291 detail: symbol.detail,
292 kind: to_proto::symbol_kind(symbol.kind), 292 kind: to_proto::structure_node_kind(symbol.kind),
293 tags: Some(tags), 293 tags: Some(tags),
294 deprecated: Some(symbol.deprecated), 294 deprecated: Some(symbol.deprecated),
295 range: to_proto::range(&line_index, symbol.node_range), 295 range: to_proto::range(&line_index, symbol.node_range),
@@ -846,9 +846,9 @@ pub(crate) fn handle_references(
846 }; 846 };
847 847
848 let decl = if params.context.include_declaration { 848 let decl = if params.context.include_declaration {
849 Some(FileRange { 849 refs.declaration.map(|decl| FileRange {
850 file_id: refs.declaration.nav.file_id, 850 file_id: decl.nav.file_id,
851 range: refs.declaration.nav.focus_or_full_range(), 851 range: decl.nav.focus_or_full_range(),
852 }) 852 })
853 } else { 853 } else {
854 None 854 None
@@ -1153,14 +1153,12 @@ pub(crate) fn handle_document_highlight(
1153 Some(refs) => refs, 1153 Some(refs) => refs,
1154 }; 1154 };
1155 1155
1156 let decl = if refs.declaration.nav.file_id == position.file_id { 1156 let decl = refs.declaration.filter(|decl| decl.nav.file_id == position.file_id).map(|decl| {
1157 Some(DocumentHighlight { 1157 DocumentHighlight {
1158 range: to_proto::range(&line_index, refs.declaration.nav.focus_or_full_range()), 1158 range: to_proto::range(&line_index, decl.nav.focus_or_full_range()),
1159 kind: refs.declaration.access.map(to_proto::document_highlight_kind), 1159 kind: decl.access.map(to_proto::document_highlight_kind),
1160 }) 1160 }
1161 } else { 1161 });
1162 None
1163 };
1164 1162
1165 let file_refs = refs.references.get(&position.file_id).map_or(&[][..], Vec::as_slice); 1163 let file_refs = refs.references.get(&position.file_id).map_or(&[][..], Vec::as_slice);
1166 let mut res = Vec::with_capacity(file_refs.len() + 1); 1164 let mut res = Vec::with_capacity(file_refs.len() + 1);
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 84f78b5b8..3ca7f8040 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -360,11 +360,11 @@ mod tests {
360 "Completion with disjoint edits is valid" 360 "Completion with disjoint edits is valid"
361 ); 361 );
362 assert!( 362 assert!(
363 !all_edits_are_disjoint(&completion_with_disjoint_edits, &[joint_edit.clone()]), 363 !all_edits_are_disjoint(&completion_with_disjoint_edits, &[joint_edit]),
364 "Completion with disjoint edits and joint extra edit is invalid" 364 "Completion with disjoint edits and joint extra edit is invalid"
365 ); 365 );
366 assert!( 366 assert!(
367 all_edits_are_disjoint(&completion_with_disjoint_edits, &[disjoint_edit_2.clone()]), 367 all_edits_are_disjoint(&completion_with_disjoint_edits, &[disjoint_edit_2]),
368 "Completion with disjoint edits and joint extra edit is valid" 368 "Completion with disjoint edits and joint extra edit is valid"
369 ); 369 );
370 } 370 }
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 5736875d3..70eaae5e8 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -9,9 +9,8 @@ use ide::{
9 CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, 9 CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind,
10 Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, 10 Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat,
11 Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange, 11 Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange,
12 TextEdit, TextRange, TextSize, 12 StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
13}; 13};
14use ide_db::SymbolKind;
15use itertools::Itertools; 14use itertools::Itertools;
16use serde_json::to_value; 15use serde_json::to_value;
17 16
@@ -63,6 +62,13 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
63 } 62 }
64} 63}
65 64
65pub(crate) fn structure_node_kind(kind: StructureNodeKind) -> lsp_types::SymbolKind {
66 match kind {
67 StructureNodeKind::SymbolKind(symbol) => symbol_kind(symbol),
68 StructureNodeKind::Region => lsp_types::SymbolKind::Namespace,
69 }
70}
71
66pub(crate) fn document_highlight_kind( 72pub(crate) fn document_highlight_kind(
67 reference_access: ReferenceAccess, 73 reference_access: ReferenceAccess,
68) -> lsp_types::DocumentHighlightKind { 74) -> lsp_types::DocumentHighlightKind {
@@ -1117,13 +1123,13 @@ mod tests {
1117 ( 1123 (
1118 "&arg", 1124 "&arg",
1119 Some( 1125 Some(
1120 "fffffffd", 1126 "fffffffa",
1121 ), 1127 ),
1122 ), 1128 ),
1123 ( 1129 (
1124 "arg", 1130 "arg",
1125 Some( 1131 Some(
1126 "fffffffe", 1132 "fffffffd",
1127 ), 1133 ),
1128 ), 1134 ),
1129 ] 1135 ]