diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/diagnostics.rs | 9 | ||||
-rw-r--r-- | crates/ide/src/goto_definition.rs | 64 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 165 | ||||
-rw-r--r-- | crates/ide/src/inlay_hints.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 103 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 166 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting.rs | 19 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | 2 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html | 2 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/test_data/highlighting.html | 10 |
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 @@ | |||
1 | use hir::Semantics; | 1 | use hir::Semantics; |
2 | use ide_db::{ | 2 | use ide_db::{ |
3 | base_db::FileId, | ||
3 | defs::{NameClass, NameRefClass}, | 4 | defs::{NameClass, NameRefClass}, |
4 | symbol_index, RootDatabase, | 5 | symbol_index, RootDatabase, |
5 | }; | 6 | }; |
6 | use syntax::{ | 7 | use 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 | ||
13 | use crate::{ | 9 | use 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 | ||
74 | fn 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)] |
66 | pub(crate) enum ReferenceResult { | 89 | pub(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#" | ||
1015 | struct Foo {} | ||
1016 | |||
1017 | impl 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#" | ||
1030 | struct Foo {} | ||
1031 | |||
1032 | impl 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 | " | 1047 | struct Thing { x: u32 } |
1034 | struct Thing { x: u32 } | 1048 | impl 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 | } | 1066 | enum Thing { A } |
1053 | } | 1067 | impl 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#" | ||
3322 | struct Foo {} | ||
3323 | impl 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#" | ||
3340 | struct Arc<T>(T); | ||
3341 | struct Foo {} | ||
3342 | impl 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 @@ | |||
1 | use assists::utils::FamousDefs; | ||
2 | use either::Either; | 1 | use either::Either; |
3 | use hir::{known, Callable, HirDisplay, Semantics}; | 2 | use hir::{known, Callable, HirDisplay, Semantics}; |
3 | use ide_db::helpers::FamousDefs; | ||
4 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
5 | use stdx::to_lower_snake_case; | 5 | use stdx::to_lower_snake_case; |
6 | use syntax::{ | 6 | use syntax::{ |
@@ -427,8 +427,8 @@ fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir: | |||
427 | 427 | ||
428 | #[cfg(test)] | 428 | #[cfg(test)] |
429 | mod tests { | 429 | mod 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 | ||
90 | pub use assists::{ | 90 | pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist}; |
91 | utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist, | ||
92 | }; | ||
93 | pub use hir::{Documentation, Semantics}; | 91 | pub use hir::{Documentation, Semantics}; |
94 | pub use ide_db::base_db::{ | 92 | pub 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::{ | |||
21 | use syntax::{ | 21 | use 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 | ||
27 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; | 27 | use 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 | ||
201 | fn 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)] |
198 | mod tests { | 273 | mod 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#" | ||
844 | struct Foo { bar: i32 } | ||
845 | |||
846 | impl 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#" | ||
1112 | struct Foo { i: i32 } | ||
1113 | |||
1114 | impl Foo { | ||
1115 | fn f(foo<|>: Foo) -> i32 { | ||
1116 | foo.i | ||
1117 | } | ||
1118 | } | ||
1119 | "#, | ||
1120 | r#" | ||
1121 | struct Foo { i: i32 } | ||
1122 | |||
1123 | impl 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#" | ||
1137 | struct Foo { i: i32 } | ||
1138 | |||
1139 | fn 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#" | ||
1148 | struct Foo { i: i32 } | ||
1149 | struct Bar; | ||
1150 | |||
1151 | impl 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#" | ||
1166 | struct Foo { i: i32 } | ||
1167 | impl 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#" | ||
1182 | struct Foo { i: i32 } | ||
1183 | impl &Foo { | ||
1184 | fn f(foo<|>: &Foo) -> i32 { | ||
1185 | foo.i | ||
1186 | } | ||
1187 | } | ||
1188 | "#, | ||
1189 | r#" | ||
1190 | struct Foo { i: i32 } | ||
1191 | impl &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#" | ||
1230 | struct Foo { i: i32 } | ||
1231 | |||
1232 | impl Foo { | ||
1233 | fn f(<|>self) -> i32 { | ||
1234 | self.i | ||
1235 | } | ||
1236 | } | ||
1237 | "#, | ||
1238 | r#" | ||
1239 | struct Foo { i: i32 } | ||
1240 | |||
1241 | impl 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">-></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">-></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></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"><</span><span class="type_param declaration">Args</span><span class="punctuation">></span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation"><</span><span class="type_param">Args</span><span class="punctuation">></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> |