5385: Off by one error when determining the active param r=matklad a=matklad

closes #3615



bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-07-15 08:17:13 +00:00 committed by GitHub
commit 77425c21c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -93,7 +93,7 @@ fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Op
arg_list arg_list
.args() .args()
.take_while(|arg| { .take_while(|arg| {
arg.syntax().text_range().end() < token.text_range().start() arg.syntax().text_range().end() <= token.text_range().start()
}) })
.count(), .count(),
); );
@ -213,169 +213,187 @@ impl CallInfo {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use expect::{expect, Expect};
use test_utils::mark; use test_utils::mark;
use crate::mock_analysis::analysis_and_position; use crate::mock_analysis::analysis_and_position;
use super::*; fn check(ra_fixture: &str, expect: Expect) {
let (analysis, position) = analysis_and_position(ra_fixture);
// These are only used when testing let call_info = analysis.call_info(position).unwrap();
impl CallInfo { let actual = match call_info {
fn doc(&self) -> Option<hir::Documentation> { Some(call_info) => {
self.signature.doc.clone() let docs = match &call_info.signature.doc {
} None => "".to_string(),
Some(docs) => format!("{}\n------\n", docs.as_str()),
fn label(&self) -> String { };
self.signature.to_string() let params = call_info
} .parameters()
} .iter()
.enumerate()
fn call_info_helper(text: &str) -> Option<CallInfo> { .map(|(i, param)| {
let (analysis, position) = analysis_and_position(text); if Some(i) == call_info.active_parameter {
analysis.call_info(position).unwrap() format!("<{}>", param)
} } else {
param.clone()
fn call_info(text: &str) -> CallInfo { }
let info = call_info_helper(text); })
assert!(info.is_some()); .collect::<Vec<_>>()
info.unwrap() .join(", ");
} format!("{}{}\n({})\n", docs, call_info.signature, params)
}
fn no_call_info(text: &str) { None => String::new(),
let info = call_info_helper(text); };
assert!(info.is_none()); expect.assert_eq(&actual);
} }
#[test] #[test]
fn test_fn_signature_two_args_firstx() { fn test_fn_signature_two_args() {
let info = call_info( check(
r#"fn foo(x: u32, y: u32) -> u32 {x + y} r#"
fn bar() { foo(<|>3, ); }"#, fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(<|>3, ); }
"#,
expect![[r#"
fn foo(x: u32, y: u32) -> u32
(<x: u32>, y: u32)
"#]],
); );
check(
assert_eq!(info.parameters(), ["x: u32", "y: u32"]); r#"
assert_eq!(info.active_parameter, Some(0)); fn foo(x: u32, y: u32) -> u32 {x + y}
} fn bar() { foo(3<|>, ); }
"#,
#[test] expect![[r#"
fn test_fn_signature_two_args_second() { fn foo(x: u32, y: u32) -> u32
let info = call_info( (<x: u32>, y: u32)
r#"fn foo(x: u32, y: u32) -> u32 {x + y} "#]],
fn bar() { foo(3, <|>); }"#, );
check(
r#"
fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3,<|> ); }
"#,
expect![[r#"
fn foo(x: u32, y: u32) -> u32
(x: u32, <y: u32>)
"#]],
);
check(
r#"
fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3, <|>); }
"#,
expect![[r#"
fn foo(x: u32, y: u32) -> u32
(x: u32, <y: u32>)
"#]],
); );
assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
assert_eq!(info.active_parameter, Some(1));
} }
#[test] #[test]
fn test_fn_signature_two_args_empty() { fn test_fn_signature_two_args_empty() {
let info = call_info( check(
r#"fn foo(x: u32, y: u32) -> u32 {x + y} r#"
fn bar() { foo(<|>); }"#, fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(<|>); }
"#,
expect![[r#"
fn foo(x: u32, y: u32) -> u32
(<x: u32>, y: u32)
"#]],
); );
assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
assert_eq!(info.active_parameter, Some(0));
} }
#[test] #[test]
fn test_fn_signature_two_args_first_generics() { fn test_fn_signature_two_args_first_generics() {
let info = call_info( check(
r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y}
fn bar() { foo(<|>3, ); }"#,
);
assert_eq!(info.parameters(), ["x: T", "y: U"]);
assert_eq!(
info.label(),
r#" r#"
fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
where T: Copy + Display, where T: Copy + Display, U: Debug
U: Debug { x + y }
"#
.trim() fn bar() { foo(<|>3, ); }
"#,
expect![[r#"
fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
where T: Copy + Display,
U: Debug
(<x: T>, y: U)
"#]],
); );
assert_eq!(info.active_parameter, Some(0));
} }
#[test] #[test]
fn test_fn_signature_no_params() { fn test_fn_signature_no_params() {
let info = call_info( check(
r#"fn foo<T>() -> T where T: Copy + Display {}
fn bar() { foo(<|>); }"#,
);
assert!(info.parameters().is_empty());
assert_eq!(
info.label(),
r#" r#"
fn foo<T>() -> T fn foo<T>() -> T where T: Copy + Display {}
where T: Copy + Display fn bar() { foo(<|>); }
"# "#,
.trim() expect![[r#"
fn foo<T>() -> T
where T: Copy + Display
()
"#]],
); );
assert!(info.active_parameter.is_none());
} }
#[test] #[test]
fn test_fn_signature_for_impl() { fn test_fn_signature_for_impl() {
let info = call_info( check(
r#"struct F; impl F { pub fn new() { F{}} } r#"
fn bar() {let _ : F = F::new(<|>);}"#, struct F; impl F { pub fn new() { F{}} }
fn bar() {let _ : F = F::new(<|>);}
"#,
expect![[r#"
pub fn new()
()
"#]],
); );
assert!(info.parameters().is_empty());
assert_eq!(info.active_parameter, None);
} }
#[test] #[test]
fn test_fn_signature_for_method_self() { fn test_fn_signature_for_method_self() {
let info = call_info( check(
r#"struct F; r#"
impl F { struct S;
pub fn new() -> F{ impl S { pub fn do_it(&self) {} }
F{}
}
pub fn do_it(&self) {}
}
fn bar() { fn bar() {
let f : F = F::new(); let s: S = S;
f.do_it(<|>); s.do_it(<|>);
}"#, }
"#,
expect![[r#"
pub fn do_it(&self)
(&self)
"#]],
); );
assert_eq!(info.parameters(), ["&self"]);
assert_eq!(info.active_parameter, None);
} }
#[test] #[test]
fn test_fn_signature_for_method_with_arg() { fn test_fn_signature_for_method_with_arg() {
let info = call_info( check(
r#"struct F; r#"
impl F { struct S;
pub fn new() -> F{ impl S { pub fn do_it(&self, x: i32) {} }
F{}
}
pub fn do_it(&self, x: i32) {}
}
fn bar() { fn bar() {
let f : F = F::new(); let s: S = S;
f.do_it(<|>); s.do_it(<|>);
}"#, }
"#,
expect![[r#"
pub fn do_it(&self, x: i32)
(&self, <x: i32>)
"#]],
); );
assert_eq!(info.parameters(), ["&self", "x: i32"]);
assert_eq!(info.active_parameter, Some(1));
} }
#[test] #[test]
fn test_fn_signature_with_docs_simple() { fn test_fn_signature_with_docs_simple() {
let info = call_info( check(
r#" r#"
/// test /// test
// non-doc-comment // non-doc-comment
@ -387,17 +405,18 @@ fn bar() {
let _ = foo(<|>); let _ = foo(<|>);
} }
"#, "#,
expect![[r#"
test
------
fn foo(j: u32) -> u32
(<j: u32>)
"#]],
); );
assert_eq!(info.parameters(), ["j: u32"]);
assert_eq!(info.active_parameter, Some(0));
assert_eq!(info.label(), "fn foo(j: u32) -> u32");
assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string()));
} }
#[test] #[test]
fn test_fn_signature_with_docs() { fn test_fn_signature_with_docs() {
let info = call_info( check(
r#" r#"
/// Adds one to the number given. /// Adds one to the number given.
/// ///
@ -415,31 +434,26 @@ pub fn add_one(x: i32) -> i32 {
pub fn do() { pub fn do() {
add_one(<|> add_one(<|>
}"#, }"#,
); expect![[r##"
Adds one to the number given.
assert_eq!(info.parameters(), ["x: i32"]); # Examples
assert_eq!(info.active_parameter, Some(0));
assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
assert_eq!(
info.doc().map(|it| it.into()),
Some(
r#"Adds one to the number given.
# Examples ```
let five = 5;
``` assert_eq!(6, my_crate::add_one(5));
let five = 5; ```
------
assert_eq!(6, my_crate::add_one(5)); pub fn add_one(x: i32) -> i32
```"# (<x: i32>)
.to_string() "##]],
)
); );
} }
#[test] #[test]
fn test_fn_signature_with_docs_impl() { fn test_fn_signature_with_docs_impl() {
let info = call_info( check(
r#" r#"
struct addr; struct addr;
impl addr { impl addr {
@ -460,32 +474,28 @@ impl addr {
pub fn do_it() { pub fn do_it() {
addr {}; addr {};
addr::add_one(<|>); addr::add_one(<|>);
}"#, }
); "#,
expect![[r##"
Adds one to the number given.
assert_eq!(info.parameters(), ["x: i32"]); # Examples
assert_eq!(info.active_parameter, Some(0));
assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
assert_eq!(
info.doc().map(|it| it.into()),
Some(
r#"Adds one to the number given.
# Examples ```
let five = 5;
``` assert_eq!(6, my_crate::add_one(5));
let five = 5; ```
------
assert_eq!(6, my_crate::add_one(5)); pub fn add_one(x: i32) -> i32
```"# (<x: i32>)
.to_string() "##]],
)
); );
} }
#[test] #[test]
fn test_fn_signature_with_docs_from_actix() { fn test_fn_signature_with_docs_from_actix() {
let info = call_info( check(
r#" r#"
struct WriteHandler<E>; struct WriteHandler<E>;
@ -509,101 +519,102 @@ impl<E> WriteHandler<E> {
pub fn foo(mut r: WriteHandler<()>) { pub fn foo(mut r: WriteHandler<()>) {
r.finished(<|>); r.finished(<|>);
} }
"#, "#,
); expect![[r#"
Method is called when writer finishes.
assert_eq!(info.label(), "fn finished(&mut self, ctx: &mut Self::Context)".to_string()); By default this method stops actor's `Context`.
assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]); ------
assert_eq!(info.active_parameter, Some(1)); fn finished(&mut self, ctx: &mut Self::Context)
assert_eq!( (&mut self, <ctx: &mut Self::Context>)
info.doc().map(|it| it.into()), "#]],
Some(
r#"Method is called when writer finishes.
By default this method stops actor's `Context`."#
.to_string()
)
); );
} }
#[test] #[test]
fn call_info_bad_offset() { fn call_info_bad_offset() {
mark::check!(call_info_bad_offset); mark::check!(call_info_bad_offset);
let (analysis, position) = analysis_and_position( check(
r#"fn foo(x: u32, y: u32) -> u32 {x + y} r#"
fn bar() { foo <|> (3, ); }"#, fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo <|> (3, ); }
"#,
expect![[""]],
); );
let call_info = analysis.call_info(position).unwrap();
assert!(call_info.is_none());
} }
#[test] #[test]
fn test_nested_method_in_lamba() { fn test_nested_method_in_lambda() {
let info = call_info( check(
r#"struct Foo; r#"
struct Foo;
impl Foo { impl Foo { fn bar(&self, _: u32) { } }
fn bar(&self, _: u32) { }
}
fn bar(_: u32) { } fn bar(_: u32) { }
fn main() { fn main() {
let foo = Foo; let foo = Foo;
std::thread::spawn(move || foo.bar(<|>)); std::thread::spawn(move || foo.bar(<|>));
}"#, }
"#,
expect![[r#"
fn bar(&self, _: u32)
(&self, <_: u32>)
"#]],
); );
assert_eq!(info.parameters(), ["&self", "_: u32"]);
assert_eq!(info.active_parameter, Some(1));
assert_eq!(info.label(), "fn bar(&self, _: u32)");
} }
#[test] #[test]
fn works_for_tuple_structs() { fn works_for_tuple_structs() {
let info = call_info( check(
r#" r#"
/// A cool tuple struct /// A cool tuple struct
struct TS(u32, i32); struct TS(u32, i32);
fn main() { fn main() {
let s = TS(0, <|>); let s = TS(0, <|>);
}"#, }
"#,
expect![[r#"
A cool tuple struct
------
struct TS(u32, i32) -> TS
(u32, <i32>)
"#]],
); );
assert_eq!(info.label(), "struct TS(u32, i32) -> TS");
assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string()));
assert_eq!(info.active_parameter, Some(1));
} }
#[test] #[test]
fn generic_struct() { fn generic_struct() {
let info = call_info( check(
r#" r#"
struct TS<T>(T); struct TS<T>(T);
fn main() { fn main() {
let s = TS(<|>); let s = TS(<|>);
}"#, }
"#,
expect![[r#"
struct TS<T>(T) -> TS
(<T>)
"#]],
); );
assert_eq!(info.label(), "struct TS<T>(T) -> TS");
assert_eq!(info.active_parameter, Some(0));
} }
#[test] #[test]
fn cant_call_named_structs() { fn cant_call_named_structs() {
no_call_info( check(
r#" r#"
struct TS { x: u32, y: i32 } struct TS { x: u32, y: i32 }
fn main() { fn main() {
let s = TS(<|>); let s = TS(<|>);
}"#, }
"#,
expect![[""]],
); );
} }
#[test] #[test]
fn works_for_enum_variants() { fn works_for_enum_variants() {
let info = call_info( check(
r#" r#"
enum E { enum E {
/// A Variant /// A Variant
@ -617,17 +628,19 @@ enum E {
fn main() { fn main() {
let a = E::A(<|>); let a = E::A(<|>);
} }
"#, "#,
expect![[r#"
A Variant
------
E::A(0: i32)
(<0: i32>)
"#]],
); );
assert_eq!(info.label(), "E::A(0: i32)");
assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string()));
assert_eq!(info.active_parameter, Some(0));
} }
#[test] #[test]
fn cant_call_enum_records() { fn cant_call_enum_records() {
no_call_info( check(
r#" r#"
enum E { enum E {
/// A Variant /// A Variant
@ -641,13 +654,14 @@ enum E {
fn main() { fn main() {
let a = E::C(<|>); let a = E::C(<|>);
} }
"#, "#,
expect![[""]],
); );
} }
#[test] #[test]
fn fn_signature_for_macro() { fn fn_signature_for_macro() {
let info = call_info( check(
r#" r#"
/// empty macro /// empty macro
macro_rules! foo { macro_rules! foo {
@ -657,31 +671,30 @@ macro_rules! foo {
fn f() { fn f() {
foo!(<|>); foo!(<|>);
} }
"#, "#,
expect![[r#"
empty macro
------
foo!()
()
"#]],
); );
assert_eq!(info.label(), "foo!()");
assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string()));
} }
#[test] #[test]
fn fn_signature_for_call_in_macro() { fn fn_signature_for_call_in_macro() {
let info = call_info( check(
r#" r#"
macro_rules! id { macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
($($tt:tt)*) => { $($tt)* } fn foo() { }
} id! {
fn foo() { fn bar() { foo(<|>); }
}
} "#,
id! { expect![[r#"
fn bar() { fn foo()
foo(<|>); ()
} "#]],
}
"#,
); );
assert_eq!(info.label(), "fn foo()");
} }
} }