aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorPaul Daniel Faria <[email protected]>2020-07-19 16:45:46 +0100
committerPaul Daniel Faria <[email protected]>2020-08-10 13:44:54 +0100
commit08182aa9fad4021e60cdc80ee0a578929507e115 (patch)
tree3056a7d87fd90356da335f3650a8438ce2a03761 /crates
parentc5cc24cb312c70159e63315ea49769b575e8cb65 (diff)
Move unsafe packed ref logic to Semantics, use `Attrs::by_key` to simplify repr attr lookup
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/semantics.rs41
-rw-r--r--crates/ra_hir_def/src/adt.rs25
-rw-r--r--crates/ra_ide/src/call_info.rs.orig769
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs34
4 files changed, 815 insertions, 54 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index e392130ab..1072b3971 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -279,6 +279,47 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
279 pub fn assert_contains_node(&self, node: &SyntaxNode) { 279 pub fn assert_contains_node(&self, node: &SyntaxNode) {
280 self.imp.assert_contains_node(node) 280 self.imp.assert_contains_node(node)
281 } 281 }
282
283 pub fn is_unsafe_pat(&self, pat: &ast::Pat) -> bool {
284 let ty = (|| {
285 let parent = match pat {
286 ast::Pat::BindPat(bind_pat) => bind_pat.syntax().parent()?,
287 _ => return None,
288 };
289
290 // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
291 // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
292 // so this tries to lookup the `BindPat` anywhere along that structure to the
293 // `RecordPat` so we can get the containing type.
294 let record_pat = ast::RecordFieldPat::cast(parent.clone())
295 .and_then(|record_pat| record_pat.syntax().parent())
296 .or_else(|| Some(parent.clone()))
297 .and_then(|parent| {
298 ast::RecordFieldPatList::cast(parent)?
299 .syntax()
300 .parent()
301 .and_then(ast::RecordPat::cast)
302 });
303
304 // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
305 // this is initialized from a `FieldExpr`.
306 if let Some(record_pat) = record_pat {
307 self.type_of_pat(&ast::Pat::RecordPat(record_pat))
308 } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
309 let field_expr = match let_stmt.initializer()? {
310 ast::Expr::FieldExpr(field_expr) => field_expr,
311 _ => return None,
312 };
313
314 self.type_of_expr(&field_expr.expr()?)
315 } else {
316 None
317 }
318 })();
319
320 // Binding a reference to a packed type is possibly unsafe.
321 ty.map(|ty| ty.is_packed(self.db)).unwrap_or(false)
322 }
282} 323}
283 324
284impl<'db> SemanticsImpl<'db> { 325impl<'db> SemanticsImpl<'db> {
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs
index 4ba694480..35c3a9140 100644
--- a/crates/ra_hir_def/src/adt.rs
+++ b/crates/ra_hir_def/src/adt.rs
@@ -12,11 +12,9 @@ use ra_syntax::ast::{self, NameOwner, VisibilityOwner};
12use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}; 12use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
13 13
14use crate::{ 14use crate::{
15 attr::{Attr, AttrInput},
16 body::{CfgExpander, LowerCtx}, 15 body::{CfgExpander, LowerCtx},
17 db::DefDatabase, 16 db::DefDatabase,
18 item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem}, 17 item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem},
19 path::{ModPath, PathKind},
20 src::HasChildSource, 18 src::HasChildSource,
21 src::HasSource, 19 src::HasSource,
22 trace::Trace, 20 trace::Trace,
@@ -69,21 +67,7 @@ pub enum ReprKind {
69} 67}
70 68
71fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> { 69fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> {
72 item_tree.attrs(of).iter().find_map(|a| { 70 item_tree.attrs(of).by_key("repr").tt_values().find_map(parse_repr_tt)
73 if let Attr {
74 path: ModPath { kind: PathKind::Plain, segments },
75 input: Some(AttrInput::TokenTree(subtree)),
76 } = a
77 {
78 if segments.len() == 1 && segments[0].to_string() == "repr" {
79 parse_repr_tt(subtree)
80 } else {
81 None
82 }
83 } else {
84 None
85 }
86 })
87} 71}
88 72
89fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> { 73fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
@@ -93,11 +77,8 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
93 } 77 }
94 78
95 let mut it = tt.token_trees.iter(); 79 let mut it = tt.token_trees.iter();
96 match it.next() { 80 match it.next()? {
97 None => None, 81 TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed),
98 Some(TokenTree::Leaf(Leaf::Ident(ident))) if ident.text == "packed" => {
99 Some(ReprKind::Packed)
100 }
101 _ => Some(ReprKind::Other), 82 _ => Some(ReprKind::Other),
102 } 83 }
103} 84}
diff --git a/crates/ra_ide/src/call_info.rs.orig b/crates/ra_ide/src/call_info.rs.orig
new file mode 100644
index 000000000..0e04c0b60
--- /dev/null
+++ b/crates/ra_ide/src/call_info.rs.orig
@@ -0,0 +1,769 @@
1//! FIXME: write short doc here
2use either::Either;
3use hir::{Docs, HirDisplay, Semantics, Type};
4use ra_ide_db::RootDatabase;
5use ra_syntax::{
6 ast::{self, ArgListOwner},
7 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
8};
9use stdx::format_to;
10use test_utils::mark;
11
12use crate::FilePosition;
13
14/// Contains information about a call site. Specifically the
15/// `FunctionSignature`and current parameter.
16#[derive(Debug)]
17pub struct CallInfo {
18 pub doc: Option<String>,
19 pub signature: String,
20 pub active_parameter: Option<usize>,
21 parameters: Vec<TextRange>,
22}
23
24impl CallInfo {
25 pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
26 self.parameters.iter().map(move |&it| &self.signature[it])
27 }
28 pub fn parameter_ranges(&self) -> &[TextRange] {
29 &self.parameters
30 }
31 fn push_param(&mut self, param: &str) {
32 if !self.signature.ends_with('(') {
33 self.signature.push_str(", ");
34 }
35 let start = TextSize::of(&self.signature);
36 self.signature.push_str(param);
37 let end = TextSize::of(&self.signature);
38 self.parameters.push(TextRange::new(start, end))
39 }
40}
41
42/// Computes parameter information for the given call expression.
43pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
44 let sema = Semantics::new(db);
45 let file = sema.parse(position.file_id);
46 let file = file.syntax();
47 let token = file.token_at_offset(position.offset).next()?;
48 let token = sema.descend_into_macros(token);
49
50 let (callable, active_parameter) = call_info_impl(&sema, token)?;
51
52 let mut res =
53 CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
54
55 match callable.kind() {
56 hir::CallableKind::Function(func) => {
57 res.doc = func.docs(db).map(|it| it.as_str().to_string());
58 format_to!(res.signature, "fn {}", func.name(db));
59 }
60 hir::CallableKind::TupleStruct(strukt) => {
61 res.doc = strukt.docs(db).map(|it| it.as_str().to_string());
62 format_to!(res.signature, "struct {}", strukt.name(db));
63 }
64 hir::CallableKind::TupleEnumVariant(variant) => {
65 res.doc = variant.docs(db).map(|it| it.as_str().to_string());
66 format_to!(
67 res.signature,
68 "enum {}::{}",
69 variant.parent_enum(db).name(db),
70 variant.name(db)
71 );
72 }
73 hir::CallableKind::Closure => (),
74 }
75
76 res.signature.push('(');
77 {
78 if let Some(self_param) = callable.receiver_param(db) {
79 format_to!(res.signature, "{}", self_param)
80 }
81 let mut buf = String::new();
82 for (pat, ty) in callable.params(db) {
83 buf.clear();
84 if let Some(pat) = pat {
85 match pat {
86 Either::Left(_self) => format_to!(buf, "self: "),
87 Either::Right(pat) => format_to!(buf, "{}: ", pat),
88 }
89 }
90 format_to!(buf, "{}", ty.display(db));
91 res.push_param(&buf);
92 }
93 }
94 res.signature.push(')');
95
96 match callable.kind() {
97 hir::CallableKind::Function(_) | hir::CallableKind::Closure => {
98 let ret_type = callable.return_type();
99 if !ret_type.is_unit() {
100 format_to!(res.signature, " -> {}", ret_type.display(db));
101 }
102 }
103 hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
104 }
105 Some(res)
106}
107
108fn call_info_impl(
109 sema: &Semantics<RootDatabase>,
110 token: SyntaxToken,
111) -> Option<(hir::Callable, Option<usize>)> {
112 // Find the calling expression and it's NameRef
113 let calling_node = FnCallNode::with_node(&token.parent())?;
114
115<<<<<<< HEAD
116 let callable = match &calling_node {
117 FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?,
118 FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
119 };
120 let active_param = if let Some(arg_list) = calling_node.arg_list() {
121 // Number of arguments specified at the call site
122 let num_args_at_callsite = arg_list.args().count();
123
124 let arg_list_range = arg_list.syntax().text_range();
125 if !arg_list_range.contains_inclusive(token.text_range().start()) {
126 mark::hit!(call_info_bad_offset);
127 return None;
128=======
129 let (mut call_info, has_self) = match &calling_node {
130 FnCallNode::CallExpr(call) => {
131 //FIXME: Type::as_callable is broken
132 let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?;
133 match callable_def {
134 hir::CallableDef::FunctionId(it) => {
135 let fn_def = it.into();
136 (CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db))
137 }
138 hir::CallableDef::StructId(it) => {
139 (CallInfo::with_struct(sema.db, it.into())?, false)
140 }
141 hir::CallableDef::EnumVariantId(it) => {
142 (CallInfo::with_enum_variant(sema.db, it.into())?, false)
143 }
144 }
145 }
146 FnCallNode::MethodCallExpr(method_call) => {
147 let function = sema.resolve_method_call(&method_call)?;
148 (CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db))
149 }
150 FnCallNode::MacroCallExpr(macro_call) => {
151 let macro_def = sema.resolve_macro_call(&macro_call)?;
152 (CallInfo::with_macro(sema.db, macro_def)?, false)
153>>>>>>> Revert function structs back to using bool to track self param, use first param for self information in syntax highlighting instead
154 }
155 let param = std::cmp::min(
156 num_args_at_callsite,
157 arg_list
158 .args()
159 .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
160 .count(),
161 );
162
163 Some(param)
164 } else {
165 None
166 };
167 Some((callable, active_param))
168}
169
170#[derive(Debug)]
171pub(crate) struct ActiveParameter {
172 pub(crate) ty: Type,
173 pub(crate) name: String,
174}
175
176impl ActiveParameter {
177 pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
178 let sema = Semantics::new(db);
179 let file = sema.parse(position.file_id);
180 let file = file.syntax();
181 let token = file.token_at_offset(position.offset).next()?;
182 let token = sema.descend_into_macros(token);
183 Self::at_token(&sema, token)
184 }
185
186 pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
187 let (signature, active_parameter) = call_info_impl(&sema, token)?;
188
189 let idx = active_parameter?;
190 let mut params = signature.params(sema.db);
191 if !(idx < params.len()) {
192 mark::hit!(too_many_arguments);
193 return None;
194 }
195 let (pat, ty) = params.swap_remove(idx);
196 let name = pat?.to_string();
197 Some(ActiveParameter { ty, name })
198 }
199}
200
201#[derive(Debug)]
202pub(crate) enum FnCallNode {
203 CallExpr(ast::CallExpr),
204 MethodCallExpr(ast::MethodCallExpr),
205}
206
207impl FnCallNode {
208 fn with_node(syntax: &SyntaxNode) -> Option<FnCallNode> {
209 syntax.ancestors().find_map(|node| {
210 match_ast! {
211 match node {
212 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
213 ast::MethodCallExpr(it) => {
214 let arg_list = it.arg_list()?;
215 if !arg_list.syntax().text_range().contains_range(syntax.text_range()) {
216 return None;
217 }
218 Some(FnCallNode::MethodCallExpr(it))
219 },
220 _ => None,
221 }
222 }
223 })
224 }
225
226 pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option<FnCallNode> {
227 match_ast! {
228 match node {
229 ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
230 ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
231 _ => None,
232 }
233 }
234 }
235
236 pub(crate) fn name_ref(&self) -> Option<ast::NameRef> {
237 match self {
238 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
239 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
240 _ => return None,
241 }),
242
243 FnCallNode::MethodCallExpr(call_expr) => {
244 call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
245 }
246 }
247 }
248
249 fn arg_list(&self) -> Option<ast::ArgList> {
250 match self {
251 FnCallNode::CallExpr(expr) => expr.arg_list(),
252 FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
253 }
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use expect::{expect, Expect};
260 use test_utils::mark;
261
262 use crate::mock_analysis::analysis_and_position;
263
264 fn check(ra_fixture: &str, expect: Expect) {
265 let (analysis, position) = analysis_and_position(ra_fixture);
266 let call_info = analysis.call_info(position).unwrap();
267 let actual = match call_info {
268 Some(call_info) => {
269 let docs = match &call_info.doc {
270 None => "".to_string(),
271 Some(docs) => format!("{}\n------\n", docs.as_str()),
272 };
273 let params = call_info
274 .parameter_labels()
275 .enumerate()
276 .map(|(i, param)| {
277 if Some(i) == call_info.active_parameter {
278 format!("<{}>", param)
279 } else {
280 param.to_string()
281 }
282 })
283 .collect::<Vec<_>>()
284 .join(", ");
285 format!("{}{}\n({})\n", docs, call_info.signature, params)
286 }
287 None => String::new(),
288 };
289 expect.assert_eq(&actual);
290 }
291
292 #[test]
293 fn test_fn_signature_two_args() {
294 check(
295 r#"
296fn foo(x: u32, y: u32) -> u32 {x + y}
297fn bar() { foo(<|>3, ); }
298"#,
299 expect![[r#"
300 fn foo(x: u32, y: u32) -> u32
301 (<x: u32>, y: u32)
302 "#]],
303 );
304 check(
305 r#"
306fn foo(x: u32, y: u32) -> u32 {x + y}
307fn bar() { foo(3<|>, ); }
308"#,
309 expect![[r#"
310 fn foo(x: u32, y: u32) -> u32
311 (<x: u32>, y: u32)
312 "#]],
313 );
314 check(
315 r#"
316fn foo(x: u32, y: u32) -> u32 {x + y}
317fn bar() { foo(3,<|> ); }
318"#,
319 expect![[r#"
320 fn foo(x: u32, y: u32) -> u32
321 (x: u32, <y: u32>)
322 "#]],
323 );
324 check(
325 r#"
326fn foo(x: u32, y: u32) -> u32 {x + y}
327fn bar() { foo(3, <|>); }
328"#,
329 expect![[r#"
330 fn foo(x: u32, y: u32) -> u32
331 (x: u32, <y: u32>)
332 "#]],
333 );
334 }
335
336 #[test]
337 fn test_fn_signature_two_args_empty() {
338 check(
339 r#"
340fn foo(x: u32, y: u32) -> u32 {x + y}
341fn bar() { foo(<|>); }
342"#,
343 expect![[r#"
344 fn foo(x: u32, y: u32) -> u32
345 (<x: u32>, y: u32)
346 "#]],
347 );
348 }
349
350 #[test]
351 fn test_fn_signature_two_args_first_generics() {
352 check(
353 r#"
354fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
355 where T: Copy + Display, U: Debug
356{ x + y }
357
358fn bar() { foo(<|>3, ); }
359"#,
360 expect![[r#"
361 fn foo(x: i32, y: {unknown}) -> u32
362 (<x: i32>, y: {unknown})
363 "#]],
364 );
365 }
366
367 #[test]
368 fn test_fn_signature_no_params() {
369 check(
370 r#"
371fn foo<T>() -> T where T: Copy + Display {}
372fn bar() { foo(<|>); }
373"#,
374 expect![[r#"
375 fn foo() -> {unknown}
376 ()
377 "#]],
378 );
379 }
380
381 #[test]
382 fn test_fn_signature_for_impl() {
383 check(
384 r#"
385struct F;
386impl F { pub fn new() { } }
387fn bar() {
388 let _ : F = F::new(<|>);
389}
390"#,
391 expect![[r#"
392 fn new()
393 ()
394 "#]],
395 );
396 }
397
398 #[test]
399 fn test_fn_signature_for_method_self() {
400 check(
401 r#"
402struct S;
403impl S { pub fn do_it(&self) {} }
404
405fn bar() {
406 let s: S = S;
407 s.do_it(<|>);
408}
409"#,
410 expect![[r#"
411 fn do_it(&self)
412 ()
413 "#]],
414 );
415 }
416
417 #[test]
418 fn test_fn_signature_for_method_with_arg() {
419 check(
420 r#"
421struct S;
422impl S {
423 fn foo(&self, x: i32) {}
424}
425
426fn main() { S.foo(<|>); }
427"#,
428 expect![[r#"
429 fn foo(&self, x: i32)
430 (<x: i32>)
431 "#]],
432 );
433 }
434
435 #[test]
436 fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
437 check(
438 r#"
439struct S;
440impl S {
441 fn foo(&self, x: i32) {}
442}
443
444fn main() { S::foo(<|>); }
445"#,
446 expect![[r#"
447 fn foo(self: &S, x: i32)
448 (<self: &S>, x: i32)
449 "#]],
450 );
451 }
452
453 #[test]
454 fn test_fn_signature_with_docs_simple() {
455 check(
456 r#"
457/// test
458// non-doc-comment
459fn foo(j: u32) -> u32 {
460 j
461}
462
463fn bar() {
464 let _ = foo(<|>);
465}
466"#,
467 expect![[r#"
468 test
469 ------
470 fn foo(j: u32) -> u32
471 (<j: u32>)
472 "#]],
473 );
474 }
475
476 #[test]
477 fn test_fn_signature_with_docs() {
478 check(
479 r#"
480/// Adds one to the number given.
481///
482/// # Examples
483///
484/// ```
485/// let five = 5;
486///
487/// assert_eq!(6, my_crate::add_one(5));
488/// ```
489pub fn add_one(x: i32) -> i32 {
490 x + 1
491}
492
493pub fn do() {
494 add_one(<|>
495}"#,
496 expect![[r##"
497 Adds one to the number given.
498
499 # Examples
500
501 ```
502 let five = 5;
503
504 assert_eq!(6, my_crate::add_one(5));
505 ```
506 ------
507 fn add_one(x: i32) -> i32
508 (<x: i32>)
509 "##]],
510 );
511 }
512
513 #[test]
514 fn test_fn_signature_with_docs_impl() {
515 check(
516 r#"
517struct addr;
518impl addr {
519 /// Adds one to the number given.
520 ///
521 /// # Examples
522 ///
523 /// ```
524 /// let five = 5;
525 ///
526 /// assert_eq!(6, my_crate::add_one(5));
527 /// ```
528 pub fn add_one(x: i32) -> i32 {
529 x + 1
530 }
531}
532
533pub fn do_it() {
534 addr {};
535 addr::add_one(<|>);
536}
537"#,
538 expect![[r##"
539 Adds one to the number given.
540
541 # Examples
542
543 ```
544 let five = 5;
545
546 assert_eq!(6, my_crate::add_one(5));
547 ```
548 ------
549 fn add_one(x: i32) -> i32
550 (<x: i32>)
551 "##]],
552 );
553 }
554
555 #[test]
556 fn test_fn_signature_with_docs_from_actix() {
557 check(
558 r#"
559struct WriteHandler<E>;
560
561impl<E> WriteHandler<E> {
562 /// Method is called when writer emits error.
563 ///
564 /// If this method returns `ErrorAction::Continue` writer processing
565 /// continues otherwise stream processing stops.
566 fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
567 Running::Stop
568 }
569
570 /// Method is called when writer finishes.
571 ///
572 /// By default this method stops actor's `Context`.
573 fn finished(&mut self, ctx: &mut Self::Context) {
574 ctx.stop()
575 }
576}
577
578pub fn foo(mut r: WriteHandler<()>) {
579 r.finished(<|>);
580}
581"#,
582 expect![[r#"
583 Method is called when writer finishes.
584
585 By default this method stops actor's `Context`.
586 ------
587 fn finished(&mut self, ctx: &mut {unknown})
588 (<ctx: &mut {unknown}>)
589 "#]],
590 );
591 }
592
593 #[test]
594 fn call_info_bad_offset() {
595 mark::check!(call_info_bad_offset);
596 check(
597 r#"
598fn foo(x: u32, y: u32) -> u32 {x + y}
599fn bar() { foo <|> (3, ); }
600"#,
601 expect![[""]],
602 );
603 }
604
605 #[test]
606 fn test_nested_method_in_lambda() {
607 check(
608 r#"
609struct Foo;
610impl Foo { fn bar(&self, _: u32) { } }
611
612fn bar(_: u32) { }
613
614fn main() {
615 let foo = Foo;
616 std::thread::spawn(move || foo.bar(<|>));
617}
618"#,
619 expect![[r#"
620 fn bar(&self, _: u32)
621 (<_: u32>)
622 "#]],
623 );
624 }
625
626 #[test]
627 fn works_for_tuple_structs() {
628 check(
629 r#"
630/// A cool tuple struct
631struct S(u32, i32);
632fn main() {
633 let s = S(0, <|>);
634}
635"#,
636 expect![[r#"
637 A cool tuple struct
638 ------
639 struct S(u32, i32)
640 (u32, <i32>)
641 "#]],
642 );
643 }
644
645 #[test]
646 fn generic_struct() {
647 check(
648 r#"
649struct S<T>(T);
650fn main() {
651 let s = S(<|>);
652}
653"#,
654 expect![[r#"
655 struct S({unknown})
656 (<{unknown}>)
657 "#]],
658 );
659 }
660
661 #[test]
662 fn works_for_enum_variants() {
663 check(
664 r#"
665enum E {
666 /// A Variant
667 A(i32),
668 /// Another
669 B,
670 /// And C
671 C { a: i32, b: i32 }
672}
673
674fn main() {
675 let a = E::A(<|>);
676}
677"#,
678 expect![[r#"
679 A Variant
680 ------
681 enum E::A(i32)
682 (<i32>)
683 "#]],
684 );
685 }
686
687 #[test]
688 fn cant_call_struct_record() {
689 check(
690 r#"
691struct S { x: u32, y: i32 }
692fn main() {
693 let s = S(<|>);
694}
695"#,
696 expect![[""]],
697 );
698 }
699
700 #[test]
701 fn cant_call_enum_record() {
702 check(
703 r#"
704enum E {
705 /// A Variant
706 A(i32),
707 /// Another
708 B,
709 /// And C
710 C { a: i32, b: i32 }
711}
712
713fn main() {
714 let a = E::C(<|>);
715}
716"#,
717 expect![[""]],
718 );
719 }
720
721 #[test]
722 fn fn_signature_for_call_in_macro() {
723 check(
724 r#"
725macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
726fn foo() { }
727id! {
728 fn bar() { foo(<|>); }
729}
730"#,
731 expect![[r#"
732 fn foo()
733 ()
734 "#]],
735 );
736 }
737
738 #[test]
739 fn call_info_for_lambdas() {
740 check(
741 r#"
742struct S;
743fn foo(s: S) -> i32 { 92 }
744fn main() {
745 (|s| foo(s))(<|>)
746}
747 "#,
748 expect![[r#"
749 (S) -> i32
750 (<S>)
751 "#]],
752 )
753 }
754
755 #[test]
756 fn call_info_for_fn_ptr() {
757 check(
758 r#"
759fn main(f: fn(i32, f64) -> char) {
760 f(0, <|>)
761}
762 "#,
763 expect![[r#"
764 (i32, f64) -> char
765 (i32, <f64>)
766 "#]],
767 )
768 }
769}
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index d5a5f69cc..cf93205b6 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -671,41 +671,11 @@ fn highlight_element(
671 T![ref] => { 671 T![ref] => {
672 let modifier: Option<HighlightModifier> = (|| { 672 let modifier: Option<HighlightModifier> = (|| {
673 let bind_pat = element.parent().and_then(ast::BindPat::cast)?; 673 let bind_pat = element.parent().and_then(ast::BindPat::cast)?;
674 let parent = bind_pat.syntax().parent()?; 674 if sema.is_unsafe_pat(&ast::Pat::BindPat(bind_pat)) {
675 675 Some(HighlightModifier::Unsafe)
676 let ty = if let Some(pat_list) =
677 ast::RecordFieldPatList::cast(parent.clone())
678 {
679 let record_pat =
680 pat_list.syntax().parent().and_then(ast::RecordPat::cast)?;
681 sema.type_of_pat(&ast::Pat::RecordPat(record_pat))
682 } else if let Some(let_stmt) = ast::LetStmt::cast(parent.clone()) {
683 let field_expr =
684 if let ast::Expr::FieldExpr(field_expr) = let_stmt.initializer()? {
685 field_expr
686 } else {
687 return None;
688 };
689
690 sema.type_of_expr(&field_expr.expr()?)
691 } else if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent) {
692 let record_pat = record_field_pat
693 .syntax()
694 .parent()
695 .and_then(ast::RecordFieldPatList::cast)?
696 .syntax()
697 .parent()
698 .and_then(ast::RecordPat::cast)?;
699 sema.type_of_pat(&ast::Pat::RecordPat(record_pat))
700 } else { 676 } else {
701 None 677 None
702 }?;
703
704 if !ty.is_packed(db) {
705 return None;
706 } 678 }
707
708 Some(HighlightModifier::Unsafe)
709 })(); 679 })();
710 680
711 if let Some(modifier) = modifier { 681 if let Some(modifier) = modifier {