Added more unit tests

This commit is contained in:
Matt Hooper 2020-03-24 19:33:00 +01:00
parent c1f9a1a1c5
commit b70ce559b8

View file

@ -70,6 +70,45 @@ pub(crate) fn inlay_hints(
res
}
fn get_chaining_hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<RootDatabase>,
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<InlayHint>,
sema: &Semantics<RootDatabase>,
@ -233,45 +272,6 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
}
}
fn get_chaining_hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<RootDatabase>,
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>(T);
struct B<T>(T);
struct C<T>(T);
struct X<T,R>(T, R);
impl<T> A<T> {
fn new(t: T) -> Self { A(t) }
fn into_b(self) -> B<T> { B(self.0) }
}
impl<T> B<T> {
fn into_c(self) -> C<T> { 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<X<i32, bool>>",
},
InlayHint {
range: [416; 435),
kind: ChainingHint,
label: "A<X<i32, bool>>",
},
]"###);
}
#[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>(T);
struct B<T>(T);
struct C<T>(T);
struct X<T,R>(T, R);
impl<T> A<T> {
fn new(t: T) -> Self { A(t) }
fn into_b(self) -> B<T> { B(self.0) }
}
impl<T> B<T> {
fn into_c(self) -> C<T> { 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<X<i32, bool>>",
},
InlayHint {
range: [416; 435),
kind: ChainingHint,
label: "A<X<i32, bool>>",
},
]"###);
}
}