aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/goto_definition.rs56
-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
7 files changed, 316 insertions, 42 deletions
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index d41dd3d92..b9810457f 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,5 +1,6 @@
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};
@@ -40,10 +41,17 @@ pub(crate) fn goto_definition(
40 vec![nav] 41 vec![nav]
41 }, 42 },
42 ast::SelfParam(self_param) => { 43 ast::SelfParam(self_param) => {
43 let ty = sema.type_of_self(&self_param)?; 44 vec![self_to_nav_target(self_param, position.file_id)?]
44 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?; 45 },
45 let nav = adt_def.to_nav(db); 46 ast::PathSegment(segment) => {
46 vec![nav] 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)?]
47 }, 55 },
48 _ => return None, 56 _ => return None,
49 } 57 }
@@ -63,6 +71,20 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
63 } 71 }
64} 72}
65 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
66#[derive(Debug)] 88#[derive(Debug)]
67pub(crate) enum ReferenceResult { 89pub(crate) enum ReferenceResult {
68 Exact(NavigationTarget), 90 Exact(NavigationTarget),
@@ -987,31 +1009,31 @@ fn g() -> <() as Iterator<A = (), B<|> = u8>>::A {}
987 } 1009 }
988 1010
989 #[test] 1011 #[test]
990 fn todo_def_type_for_self() { 1012 fn goto_self_param_ty_specified() {
991 check( 1013 check(
992 r#" 1014 r#"
993struct Foo {} 1015struct Foo {}
994 //^^^
995 1016
996impl Foo { 1017impl Foo {
997 fn bar(&self<|>) {} 1018 fn bar(self: &Foo) {
998} 1019 //^^^^
999"#, 1020 let foo = sel<|>f;
1000 ); 1021 }
1022}"#,
1023 )
1001 } 1024 }
1002 1025
1003 #[test] 1026 #[test]
1004 fn todo_def_type_for_arbitrary_self() { 1027 fn goto_self_param_on_decl() {
1005 check( 1028 check(
1006 r#" 1029 r#"
1007struct Arc<T>(T);
1008 //^^^
1009struct Foo {} 1030struct Foo {}
1010 1031
1011impl Foo { 1032impl Foo {
1012 fn bar(self<|>: Arc<Self>) {} 1033 fn bar(&self<|>) {
1013} 1034 //^^^^
1014"#, 1035 }
1015 ); 1036}"#,
1037 )
1016 } 1038 }
1017} 1039}
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>