aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/diagnostics.rs9
-rw-r--r--crates/ide/src/goto_definition.rs64
-rw-r--r--crates/ide/src/hover.rs165
-rw-r--r--crates/ide/src/inlay_hints.rs4
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/ide/src/references.rs103
-rw-r--r--crates/ide/src/references/rename.rs166
-rw-r--r--crates/ide/src/syntax_highlighting.rs19
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html10
11 files changed, 464 insertions, 84 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 3df73ed4f..9d3d88289 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -142,6 +142,15 @@ pub(crate) fn diagnostics(
142 .with_code(Some(d.code())), 142 .with_code(Some(d.code())),
143 ); 143 );
144 }) 144 })
145 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
146 // Use more accurate position if available.
147 let display_range =
148 d.precise_location.unwrap_or_else(|| sema.diagnostics_display_range(d).range);
149
150 // FIXME: it would be nice to tell the user whether proc macros are currently disabled
151 res.borrow_mut()
152 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
153 })
145 // Only collect experimental diagnostics when they're enabled. 154 // Only collect experimental diagnostics when they're enabled.
146 .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) 155 .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
147 .filter(|diag| !config.disabled.contains(diag.code().as_str())); 156 .filter(|diag| !config.disabled.contains(diag.code().as_str()));
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 15792f947..b9810457f 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,14 +1,10 @@
1use hir::Semantics; 1use hir::Semantics;
2use ide_db::{ 2use ide_db::{
3 base_db::FileId,
3 defs::{NameClass, NameRefClass}, 4 defs::{NameClass, NameRefClass},
4 symbol_index, RootDatabase, 5 symbol_index, RootDatabase,
5}; 6};
6use syntax::{ 7use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
7 ast::{self},
8 match_ast, AstNode,
9 SyntaxKind::*,
10 SyntaxToken, TokenAtOffset, T,
11};
12 8
13use crate::{ 9use crate::{
14 display::{ToNav, TryToNav}, 10 display::{ToNav, TryToNav},
@@ -44,6 +40,19 @@ pub(crate) fn goto_definition(
44 let nav = def.try_to_nav(sema.db)?; 40 let nav = def.try_to_nav(sema.db)?;
45 vec![nav] 41 vec![nav]
46 }, 42 },
43 ast::SelfParam(self_param) => {
44 vec![self_to_nav_target(self_param, position.file_id)?]
45 },
46 ast::PathSegment(segment) => {
47 segment.self_token()?;
48 let path = segment.parent_path();
49 if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) {
50 return None;
51 }
52 let func = segment.syntax().ancestors().find_map(ast::Fn::cast)?;
53 let self_param = func.param_list()?.self_param()?;
54 vec![self_to_nav_target(self_param, position.file_id)?]
55 },
47 _ => return None, 56 _ => return None,
48 } 57 }
49 }; 58 };
@@ -62,6 +71,20 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
62 } 71 }
63} 72}
64 73
74fn self_to_nav_target(self_param: ast::SelfParam, file_id: FileId) -> Option<NavigationTarget> {
75 let self_token = self_param.self_token()?;
76 Some(NavigationTarget {
77 file_id,
78 full_range: self_param.syntax().text_range(),
79 focus_range: Some(self_token.text_range()),
80 name: self_token.text().clone(),
81 kind: self_token.kind(),
82 container_name: None,
83 description: None,
84 docs: None,
85 })
86}
87
65#[derive(Debug)] 88#[derive(Debug)]
66pub(crate) enum ReferenceResult { 89pub(crate) enum ReferenceResult {
67 Exact(NavigationTarget), 90 Exact(NavigationTarget),
@@ -984,4 +1007,33 @@ fn g() -> <() as Iterator<A = (), B<|> = u8>>::A {}
984"#, 1007"#,
985 ); 1008 );
986 } 1009 }
1010
1011 #[test]
1012 fn goto_self_param_ty_specified() {
1013 check(
1014 r#"
1015struct Foo {}
1016
1017impl Foo {
1018 fn bar(self: &Foo) {
1019 //^^^^
1020 let foo = sel<|>f;
1021 }
1022}"#,
1023 )
1024 }
1025
1026 #[test]
1027 fn goto_self_param_on_decl() {
1028 check(
1029 r#"
1030struct Foo {}
1031
1032impl Foo {
1033 fn bar(&self<|>) {
1034 //^^^^
1035 }
1036}"#,
1037 )
1038 }
987} 1039}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 832192881..462f5c2b8 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -139,14 +139,17 @@ pub(crate) fn hover(
139 } 139 }
140 } 140 }
141 141
142 let node = token 142 let node = token.ancestors().find(|n| {
143 .ancestors() 143 ast::Expr::can_cast(n.kind())
144 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; 144 || ast::Pat::can_cast(n.kind())
145 || ast::SelfParam::can_cast(n.kind())
146 })?;
145 147
146 let ty = match_ast! { 148 let ty = match_ast! {
147 match node { 149 match node {
148 ast::Expr(it) => sema.type_of_expr(&it)?, 150 ast::Expr(it) => sema.type_of_expr(&it)?,
149 ast::Pat(it) => sema.type_of_pat(&it)?, 151 ast::Pat(it) => sema.type_of_pat(&it)?,
152 ast::SelfParam(self_param) => sema.type_of_self(&self_param)?,
150 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. 153 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
151 // (e.g expanding a builtin macro). So we give up here. 154 // (e.g expanding a builtin macro). So we give up here.
152 ast::MacroCall(_it) => return None, 155 ast::MacroCall(_it) => return None,
@@ -292,7 +295,6 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
292 ModuleDef::EnumVariant(e) => Some(e.parent_enum(db).name(db)), 295 ModuleDef::EnumVariant(e) => Some(e.parent_enum(db).name(db)),
293 _ => None, 296 _ => None,
294 }, 297 },
295 Definition::SelfType(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)),
296 _ => None, 298 _ => None,
297 } 299 }
298 .map(|name| name.to_string()) 300 .map(|name| name.to_string())
@@ -354,7 +356,14 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
354 ModuleDef::BuiltinType(it) => return Some(it.to_string().into()), 356 ModuleDef::BuiltinType(it) => return Some(it.to_string().into()),
355 }, 357 },
356 Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))), 358 Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))),
357 Definition::TypeParam(_) | Definition::SelfType(_) => { 359 Definition::SelfType(impl_def) => {
360 impl_def.target_ty(db).as_adt().and_then(|adt| match adt {
361 Adt::Struct(it) => from_def_source(db, it, mod_path),
362 Adt::Union(it) => from_def_source(db, it, mod_path),
363 Adt::Enum(it) => from_def_source(db, it, mod_path),
364 })
365 }
366 Definition::TypeParam(_) => {
358 // FIXME: Hover for generic param 367 // FIXME: Hover for generic param
359 None 368 None
360 } 369 }
@@ -1022,52 +1031,75 @@ impl Thing {
1022} 1031}
1023"#, 1032"#,
1024 expect![[r#" 1033 expect![[r#"
1025 *Self { x: 0 }* 1034 *Self*
1035
1026 ```rust 1036 ```rust
1027 Thing 1037 test
1038 ```
1039
1040 ```rust
1041 struct Thing
1028 ``` 1042 ```
1029 "#]], 1043 "#]],
1030 ) 1044 );
1031 } /* FIXME: revive these tests 1045 check(
1032 let (analysis, position) = fixture::position( 1046 r#"
1033 " 1047struct Thing { x: u32 }
1034 struct Thing { x: u32 } 1048impl Thing {
1035 impl Thing { 1049 fn new() -> Self<|> { Self { x: 0 } }
1036 fn new() -> Self<|> { 1050}
1037 Self { x: 0 } 1051"#,
1038 } 1052 expect![[r#"
1039 } 1053 *Self*
1040 ", 1054
1041 ); 1055 ```rust
1042 1056 test
1043 let hover = analysis.hover(position).unwrap().unwrap(); 1057 ```
1044 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("Thing")); 1058
1045 1059 ```rust
1046 let (analysis, position) = fixture::position( 1060 struct Thing
1047 " 1061 ```
1048 enum Thing { A } 1062 "#]],
1049 impl Thing { 1063 );
1050 pub fn new() -> Self<|> { 1064 check(
1051 Thing::A 1065 r#"
1052 } 1066enum Thing { A }
1053 } 1067impl Thing {
1054 ", 1068 pub fn new() -> Self<|> { Thing::A }
1055 ); 1069}
1056 let hover = analysis.hover(position).unwrap().unwrap(); 1070"#,
1057 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing")); 1071 expect![[r#"
1058 1072 *Self*
1059 let (analysis, position) = fixture::position( 1073
1060 " 1074 ```rust
1061 enum Thing { A } 1075 test
1062 impl Thing { 1076 ```
1063 pub fn thing(a: Self<|>) { 1077
1064 } 1078 ```rust
1065 } 1079 enum Thing
1066 ", 1080 ```
1067 ); 1081 "#]],
1068 let hover = analysis.hover(position).unwrap().unwrap(); 1082 );
1069 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing")); 1083 check(
1070 */ 1084 r#"
1085 enum Thing { A }
1086 impl Thing {
1087 pub fn thing(a: Self<|>) {}
1088 }
1089 "#,
1090 expect![[r#"
1091 *Self*
1092
1093 ```rust
1094 test
1095 ```
1096
1097 ```rust
1098 enum Thing
1099 ```
1100 "#]],
1101 );
1102 }
1071 1103
1072 #[test] 1104 #[test]
1073 fn test_hover_shadowing_pat() { 1105 fn test_hover_shadowing_pat() {
@@ -3282,4 +3314,41 @@ fn main() {
3282 "#]], 3314 "#]],
3283 ); 3315 );
3284 } 3316 }
3317
3318 #[test]
3319 fn hover_self_param_shows_type() {
3320 check(
3321 r#"
3322struct Foo {}
3323impl Foo {
3324 fn bar(&sel<|>f) {}
3325}
3326"#,
3327 expect![[r#"
3328 *&self*
3329 ```rust
3330 &Foo
3331 ```
3332 "#]],
3333 );
3334 }
3335
3336 #[test]
3337 fn hover_self_param_shows_type_for_arbitrary_self_type() {
3338 check(
3339 r#"
3340struct Arc<T>(T);
3341struct Foo {}
3342impl Foo {
3343 fn bar(sel<|>f: Arc<Foo>) {}
3344}
3345"#,
3346 expect![[r#"
3347 *self: Arc<Foo>*
3348 ```rust
3349 Arc<Foo>
3350 ```
3351 "#]],
3352 );
3353 }
3285} 3354}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 6cfb22e13..65df7979c 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,6 +1,6 @@
1use assists::utils::FamousDefs;
2use either::Either; 1use either::Either;
3use hir::{known, Callable, HirDisplay, Semantics}; 2use hir::{known, Callable, HirDisplay, Semantics};
3use ide_db::helpers::FamousDefs;
4use ide_db::RootDatabase; 4use ide_db::RootDatabase;
5use stdx::to_lower_snake_case; 5use stdx::to_lower_snake_case;
6use syntax::{ 6use syntax::{
@@ -427,8 +427,8 @@ fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir:
427 427
428#[cfg(test)] 428#[cfg(test)]
429mod tests { 429mod tests {
430 use assists::utils::FamousDefs;
431 use expect_test::{expect, Expect}; 430 use expect_test::{expect, Expect};
431 use ide_db::helpers::FamousDefs;
432 use test_utils::extract_annotations; 432 use test_utils::extract_annotations;
433 433
434 use crate::{fixture, inlay_hints::InlayHintsConfig}; 434 use crate::{fixture, inlay_hints::InlayHintsConfig};
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 6288f7ea7..5244bdd61 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -87,9 +87,7 @@ pub use ide_db::{
87 search::{Reference, ReferenceAccess, ReferenceKind}, 87 search::{Reference, ReferenceAccess, ReferenceKind},
88}; 88};
89 89
90pub use assists::{ 90pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist};
91 utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist,
92};
93pub use hir::{Documentation, Semantics}; 91pub use hir::{Documentation, Semantics};
94pub use ide_db::base_db::{ 92pub use ide_db::base_db::{
95 Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, 93 Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 5693dd400..7395b81bd 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -21,7 +21,7 @@ use ide_db::{
21use syntax::{ 21use syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, NameOwner}, 23 ast::{self, NameOwner},
24 AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, 24 match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset,
25}; 25};
26 26
27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; 27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
@@ -89,6 +89,10 @@ pub(crate) fn find_all_refs(
89 let _p = profile::span("find_all_refs"); 89 let _p = profile::span("find_all_refs");
90 let syntax = sema.parse(position.file_id).syntax().clone(); 90 let syntax = sema.parse(position.file_id).syntax().clone();
91 91
92 if let Some(res) = try_find_self_references(&syntax, position) {
93 return Some(res);
94 }
95
92 let (opt_name, search_kind) = if let Some(name) = 96 let (opt_name, search_kind) = if let Some(name) =
93 get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) 97 get_struct_def_name_for_struct_literal_search(&sema, &syntax, position)
94 { 98 {
@@ -194,6 +198,77 @@ fn get_struct_def_name_for_struct_literal_search(
194 None 198 None
195} 199}
196 200
201fn try_find_self_references(
202 syntax: &SyntaxNode,
203 position: FilePosition,
204) -> Option<RangeInfo<ReferenceSearchResult>> {
205 let self_token =
206 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)?;
207 let parent = self_token.parent();
208 match_ast! {
209 match parent {
210 ast::SelfParam(it) => (),
211 ast::PathSegment(segment) => {
212 segment.self_token()?;
213 let path = segment.parent_path();
214 if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) {
215 return None;
216 }
217 },
218 _ => return None,
219 }
220 };
221 let function = parent.ancestors().find_map(ast::Fn::cast)?;
222 let self_param = function.param_list()?.self_param()?;
223 let param_self_token = self_param.self_token()?;
224
225 let declaration = Declaration {
226 nav: NavigationTarget {
227 file_id: position.file_id,
228 full_range: self_param.syntax().text_range(),
229 focus_range: Some(param_self_token.text_range()),
230 name: param_self_token.text().clone(),
231 kind: param_self_token.kind(),
232 container_name: None,
233 description: None,
234 docs: None,
235 },
236 kind: ReferenceKind::SelfKw,
237 access: Some(if self_param.mut_token().is_some() {
238 ReferenceAccess::Write
239 } else {
240 ReferenceAccess::Read
241 }),
242 };
243 let references = function
244 .body()
245 .map(|body| {
246 body.syntax()
247 .descendants()
248 .filter_map(ast::PathExpr::cast)
249 .filter_map(|expr| {
250 let path = expr.path()?;
251 if path.qualifier().is_none() {
252 path.segment()?.self_token()
253 } else {
254 None
255 }
256 })
257 .map(|token| Reference {
258 file_range: FileRange { file_id: position.file_id, range: token.text_range() },
259 kind: ReferenceKind::SelfKw,
260 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration
261 })
262 .collect()
263 })
264 .unwrap_or_default();
265
266 Some(RangeInfo::new(
267 param_self_token.text_range(),
268 ReferenceSearchResult { declaration, references },
269 ))
270}
271
197#[cfg(test)] 272#[cfg(test)]
198mod tests { 273mod tests {
199 use expect_test::{expect, Expect}; 274 use expect_test::{expect, Expect};
@@ -762,6 +837,32 @@ fn f() -> m::En {
762 ); 837 );
763 } 838 }
764 839
840 #[test]
841 fn test_find_self_refs() {
842 check(
843 r#"
844struct Foo { bar: i32 }
845
846impl Foo {
847 fn foo(self) {
848 let x = self<|>.bar;
849 if true {
850 let _ = match () {
851 () => self,
852 };
853 }
854 }
855}
856"#,
857 expect![[r#"
858 self SELF_KW FileId(0) 47..51 47..51 SelfKw Read
859
860 FileId(0) 71..75 SelfKw Read
861 FileId(0) 152..156 SelfKw Read
862 "#]],
863 );
864 }
865
765 fn check(ra_fixture: &str, expect: Expect) { 866 fn check(ra_fixture: &str, expect: Expect) {
766 check_with_scope(ra_fixture, None, expect) 867 check_with_scope(ra_fixture, None, expect)
767 } 868 }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 91c64bd4a..64fe8bd65 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -221,24 +221,47 @@ fn rename_to_self(
221 let source_file = sema.parse(position.file_id); 221 let source_file = sema.parse(position.file_id);
222 let syn = source_file.syntax(); 222 let syn = source_file.syntax();
223 223
224 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset) 224 let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset)
225 .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast)))
225 .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; 226 .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?;
226 let params = 227 let param_range = fn_ast
227 fn_def.param_list().ok_or_else(|| RenameError("Method has no parameters".to_string()))?; 228 .param_list()
228 if params.self_param().is_some() { 229 .and_then(|p| p.params().next())
230 .ok_or_else(|| RenameError("Method has no parameters".to_string()))?
231 .syntax()
232 .text_range();
233 if !param_range.contains(position.offset) {
234 return Err(RenameError("Only the first parameter can be self".to_string()));
235 }
236
237 let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset)
238 .and_then(|def| sema.to_def(&def))
239 .ok_or_else(|| RenameError("No impl block found for function".to_string()))?;
240 if fn_def.self_param(sema.db).is_some() {
229 return Err(RenameError("Method already has a self parameter".to_string())); 241 return Err(RenameError("Method already has a self parameter".to_string()));
230 } 242 }
243
244 let params = fn_def.assoc_fn_params(sema.db);
231 let first_param = 245 let first_param =
232 params.params().next().ok_or_else(|| RenameError("Method has no parameters".into()))?; 246 params.first().ok_or_else(|| RenameError("Method has no parameters".into()))?;
233 let mutable = match first_param.ty() { 247 let first_param_ty = first_param.ty();
234 Some(ast::Type::RefType(rt)) => rt.mut_token().is_some(), 248 let impl_ty = impl_block.target_ty(sema.db);
235 _ => return Err(RenameError("Not renaming other types".to_string())), 249 let (ty, self_param) = if impl_ty.remove_ref().is_some() {
250 // if the impl is a ref to the type we can just match the `&T` with self directly
251 (first_param_ty.clone(), "self")
252 } else {
253 first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| {
254 (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" })
255 })
236 }; 256 };
237 257
258 if ty != impl_ty {
259 return Err(RenameError("Parameter type differs from impl block type".to_string()));
260 }
261
238 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) 262 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)
239 .ok_or_else(|| RenameError("No reference found at position".to_string()))?; 263 .ok_or_else(|| RenameError("No reference found at position".to_string()))?;
240 264
241 let param_range = first_param.syntax().text_range();
242 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs 265 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
243 .into_iter() 266 .into_iter()
244 .partition(|reference| param_range.intersect(reference.file_range.range).is_some()); 267 .partition(|reference| param_range.intersect(reference.file_range.range).is_some());
@@ -254,10 +277,7 @@ fn rename_to_self(
254 277
255 edits.push(SourceFileEdit { 278 edits.push(SourceFileEdit {
256 file_id: position.file_id, 279 file_id: position.file_id,
257 edit: TextEdit::replace( 280 edit: TextEdit::replace(param_range, String::from(self_param)),
258 param_range,
259 String::from(if mutable { "&mut self" } else { "&self" }),
260 ),
261 }); 281 });
262 282
263 Ok(RangeInfo::new(range, SourceChange::from(edits))) 283 Ok(RangeInfo::new(range, SourceChange::from(edits)))
@@ -280,7 +300,11 @@ fn text_edit_from_self_param(
280 300
281 let mut replacement_text = String::from(new_name); 301 let mut replacement_text = String::from(new_name);
282 replacement_text.push_str(": "); 302 replacement_text.push_str(": ");
283 replacement_text.push_str(self_param.mut_token().map_or("&", |_| "&mut ")); 303 match (self_param.amp_token(), self_param.mut_token()) {
304 (None, None) => (),
305 (Some(_), None) => replacement_text.push('&'),
306 (_, Some(_)) => replacement_text.push_str("&mut "),
307 };
284 replacement_text.push_str(type_name.as_str()); 308 replacement_text.push_str(type_name.as_str());
285 309
286 Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) 310 Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
@@ -1082,6 +1106,95 @@ impl Foo {
1082} 1106}
1083"#, 1107"#,
1084 ); 1108 );
1109 check(
1110 "self",
1111 r#"
1112struct Foo { i: i32 }
1113
1114impl Foo {
1115 fn f(foo<|>: Foo) -> i32 {
1116 foo.i
1117 }
1118}
1119"#,
1120 r#"
1121struct Foo { i: i32 }
1122
1123impl Foo {
1124 fn f(self) -> i32 {
1125 self.i
1126 }
1127}
1128"#,
1129 );
1130 }
1131
1132 #[test]
1133 fn test_parameter_to_self_error_no_impl() {
1134 check(
1135 "self",
1136 r#"
1137struct Foo { i: i32 }
1138
1139fn f(foo<|>: &mut Foo) -> i32 {
1140 foo.i
1141}
1142"#,
1143 "error: No impl block found for function",
1144 );
1145 check(
1146 "self",
1147 r#"
1148struct Foo { i: i32 }
1149struct Bar;
1150
1151impl Bar {
1152 fn f(foo<|>: &mut Foo) -> i32 {
1153 foo.i
1154 }
1155}
1156"#,
1157 "error: Parameter type differs from impl block type",
1158 );
1159 }
1160
1161 #[test]
1162 fn test_parameter_to_self_error_not_first() {
1163 check(
1164 "self",
1165 r#"
1166struct Foo { i: i32 }
1167impl Foo {
1168 fn f(x: (), foo<|>: &mut Foo) -> i32 {
1169 foo.i
1170 }
1171}
1172"#,
1173 "error: Only the first parameter can be self",
1174 );
1175 }
1176
1177 #[test]
1178 fn test_parameter_to_self_impl_ref() {
1179 check(
1180 "self",
1181 r#"
1182struct Foo { i: i32 }
1183impl &Foo {
1184 fn f(foo<|>: &Foo) -> i32 {
1185 foo.i
1186 }
1187}
1188"#,
1189 r#"
1190struct Foo { i: i32 }
1191impl &Foo {
1192 fn f(self) -> i32 {
1193 self.i
1194 }
1195}
1196"#,
1197 );
1085 } 1198 }
1086 1199
1087 #[test] 1200 #[test]
@@ -1110,6 +1223,31 @@ impl Foo {
1110 } 1223 }
1111 1224
1112 #[test] 1225 #[test]
1226 fn test_owned_self_to_parameter() {
1227 check(
1228 "foo",
1229 r#"
1230struct Foo { i: i32 }
1231
1232impl Foo {
1233 fn f(<|>self) -> i32 {
1234 self.i
1235 }
1236}
1237"#,
1238 r#"
1239struct Foo { i: i32 }
1240
1241impl Foo {
1242 fn f(foo: Foo) -> i32 {
1243 foo.i
1244 }
1245}
1246"#,
1247 );
1248 }
1249
1250 #[test]
1113 fn test_self_in_path_to_parameter() { 1251 fn test_self_in_path_to_parameter() {
1114 check( 1252 check(
1115 "foo", 1253 "foo",
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 1ed77b40b..5150a970c 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -76,6 +76,7 @@ pub(crate) fn highlight(
76 let mut current_macro_call: Option<ast::MacroCall> = None; 76 let mut current_macro_call: Option<ast::MacroCall> = None;
77 let mut format_string_highlighter = FormatStringHighlighter::default(); 77 let mut format_string_highlighter = FormatStringHighlighter::default();
78 let mut macro_rules_highlighter = MacroRulesHighlighter::default(); 78 let mut macro_rules_highlighter = MacroRulesHighlighter::default();
79 let mut inside_attribute = false;
79 80
80 // Walk all nodes, keeping track of whether we are inside a macro or not. 81 // Walk all nodes, keeping track of whether we are inside a macro or not.
81 // If in macro, expand it first and highlight the expanded code. 82 // If in macro, expand it first and highlight the expanded code.
@@ -132,9 +133,12 @@ pub(crate) fn highlight(
132 _ => (), 133 _ => (),
133 } 134 }
134 135
135 // Check for Rust code in documentation
136 match &event { 136 match &event {
137 // Check for Rust code in documentation
137 WalkEvent::Leave(NodeOrToken::Node(node)) => { 138 WalkEvent::Leave(NodeOrToken::Node(node)) => {
139 if ast::Attr::can_cast(node.kind()) {
140 inside_attribute = false
141 }
138 if let Some((doctest, range_mapping, new_comments)) = 142 if let Some((doctest, range_mapping, new_comments)) =
139 injection::extract_doc_comments(node) 143 injection::extract_doc_comments(node)
140 { 144 {
@@ -146,6 +150,9 @@ pub(crate) fn highlight(
146 ); 150 );
147 } 151 }
148 } 152 }
153 WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
154 inside_attribute = true
155 }
149 _ => (), 156 _ => (),
150 } 157 }
151 158
@@ -188,12 +195,16 @@ pub(crate) fn highlight(
188 } 195 }
189 } 196 }
190 197
191 if let Some((highlight, binding_hash)) = highlight_element( 198 if let Some((mut highlight, binding_hash)) = highlight_element(
192 &sema, 199 &sema,
193 &mut bindings_shadow_count, 200 &mut bindings_shadow_count,
194 syntactic_name_ref_highlighting, 201 syntactic_name_ref_highlighting,
195 element_to_highlight.clone(), 202 element_to_highlight.clone(),
196 ) { 203 ) {
204 if inside_attribute {
205 highlight = highlight | HighlightModifier::Attribute;
206 }
207
197 if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() { 208 if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() {
198 stack.add(HighlightedRange { range, highlight, binding_hash }); 209 stack.add(HighlightedRange { range, highlight, binding_hash });
199 } 210 }
@@ -474,7 +485,9 @@ fn highlight_element(
474 485
475 // Highlight references like the definitions they resolve to 486 // Highlight references like the definitions they resolve to
476 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { 487 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
477 Highlight::from(HighlightTag::Function) | HighlightModifier::Attribute 488 // even though we track whether we are in an attribute or not we still need this special case
489 // as otherwise we would emit unresolved references for name refs inside attributes
490 Highlight::from(HighlightTag::Function)
478 } 491 }
479 NAME_REF => { 492 NAME_REF => {
480 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); 493 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 6be88f856..d79fa6dca 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -50,7 +50,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
50 <span class="comment documentation">/// # Examples</span> 50 <span class="comment documentation">/// # Examples</span>
51 <span class="comment documentation">///</span> 51 <span class="comment documentation">///</span>
52 <span class="comment documentation">/// ```</span> 52 <span class="comment documentation">/// ```</span>
53 <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute injected">#</span><span class="attribute injected">!</span><span class="attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="attribute injected">]</span> 53 <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="punctuation attribute injected">)</span><span class="attribute attribute injected">]</span>
54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="operator injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected">
55</span> <span class="comment documentation">/// ```</span> 55</span> <span class="comment documentation">/// ```</span>
56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 4b6d6adc9..1d05b7713 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
54 54
55<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> 55<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
56 56
57<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">repr</span><span class="punctuation">(</span><span class="attribute">packed</span><span class="punctuation">)</span><span class="attribute">]</span> 57<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">repr</span><span class="punctuation attribute">(</span><span class="attribute attribute">packed</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span>
58<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="punctuation">{</span> 58<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="punctuation">{</span>
59 <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u16</span><span class="punctuation">,</span> 59 <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u16</span><span class="punctuation">,</span>
60<span class="punctuation">}</span> 60<span class="punctuation">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 6a10a9dcd..15fbf2ce3 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -40,18 +40,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
40 40
41<span class="comment">// Needed for function consuming vs normal</span> 41<span class="comment">// Needed for function consuming vs normal</span>
42<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span> 42<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span>
43 <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"copy"</span><span class="attribute">]</span> 43 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span>
44 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> 44 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span>
45<span class="punctuation">}</span> 45<span class="punctuation">}</span>
46 46
47<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="punctuation">{</span> 47<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="punctuation">{</span>
48 <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"fn_once"</span><span class="attribute">]</span> 48 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span>
49 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 49 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span>
50 50
51 <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"fn_mut"</span><span class="attribute">]</span> 51 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span>
52 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 52 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span>
53 53
54 <span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">lang</span><span class="attribute"> </span><span class="operator">=</span><span class="attribute"> </span><span class="string_literal">"fn"</span><span class="attribute">]</span> 54 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span>
55 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 55 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span>
56<span class="punctuation">}</span> 56<span class="punctuation">}</span>
57 57
@@ -85,7 +85,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
85 <span class="punctuation">}</span> 85 <span class="punctuation">}</span>
86<span class="punctuation">}</span> 86<span class="punctuation">}</span>
87 87
88<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Copy</span><span class="punctuation">)</span><span class="attribute">]</span> 88<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">derive</span><span class="punctuation attribute">(</span><span class="attribute attribute">Copy</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span>
89<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> 89<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span>
90 <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> 90 <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span>
91<span class="punctuation">}</span> 91<span class="punctuation">}</span>