From b70ce559b8d3102c3fed3ecef8edef3038a5ceed Mon Sep 17 00:00:00 2001 From: Matt Hooper Date: Tue, 24 Mar 2020 19:33:00 +0100 Subject: [PATCH] Added more unit tests --- crates/ra_ide/src/inlay_hints.rs | 235 +++++++++++++++++++++---------- 1 file changed, 159 insertions(+), 76 deletions(-) diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 2353ad71fa..2939442067 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -70,6 +70,45 @@ pub(crate) fn inlay_hints( res } +fn get_chaining_hints( + acc: &mut Vec, + sema: &Semantics, + options: &InlayHintsOptions, + expr: ast::Expr, +) -> Option<()> { + if !options.chaining_hints { + return None; + } + + let ty = sema.type_of_expr(&expr)?; + let label = ty.display_truncated(sema.db, options.max_length).to_string(); + if ty.is_unknown() { + return None; + } + + let mut tokens = expr.syntax() + .siblings_with_tokens(Direction::Next) + .filter_map(NodeOrToken::into_token) + .filter(|t| match t.kind() { + SyntaxKind::WHITESPACE if !t.text().contains('\n') => false, + SyntaxKind::COMMENT => false, + _ => true, + }); + + // Chaining can be defined as an expression whose next sibling tokens are newline and dot + // Ignoring extra whitespace and comments + let next = tokens.next()?.kind(); + let next_next = tokens.next()?.kind(); + if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::ChainingHint, + label: label.into(), + }); + } + Some(()) +} + fn get_param_name_hints( acc: &mut Vec, sema: &Semantics, @@ -233,45 +272,6 @@ fn get_fn_signature(sema: &Semantics, expr: &ast::Expr) -> Option< } } -fn get_chaining_hints( - acc: &mut Vec, - sema: &Semantics, - options: &InlayHintsOptions, - expr: ast::Expr, -) -> Option<()> { - if !options.chaining_hints { - return None; - } - - let ty = sema.type_of_expr(&expr)?; - let label = ty.display_truncated(sema.db, options.max_length).to_string(); - if ty.is_unknown() { - return None; - } - - let mut tokens = expr.syntax() - .siblings_with_tokens(Direction::Next) - .filter_map(NodeOrToken::into_token) - .filter(|t| match t.kind() { - SyntaxKind::WHITESPACE if !t.text().contains('\n') => false, - SyntaxKind::COMMENT => false, - _ => true, - }); - - // Chaining can be defined as an expression whose next sibling tokens are newline and dot - // Ignoring extra whitespace and comments - let next = tokens.next()?.kind(); - let next_next = tokens.next()?.kind(); - if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { - acc.push(InlayHint { - range: expr.syntax().text_range(), - kind: InlayKind::ChainingHint, - label: label.into(), - }); - } - Some(()) -} - #[cfg(test)] mod tests { use crate::inlay_hints::InlayHintsOptions; @@ -279,43 +279,6 @@ mod tests { use crate::mock_analysis::single_file; - #[test] - fn generic_chaining_hints() { - let (analysis, file_id) = single_file( - r#" - struct A(T); - struct B(T); - struct C(T); - struct X(T, R); - - impl A { - fn new(t: T) -> Self { A(t) } - fn into_b(self) -> B { B(self.0) } - } - impl B { - fn into_c(self) -> C { C(self.0) } - } - fn test() { - let c = A::new(X(42, true)) - .into_b() // All the from A -> B -> C - .into_c(); - }"#, - ); - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" - [ - InlayHint { - range: [416; 465), - kind: ChainingHint, - label: "B>", - }, - InlayHint { - range: [416; 435), - kind: ChainingHint, - label: "A>", - }, - ]"###); - } - #[test] fn param_hints_only() { let (analysis, file_id) = single_file( @@ -1139,4 +1102,124 @@ fn main() { "### ); } + + #[test] + fn chaining_hints_ignore_comments() { + let (analysis, file_id) = single_file( + r#" + struct A(B); + impl A { fn into_b(self) -> B { self.0 } } + struct B(C) + impl B { fn into_c(self) -> C { self.0 } } + struct C; + + fn main() { + let c = A(B(C)) + .into_b() // This is a comment + .into_c(); + }"#, + ); + assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" + [ + InlayHint { + range: [231; 268), + kind: ChainingHint, + label: "B", + }, + InlayHint { + range: [231; 238), + kind: ChainingHint, + label: "A", + }, + ]"###); + } + + #[test] + fn chaining_hints_without_newlines() { + let (analysis, file_id) = single_file( + r#" + struct A(B); + impl A { fn into_b(self) -> B { self.0 } } + struct B(C) + impl B { fn into_c(self) -> C { self.0 } } + struct C; + + fn main() { + let c = A(B(C)).into_b().into_c(); + }"#, + ); + assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###); + } + + #[test] + fn struct_access_chaining_hints() { + let (analysis, file_id) = single_file( + r#" + struct A { pub b: B } + struct B { pub c: C } + struct C(pub bool); + + fn main() { + let x = A { b: B { c: C(true) } } + .b + .c + .0; + }"#, + ); + assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" + [ + InlayHint { + range: [150; 221), + kind: ChainingHint, + label: "C", + }, + InlayHint { + range: [150; 198), + kind: ChainingHint, + label: "B", + }, + InlayHint { + range: [150; 175), + kind: ChainingHint, + label: "A", + }, + ]"###); + } + + #[test] + fn generic_chaining_hints() { + let (analysis, file_id) = single_file( + r#" + struct A(T); + struct B(T); + struct C(T); + struct X(T, R); + + impl A { + fn new(t: T) -> Self { A(t) } + fn into_b(self) -> B { B(self.0) } + } + impl B { + fn into_c(self) -> C { C(self.0) } + } + fn main() { + let c = A::new(X(42, true)) + .into_b() + .into_c(); + }"#, + ); + assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" + [ + InlayHint { + range: [416; 465), + kind: ChainingHint, + label: "B>", + }, + InlayHint { + range: [416; 435), + kind: ChainingHint, + label: "A>", + }, + ]"###); + } }