mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-15 14:43:58 +00:00
489 lines
10 KiB
Rust
489 lines
10 KiB
Rust
use either::Either;
|
|
use hir::InFile;
|
|
use ide_db::FileRange;
|
|
use syntax::{
|
|
ast::{self, HasArgList},
|
|
AstNode, AstPtr,
|
|
};
|
|
|
|
use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
|
|
|
|
// Diagnostic: mismatched-tuple-struct-pat-arg-count
|
|
//
|
|
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
|
|
pub(crate) fn mismatched_tuple_struct_pat_arg_count(
|
|
ctx: &DiagnosticsContext<'_>,
|
|
d: &hir::MismatchedTupleStructPatArgCount,
|
|
) -> Diagnostic {
|
|
let s = if d.found == 1 { "" } else { "s" };
|
|
let s2 = if d.expected == 1 { "" } else { "s" };
|
|
let message = format!(
|
|
"this pattern has {} field{s}, but the corresponding tuple struct has {} field{s2}",
|
|
d.found, d.expected
|
|
);
|
|
Diagnostic::new(
|
|
DiagnosticCode::RustcHardError("E0023"),
|
|
message,
|
|
invalid_args_range(ctx, d.expr_or_pat, d.expected, d.found),
|
|
)
|
|
}
|
|
|
|
// Diagnostic: mismatched-arg-count
|
|
//
|
|
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
|
|
pub(crate) fn mismatched_arg_count(
|
|
ctx: &DiagnosticsContext<'_>,
|
|
d: &hir::MismatchedArgCount,
|
|
) -> Diagnostic {
|
|
let s = if d.expected == 1 { "" } else { "s" };
|
|
let message = format!("expected {} argument{s}, found {}", d.expected, d.found);
|
|
Diagnostic::new(
|
|
DiagnosticCode::RustcHardError("E0107"),
|
|
message,
|
|
invalid_args_range(ctx, d.call_expr.map(AstPtr::wrap_left), d.expected, d.found),
|
|
)
|
|
}
|
|
|
|
fn invalid_args_range(
|
|
ctx: &DiagnosticsContext<'_>,
|
|
source: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
|
|
expected: usize,
|
|
found: usize,
|
|
) -> FileRange {
|
|
adjusted_display_range(ctx, source, &|expr| {
|
|
let (text_range, r_paren_token, expected_arg) = match expr {
|
|
Either::Left(ast::Expr::CallExpr(call)) => {
|
|
let arg_list = call.arg_list()?;
|
|
(
|
|
arg_list.syntax().text_range(),
|
|
arg_list.r_paren_token(),
|
|
arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
|
|
)
|
|
}
|
|
Either::Left(ast::Expr::MethodCallExpr(call)) => {
|
|
let arg_list = call.arg_list()?;
|
|
(
|
|
arg_list.syntax().text_range(),
|
|
arg_list.r_paren_token(),
|
|
arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
|
|
)
|
|
}
|
|
Either::Right(ast::Pat::TupleStructPat(pat)) => {
|
|
let r_paren = pat.r_paren_token()?;
|
|
let l_paren = pat.l_paren_token()?;
|
|
(
|
|
l_paren.text_range().cover(r_paren.text_range()),
|
|
Some(r_paren),
|
|
pat.fields().nth(expected).map(|it| it.syntax().text_range()),
|
|
)
|
|
}
|
|
_ => return None,
|
|
};
|
|
if found < expected {
|
|
if found == 0 {
|
|
return Some(text_range);
|
|
}
|
|
if let Some(r_paren) = r_paren_token {
|
|
return Some(r_paren.text_range());
|
|
}
|
|
}
|
|
if expected < found {
|
|
if expected == 0 {
|
|
return Some(text_range);
|
|
}
|
|
let zip = expected_arg.zip(r_paren_token);
|
|
if let Some((arg, r_paren)) = zip {
|
|
return Some(arg.cover(r_paren.text_range()));
|
|
}
|
|
}
|
|
|
|
None
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::tests::check_diagnostics;
|
|
|
|
#[test]
|
|
fn simple_free_fn_zero() {
|
|
check_diagnostics(
|
|
r#"
|
|
fn zero() {}
|
|
fn f() { zero(1); }
|
|
//^^^ error: expected 0 arguments, found 1
|
|
"#,
|
|
);
|
|
|
|
check_diagnostics(
|
|
r#"
|
|
fn zero() {}
|
|
fn f() { zero(); }
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn simple_free_fn_one() {
|
|
check_diagnostics(
|
|
r#"
|
|
fn one(_arg: u8) {}
|
|
fn f() { one(); }
|
|
//^^ error: expected 1 argument, found 0
|
|
"#,
|
|
);
|
|
|
|
check_diagnostics(
|
|
r#"
|
|
fn one(_arg: u8) {}
|
|
fn f() { one(1); }
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn method_as_fn() {
|
|
check_diagnostics(
|
|
r#"
|
|
struct S;
|
|
impl S { fn method(&self) {} }
|
|
|
|
fn f() {
|
|
S::method();
|
|
} //^^ error: expected 1 argument, found 0
|
|
"#,
|
|
);
|
|
|
|
check_diagnostics(
|
|
r#"
|
|
struct S;
|
|
impl S { fn method(&self) {} }
|
|
|
|
fn f() {
|
|
S::method(&S);
|
|
S.method();
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn method_with_arg() {
|
|
check_diagnostics(
|
|
r#"
|
|
struct S;
|
|
impl S { fn method(&self, _arg: u8) {} }
|
|
|
|
fn f() {
|
|
S.method();
|
|
} //^^ error: expected 1 argument, found 0
|
|
"#,
|
|
);
|
|
|
|
check_diagnostics(
|
|
r#"
|
|
struct S;
|
|
impl S { fn method(&self, _arg: u8) {} }
|
|
|
|
fn f() {
|
|
S::method(&S, 0);
|
|
S.method(1);
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn method_unknown_receiver() {
|
|
// note: this is incorrect code, so there might be errors on this in the
|
|
// future, but we shouldn't emit an argument count diagnostic here
|
|
check_diagnostics(
|
|
r#"
|
|
trait Foo { fn method(&self, _arg: usize) {} }
|
|
|
|
fn f() {
|
|
let x;
|
|
x.method();
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn tuple_struct() {
|
|
check_diagnostics(
|
|
r#"
|
|
struct Tup(u8, u16);
|
|
fn f() {
|
|
Tup(0);
|
|
} //^ error: expected 2 arguments, found 1
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn enum_variant() {
|
|
check_diagnostics(
|
|
r#"
|
|
enum En { Variant(u8, u16), }
|
|
fn f() {
|
|
En::Variant(0);
|
|
} //^ error: expected 2 arguments, found 1
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn enum_variant_type_macro() {
|
|
check_diagnostics(
|
|
r#"
|
|
macro_rules! Type {
|
|
() => { u32 };
|
|
}
|
|
enum Foo {
|
|
Bar(Type![])
|
|
}
|
|
impl Foo {
|
|
fn new() {
|
|
Foo::Bar(0);
|
|
Foo::Bar(0, 1);
|
|
//^^ error: expected 1 argument, found 2
|
|
Foo::Bar();
|
|
//^^ error: expected 1 argument, found 0
|
|
}
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn rest_pat_in_macro_expansion() {
|
|
check_diagnostics(
|
|
r#"
|
|
// issue #17292
|
|
#![allow(dead_code)]
|
|
|
|
macro_rules! replace_with_2_dots {
|
|
( $( $input:tt )* ) => {
|
|
..
|
|
};
|
|
}
|
|
|
|
macro_rules! enum_str {
|
|
(
|
|
$(
|
|
$variant:ident (
|
|
$( $tfield:ty ),*
|
|
)
|
|
)
|
|
,
|
|
*
|
|
) => {
|
|
enum Foo {
|
|
$(
|
|
$variant ( $( $tfield ),* ),
|
|
)*
|
|
}
|
|
|
|
impl Foo {
|
|
fn variant_name_as_str(&self) -> &str {
|
|
match self {
|
|
$(
|
|
Self::$variant ( replace_with_2_dots!( $( $tfield ),* ) )
|
|
=> "",
|
|
)*
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
enum_str! {
|
|
TupleVariant1(i32),
|
|
TupleVariant2(),
|
|
TupleVariant3(i8,u8,i128)
|
|
}
|
|
"#,
|
|
);
|
|
|
|
check_diagnostics(
|
|
r#"
|
|
#![allow(dead_code)]
|
|
macro_rules! two_dots1 {
|
|
() => { .. };
|
|
}
|
|
|
|
macro_rules! two_dots2 {
|
|
() => { two_dots1!() };
|
|
}
|
|
|
|
fn test() {
|
|
let (_, _, two_dots1!()) = ((), 42);
|
|
let (_, two_dots2!(), _) = (1, true, 2, false, (), (), 3);
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn varargs() {
|
|
check_diagnostics(
|
|
r#"
|
|
extern "C" {
|
|
fn fixed(fixed: u8);
|
|
fn varargs(fixed: u8, ...);
|
|
fn varargs2(...);
|
|
}
|
|
|
|
fn f() {
|
|
unsafe {
|
|
fixed(0);
|
|
fixed(0, 1);
|
|
//^^ error: expected 1 argument, found 2
|
|
varargs(0);
|
|
varargs(0, 1);
|
|
varargs2();
|
|
varargs2(0);
|
|
varargs2(0, 1);
|
|
}
|
|
}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn arg_count_lambda() {
|
|
check_diagnostics(
|
|
r#"
|
|
fn main() {
|
|
let f = |()| ();
|
|
f();
|
|
//^^ error: expected 1 argument, found 0
|
|
f(());
|
|
f((), ());
|
|
//^^^ error: expected 1 argument, found 2
|
|
}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn cfgd_out_call_arguments() {
|
|
check_diagnostics(
|
|
r#"
|
|
struct C(#[cfg(FALSE)] ());
|
|
impl C {
|
|
fn new() -> Self {
|
|
Self(
|
|
#[cfg(FALSE)]
|
|
(),
|
|
)
|
|
}
|
|
|
|
fn method(&self) {}
|
|
}
|
|
|
|
fn main() {
|
|
C::new().method(#[cfg(FALSE)] 0);
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn cfgd_out_fn_params() {
|
|
check_diagnostics(
|
|
r#"
|
|
fn foo(#[cfg(NEVER)] x: ()) {}
|
|
|
|
struct S;
|
|
|
|
impl S {
|
|
fn method(#[cfg(NEVER)] self) {}
|
|
fn method2(#[cfg(NEVER)] self, _arg: u8) {}
|
|
fn method3(self, #[cfg(NEVER)] _arg: u8) {}
|
|
}
|
|
|
|
extern "C" {
|
|
fn fixed(fixed: u8, #[cfg(NEVER)] ...);
|
|
fn varargs(#[cfg(not(NEVER))] ...);
|
|
}
|
|
|
|
fn main() {
|
|
foo();
|
|
S::method();
|
|
S::method2(0);
|
|
S::method3(S);
|
|
S.method3();
|
|
unsafe {
|
|
fixed(0);
|
|
varargs(1, 2, 3);
|
|
}
|
|
}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn legacy_const_generics() {
|
|
check_diagnostics(
|
|
r#"
|
|
#[rustc_legacy_const_generics(1, 3)]
|
|
fn mixed<const N1: &'static str, const N2: bool>(
|
|
_a: u8,
|
|
_b: i8,
|
|
) {}
|
|
|
|
fn f() {
|
|
mixed(0, "", -1, true);
|
|
mixed::<"", true>(0, -1);
|
|
}
|
|
|
|
#[rustc_legacy_const_generics(1, 3)]
|
|
fn b<const N1: u8, const N2: u8>(
|
|
_a: u8,
|
|
_b: u8,
|
|
) {}
|
|
|
|
fn g() {
|
|
b(0, 1, 2, 3);
|
|
b::<1, 3>(0, 2);
|
|
|
|
b(0, 1, 2);
|
|
//^ error: expected 4 arguments, found 3
|
|
}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn tuple_struct_pat() {
|
|
check_diagnostics(
|
|
r#"
|
|
struct S(u32, u32);
|
|
fn f(
|
|
S(a, b, c): S,
|
|
// ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
|
|
S(): S,
|
|
// ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
|
|
S(e, f, .., g, d): S
|
|
// ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields
|
|
) { _ = (a, b, c, d, e, f, g); }
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn no_type_mismatches_when_arg_count_mismatch() {
|
|
check_diagnostics(
|
|
r#"
|
|
fn foo((): (), (): ()) {
|
|
foo(1, 2, 3);
|
|
// ^^ error: expected 2 arguments, found 3
|
|
foo(1);
|
|
// ^ error: expected 2 arguments, found 1
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
}
|