//! This module provides primitives for showing type and function parameter information when editing
//! a call or use-site.

use std::collections::BTreeSet;

use either::Either;
use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait};
use ide_db::{
    active_parameter::{callable_for_node, generic_def_for_node},
    base_db::FilePosition,
    documentation::{Documentation, HasDocs},
    FxIndexMap,
};
use stdx::format_to;
use syntax::{
    algo,
    ast::{self, AstChildren, HasArgList},
    match_ast, AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken,
    TextRange, TextSize, T,
};

use crate::RootDatabase;

/// Contains information about an item signature as seen from a use site.
///
/// This includes the "active parameter", which is the parameter whose value is currently being
/// edited.
#[derive(Debug)]
pub struct SignatureHelp {
    pub doc: Option<Documentation>,
    pub signature: String,
    pub active_parameter: Option<usize>,
    parameters: Vec<TextRange>,
}

impl SignatureHelp {
    pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
        self.parameters.iter().map(move |&it| &self.signature[it])
    }

    pub fn parameter_ranges(&self) -> &[TextRange] {
        &self.parameters
    }

    fn push_call_param(&mut self, param: &str) {
        self.push_param("(", param);
    }

    fn push_generic_param(&mut self, param: &str) {
        self.push_param("<", param);
    }

    fn push_record_field(&mut self, param: &str) {
        self.push_param("{ ", param);
    }

    fn push_param(&mut self, opening_delim: &str, param: &str) {
        if !self.signature.ends_with(opening_delim) {
            self.signature.push_str(", ");
        }
        let start = TextSize::of(&self.signature);
        self.signature.push_str(param);
        let end = TextSize::of(&self.signature);
        self.parameters.push(TextRange::new(start, end))
    }
}

/// Computes parameter information for the given position.
pub(crate) fn signature_help(
    db: &RootDatabase,
    FilePosition { file_id, offset }: FilePosition,
) -> Option<SignatureHelp> {
    let sema = Semantics::new(db);
    let file = sema.parse(file_id);
    let file = file.syntax();
    let token = file
        .token_at_offset(offset)
        .left_biased()
        // if the cursor is sandwiched between two space tokens and the call is unclosed
        // this prevents us from leaving the CallExpression
        .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
    let token = sema.descend_into_macros_single(token, offset);

    for node in token.parent_ancestors() {
        match_ast! {
            match node {
                ast::ArgList(arg_list) => {
                    let cursor_outside = arg_list.r_paren_token().as_ref() == Some(&token);
                    if cursor_outside {
                        continue;
                    }
                    return signature_help_for_call(&sema, arg_list, token);
                },
                ast::GenericArgList(garg_list) => {
                    let cursor_outside = garg_list.r_angle_token().as_ref() == Some(&token);
                    if cursor_outside {
                        continue;
                    }
                    return signature_help_for_generics(&sema, garg_list, token);
                },
                ast::RecordExpr(record) => {
                    let cursor_outside = record.record_expr_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token);
                    if cursor_outside {
                        continue;
                    }
                    return signature_help_for_record_lit(&sema, record, token);
                },
                ast::RecordPat(record) => {
                    let cursor_outside = record.record_pat_field_list().and_then(|list| list.r_curly_token()).as_ref() == Some(&token);
                    if cursor_outside {
                        continue;
                    }
                    return signature_help_for_record_pat(&sema, record, token);
                },
                ast::TupleStructPat(tuple_pat) => {
                    let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
                    if cursor_outside {
                        continue;
                    }
                    return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
                },
                ast::TuplePat(tuple_pat) => {
                    let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
                    if cursor_outside {
                        continue;
                    }
                    return signature_help_for_tuple_pat(&sema, tuple_pat, token);
                },
                ast::TupleExpr(tuple_expr) => {
                    let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token);
                    if cursor_outside {
                        continue;
                    }
                    return signature_help_for_tuple_expr(&sema, tuple_expr, token);
                },
                _ => (),
            }
        }

        // Stop at multi-line expressions, since the signature of the outer call is not very
        // helpful inside them.
        if let Some(expr) = ast::Expr::cast(node.clone()) {
            if !matches!(expr, ast::Expr::RecordExpr(..))
                && expr.syntax().text().contains_char('\n')
            {
                break;
            }
        }
    }

    None
}

fn signature_help_for_call(
    sema: &Semantics<'_, RootDatabase>,
    arg_list: ast::ArgList,
    token: SyntaxToken,
) -> Option<SignatureHelp> {
    // Find the calling expression and its NameRef
    let mut nodes = arg_list.syntax().ancestors().skip(1);
    let calling_node = loop {
        if let Some(callable) = ast::CallableExpr::cast(nodes.next()?) {
            let inside_callable = callable
                .arg_list()
                .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()));
            if inside_callable {
                break callable;
            }
        }
    };

    let (callable, active_parameter) = callable_for_node(sema, &calling_node, &token)?;

    let mut res =
        SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter };

    let db = sema.db;
    let mut fn_params = None;
    match callable.kind() {
        hir::CallableKind::Function(func) => {
            res.doc = func.docs(db);
            format_to!(res.signature, "fn {}", func.name(db).display(db));
            fn_params = Some(match callable.receiver_param(db) {
                Some(_self) => func.params_without_self(db),
                None => func.assoc_fn_params(db),
            });
        }
        hir::CallableKind::TupleStruct(strukt) => {
            res.doc = strukt.docs(db);
            format_to!(res.signature, "struct {}", strukt.name(db).display(db));
        }
        hir::CallableKind::TupleEnumVariant(variant) => {
            res.doc = variant.docs(db);
            format_to!(
                res.signature,
                "enum {}::{}",
                variant.parent_enum(db).name(db).display(db),
                variant.name(db).display(db)
            );
        }
        hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
    }

    res.signature.push('(');
    {
        if let Some((self_param, _)) = callable.receiver_param(db) {
            format_to!(res.signature, "{}", self_param.display(db))
        }
        let mut buf = String::new();
        for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() {
            buf.clear();
            if let Some(pat) = pat {
                match pat {
                    Either::Left(_self) => format_to!(buf, "self: "),
                    Either::Right(pat) => format_to!(buf, "{}: ", pat),
                }
            }
            // APITs (argument position `impl Trait`s) are inferred as {unknown} as the user is
            // in the middle of entering call arguments.
            // In that case, fall back to render definitions of the respective parameters.
            // This is overly conservative: we do not substitute known type vars
            // (see FIXME in tests::impl_trait) and falling back on any unknowns.
            match (ty.contains_unknown(), fn_params.as_deref()) {
                (true, Some(fn_params)) => format_to!(buf, "{}", fn_params[idx].ty().display(db)),
                _ => format_to!(buf, "{}", ty.display(db)),
            }
            res.push_call_param(&buf);
        }
    }
    res.signature.push(')');

    let mut render = |ret_type: hir::Type| {
        if !ret_type.is_unit() {
            format_to!(res.signature, " -> {}", ret_type.display(db));
        }
    };
    match callable.kind() {
        hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => {
            render(func.ret_type(db))
        }
        hir::CallableKind::Function(_)
        | hir::CallableKind::Closure
        | hir::CallableKind::FnPtr
        | hir::CallableKind::Other => render(callable.return_type()),
        hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
    }
    Some(res)
}

fn signature_help_for_generics(
    sema: &Semantics<'_, RootDatabase>,
    arg_list: ast::GenericArgList,
    token: SyntaxToken,
) -> Option<SignatureHelp> {
    let (mut generics_def, mut active_parameter, first_arg_is_non_lifetime) =
        generic_def_for_node(sema, &arg_list, &token)?;
    let mut res = SignatureHelp {
        doc: None,
        signature: String::new(),
        parameters: vec![],
        active_parameter: None,
    };

    let db = sema.db;
    match generics_def {
        hir::GenericDef::Function(it) => {
            res.doc = it.docs(db);
            format_to!(res.signature, "fn {}", it.name(db).display(db));
        }
        hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
            res.doc = it.docs(db);
            format_to!(res.signature, "enum {}", it.name(db).display(db));
        }
        hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
            res.doc = it.docs(db);
            format_to!(res.signature, "struct {}", it.name(db).display(db));
        }
        hir::GenericDef::Adt(hir::Adt::Union(it)) => {
            res.doc = it.docs(db);
            format_to!(res.signature, "union {}", it.name(db).display(db));
        }
        hir::GenericDef::Trait(it) => {
            res.doc = it.docs(db);
            format_to!(res.signature, "trait {}", it.name(db).display(db));
        }
        hir::GenericDef::TraitAlias(it) => {
            res.doc = it.docs(db);
            format_to!(res.signature, "trait {}", it.name(db).display(db));
        }
        hir::GenericDef::TypeAlias(it) => {
            res.doc = it.docs(db);
            format_to!(res.signature, "type {}", it.name(db).display(db));
        }
        hir::GenericDef::Variant(it) => {
            // In paths, generics of an enum can be specified *after* one of its variants.
            // eg. `None::<u8>`
            // We'll use the signature of the enum, but include the docs of the variant.
            res.doc = it.docs(db);
            let enum_ = it.parent_enum(db);
            format_to!(res.signature, "enum {}", enum_.name(db).display(db));
            generics_def = enum_.into();
        }
        // These don't have generic args that can be specified
        hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
    }

    let params = generics_def.params(sema.db);
    let num_lifetime_params =
        params.iter().take_while(|param| matches!(param, GenericParam::LifetimeParam(_))).count();
    if first_arg_is_non_lifetime {
        // Lifetime parameters were omitted.
        active_parameter += num_lifetime_params;
    }
    res.active_parameter = Some(active_parameter);

    res.signature.push('<');
    let mut buf = String::new();
    for param in params {
        if let hir::GenericParam::TypeParam(ty) = param {
            if ty.is_implicit(db) {
                continue;
            }
        }

        buf.clear();
        format_to!(buf, "{}", param.display(db));
        res.push_generic_param(&buf);
    }
    if let hir::GenericDef::Trait(tr) = generics_def {
        add_assoc_type_bindings(db, &mut res, tr, arg_list);
    }
    res.signature.push('>');

    Some(res)
}

fn add_assoc_type_bindings(
    db: &RootDatabase,
    res: &mut SignatureHelp,
    tr: Trait,
    args: ast::GenericArgList,
) {
    if args.syntax().ancestors().find_map(ast::TypeBound::cast).is_none() {
        // Assoc type bindings are only valid in type bound position.
        return;
    }

    let present_bindings = args
        .generic_args()
        .filter_map(|arg| match arg {
            ast::GenericArg::AssocTypeArg(arg) => arg.name_ref().map(|n| n.to_string()),
            _ => None,
        })
        .collect::<BTreeSet<_>>();

    let mut buf = String::new();
    for binding in &present_bindings {
        buf.clear();
        format_to!(buf, "{} = …", binding);
        res.push_generic_param(&buf);
    }

    for item in tr.items_with_supertraits(db) {
        if let AssocItem::TypeAlias(ty) = item {
            let name = ty.name(db).to_smol_str();
            if !present_bindings.contains(&*name) {
                buf.clear();
                format_to!(buf, "{} = …", name);
                res.push_generic_param(&buf);
            }
        }
    }
}

fn signature_help_for_record_lit(
    sema: &Semantics<'_, RootDatabase>,
    record: ast::RecordExpr,
    token: SyntaxToken,
) -> Option<SignatureHelp> {
    signature_help_for_record_(
        sema,
        record.record_expr_field_list()?.syntax().children_with_tokens(),
        &record.path()?,
        record
            .record_expr_field_list()?
            .fields()
            .filter_map(|field| sema.resolve_record_field(&field))
            .map(|(field, _, ty)| (field, ty)),
        token,
    )
}

fn signature_help_for_record_pat(
    sema: &Semantics<'_, RootDatabase>,
    record: ast::RecordPat,
    token: SyntaxToken,
) -> Option<SignatureHelp> {
    signature_help_for_record_(
        sema,
        record.record_pat_field_list()?.syntax().children_with_tokens(),
        &record.path()?,
        record
            .record_pat_field_list()?
            .fields()
            .filter_map(|field| sema.resolve_record_pat_field(&field)),
        token,
    )
}

fn signature_help_for_tuple_struct_pat(
    sema: &Semantics<'_, RootDatabase>,
    pat: ast::TupleStructPat,
    token: SyntaxToken,
) -> Option<SignatureHelp> {
    let path = pat.path()?;
    let path_res = sema.resolve_path(&path)?;
    let mut res = SignatureHelp {
        doc: None,
        signature: String::new(),
        parameters: vec![],
        active_parameter: None,
    };
    let db = sema.db;

    let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
        let en = variant.parent_enum(db);

        res.doc = en.docs(db).map(|it| it.into());
        format_to!(
            res.signature,
            "enum {}::{} (",
            en.name(db).display(db),
            variant.name(db).display(db)
        );
        variant.fields(db)
    } else {
        let adt = match path_res {
            PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?,
            PathResolution::Def(ModuleDef::Adt(adt)) => adt,
            _ => return None,
        };

        match adt {
            hir::Adt::Struct(it) => {
                res.doc = it.docs(db).map(|it| it.into());
                format_to!(res.signature, "struct {} (", it.name(db).display(db));
                it.fields(db)
            }
            _ => return None,
        }
    };
    Some(signature_help_for_tuple_pat_ish(
        db,
        res,
        pat.syntax(),
        token,
        pat.fields(),
        fields.into_iter().map(|it| it.ty(db)),
    ))
}

fn signature_help_for_tuple_pat(
    sema: &Semantics<'_, RootDatabase>,
    pat: ast::TuplePat,
    token: SyntaxToken,
) -> Option<SignatureHelp> {
    let db = sema.db;
    let field_pats = pat.fields();
    let pat = pat.into();
    let ty = sema.type_of_pat(&pat)?;
    let fields = ty.original.tuple_fields(db);

    Some(signature_help_for_tuple_pat_ish(
        db,
        SignatureHelp {
            doc: None,
            signature: String::from('('),
            parameters: vec![],
            active_parameter: None,
        },
        pat.syntax(),
        token,
        field_pats,
        fields.into_iter(),
    ))
}

fn signature_help_for_tuple_expr(
    sema: &Semantics<'_, RootDatabase>,
    expr: ast::TupleExpr,
    token: SyntaxToken,
) -> Option<SignatureHelp> {
    let active_parameter = Some(
        expr.syntax()
            .children_with_tokens()
            .filter_map(NodeOrToken::into_token)
            .filter(|t| t.kind() == T![,])
            .take_while(|t| t.text_range().start() <= token.text_range().start())
            .count(),
    );

    let db = sema.db;
    let mut res = SignatureHelp {
        doc: None,
        signature: String::from('('),
        parameters: vec![],
        active_parameter,
    };
    let expr = sema.type_of_expr(&expr.into())?;
    let fields = expr.original.tuple_fields(db);
    let mut buf = String::new();
    for ty in fields {
        format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
        res.push_call_param(&buf);
        buf.clear();
    }
    res.signature.push(')');
    Some(res)
}

fn signature_help_for_record_(
    sema: &Semantics<'_, RootDatabase>,
    field_list_children: SyntaxElementChildren,
    path: &ast::Path,
    fields2: impl Iterator<Item = (hir::Field, hir::Type)>,
    token: SyntaxToken,
) -> Option<SignatureHelp> {
    let active_parameter = field_list_children
        .filter_map(NodeOrToken::into_token)
        .filter(|t| t.kind() == T![,])
        .take_while(|t| t.text_range().start() <= token.text_range().start())
        .count();

    let mut res = SignatureHelp {
        doc: None,
        signature: String::new(),
        parameters: vec![],
        active_parameter: Some(active_parameter),
    };

    let fields;

    let db = sema.db;
    let path_res = sema.resolve_path(path)?;
    if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
        fields = variant.fields(db);
        let en = variant.parent_enum(db);

        res.doc = en.docs(db).map(|it| it.into());
        format_to!(
            res.signature,
            "enum {}::{} {{ ",
            en.name(db).display(db),
            variant.name(db).display(db)
        );
    } else {
        let adt = match path_res {
            PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?,
            PathResolution::Def(ModuleDef::Adt(adt)) => adt,
            _ => return None,
        };

        match adt {
            hir::Adt::Struct(it) => {
                fields = it.fields(db);
                res.doc = it.docs(db).map(|it| it.into());
                format_to!(res.signature, "struct {} {{ ", it.name(db).display(db));
            }
            hir::Adt::Union(it) => {
                fields = it.fields(db);
                res.doc = it.docs(db).map(|it| it.into());
                format_to!(res.signature, "union {} {{ ", it.name(db).display(db));
            }
            _ => return None,
        }
    }

    let mut fields =
        fields.into_iter().map(|field| (field.name(db), Some(field))).collect::<FxIndexMap<_, _>>();
    let mut buf = String::new();
    for (field, ty) in fields2 {
        let name = field.name(db);
        format_to!(buf, "{}: {}", name.display(db), ty.display_truncated(db, Some(20)));
        res.push_record_field(&buf);
        buf.clear();

        if let Some(field) = fields.get_mut(&name) {
            *field = None;
        }
    }
    for (name, field) in fields {
        let Some(field) = field else { continue };
        format_to!(buf, "{}: {}", name.display(db), field.ty(db).display_truncated(db, Some(20)));
        res.push_record_field(&buf);
        buf.clear();
    }
    res.signature.push_str(" }");
    Some(res)
}

fn signature_help_for_tuple_pat_ish(
    db: &RootDatabase,
    mut res: SignatureHelp,
    pat: &SyntaxNode,
    token: SyntaxToken,
    mut field_pats: AstChildren<ast::Pat>,
    fields: impl ExactSizeIterator<Item = hir::Type>,
) -> SignatureHelp {
    let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));
    let is_left_of_rest_pat =
        rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());

    let commas = pat
        .children_with_tokens()
        .filter_map(NodeOrToken::into_token)
        .filter(|t| t.kind() == T![,]);

    res.active_parameter = {
        Some(if is_left_of_rest_pat {
            commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
        } else {
            let n_commas = commas
                .collect::<Vec<_>>()
                .into_iter()
                .rev()
                .take_while(|t| t.text_range().start() > token.text_range().start())
                .count();
            fields.len().saturating_sub(1).saturating_sub(n_commas)
        })
    };

    let mut buf = String::new();
    for ty in fields {
        format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
        res.push_call_param(&buf);
        buf.clear();
    }
    res.signature.push_str(")");
    res
}
#[cfg(test)]
mod tests {
    use std::iter;

    use expect_test::{expect, Expect};
    use ide_db::base_db::{fixture::ChangeFixture, FilePosition};
    use stdx::format_to;

    use crate::RootDatabase;

    /// Creates analysis from a multi-file fixture, returns positions marked with $0.
    pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
        let change_fixture = ChangeFixture::parse(ra_fixture);
        let mut database = RootDatabase::default();
        database.apply_change(change_fixture.change);
        let (file_id, range_or_offset) =
            change_fixture.file_position.expect("expected a marker ($0)");
        let offset = range_or_offset.expect_offset();
        (database, FilePosition { file_id, offset })
    }

    #[track_caller]
    fn check(ra_fixture: &str, expect: Expect) {
        let fixture = format!(
            r#"
//- minicore: sized, fn
{ra_fixture}
            "#
        );
        let (db, position) = position(&fixture);
        let sig_help = crate::signature_help::signature_help(&db, position);
        let actual = match sig_help {
            Some(sig_help) => {
                let mut rendered = String::new();
                if let Some(docs) = &sig_help.doc {
                    format_to!(rendered, "{}\n------\n", docs.as_str());
                }
                format_to!(rendered, "{}\n", sig_help.signature);
                let mut offset = 0;
                for (i, range) in sig_help.parameter_ranges().iter().enumerate() {
                    let is_active = sig_help.active_parameter == Some(i);

                    let start = u32::from(range.start());
                    let gap = start.checked_sub(offset).unwrap_or_else(|| {
                        panic!("parameter ranges out of order: {:?}", sig_help.parameter_ranges())
                    });
                    rendered.extend(iter::repeat(' ').take(gap as usize));
                    let param_text = &sig_help.signature[*range];
                    let width = param_text.chars().count(); // …
                    let marker = if is_active { '^' } else { '-' };
                    rendered.extend(iter::repeat(marker).take(width));
                    offset += gap + u32::from(range.len());
                }
                if !sig_help.parameter_ranges().is_empty() {
                    format_to!(rendered, "\n");
                }
                rendered
            }
            None => String::new(),
        };
        expect.assert_eq(&actual);
    }

    #[test]
    fn test_fn_signature_two_args() {
        check(
            r#"
fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo($03, ); }
"#,
            expect![[r#"
                fn foo(x: u32, y: u32) -> u32
                       ^^^^^^  ------
            "#]],
        );
        check(
            r#"
fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3$0, ); }
"#,
            expect![[r#"
                fn foo(x: u32, y: u32) -> u32
                       ^^^^^^  ------
            "#]],
        );
        check(
            r#"
fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3,$0 ); }
"#,
            expect![[r#"
                fn foo(x: u32, y: u32) -> u32
                       ------  ^^^^^^
            "#]],
        );
        check(
            r#"
fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3, $0); }
"#,
            expect![[r#"
                fn foo(x: u32, y: u32) -> u32
                       ------  ^^^^^^
            "#]],
        );
    }

    #[test]
    fn test_fn_signature_two_args_empty() {
        check(
            r#"
fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo($0); }
"#,
            expect![[r#"
                fn foo(x: u32, y: u32) -> u32
                       ^^^^^^  ------
            "#]],
        );
    }

    #[test]
    fn test_fn_signature_two_args_first_generics() {
        check(
            r#"
fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
    where T: Copy + Display, U: Debug
{ x + y }

fn bar() { foo($03, ); }
"#,
            expect![[r#"
                fn foo(x: i32, y: U) -> u32
                       ^^^^^^  ----
            "#]],
        );
    }

    #[test]
    fn test_fn_signature_no_params() {
        check(
            r#"
fn foo<T>() -> T where T: Copy + Display {}
fn bar() { foo($0); }
"#,
            expect![[r#"
                fn foo() -> T
            "#]],
        );
    }

    #[test]
    fn test_fn_signature_for_impl() {
        check(
            r#"
struct F;
impl F { pub fn new() { } }
fn bar() {
    let _ : F = F::new($0);
}
"#,
            expect![[r#"
                fn new()
            "#]],
        );
    }

    #[test]
    fn test_fn_signature_for_method_self() {
        check(
            r#"
struct S;
impl S { pub fn do_it(&self) {} }

fn bar() {
    let s: S = S;
    s.do_it($0);
}
"#,
            expect![[r#"
                fn do_it(&self)
            "#]],
        );
    }

    #[test]
    fn test_fn_signature_for_method_with_arg() {
        check(
            r#"
struct S;
impl S {
    fn foo(&self, x: i32) {}
}

fn main() { S.foo($0); }
"#,
            expect![[r#"
                fn foo(&self, x: i32)
                              ^^^^^^
            "#]],
        );
    }

    #[test]
    fn test_fn_signature_for_generic_method() {
        check(
            r#"
struct S<T>(T);
impl<T> S<T> {
    fn foo(&self, x: T) {}
}

fn main() { S(1u32).foo($0); }
"#,
            expect![[r#"
                fn foo(&self, x: u32)
                              ^^^^^^
            "#]],
        );
    }

    #[test]
    fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
        check(
            r#"
struct S;
impl S {
    fn foo(&self, x: i32) {}
}

fn main() { S::foo($0); }
"#,
            expect![[r#"
                fn foo(self: &S, x: i32)
                       ^^^^^^^^  ------
            "#]],
        );
    }

    #[test]
    fn test_fn_signature_with_docs_simple() {
        check(
            r#"
/// test
// non-doc-comment
fn foo(j: u32) -> u32 {
    j
}

fn bar() {
    let _ = foo($0);
}
"#,
            expect![[r#"
                test
                ------
                fn foo(j: u32) -> u32
                       ^^^^^^
            "#]],
        );
    }

    #[test]
    fn test_fn_signature_with_docs() {
        check(
            r#"
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, my_crate::add_one(5));
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

pub fn r#do() {
    add_one($0
}"#,
            expect![[r##"
                Adds one to the number given.

                # Examples

                ```
                let five = 5;

                assert_eq!(6, my_crate::add_one(5));
                ```
                ------
                fn add_one(x: i32) -> i32
                           ^^^^^^
            "##]],
        );
    }

    #[test]
    fn test_fn_signature_with_docs_impl() {
        check(
            r#"
struct addr;
impl addr {
    /// Adds one to the number given.
    ///
    /// # Examples
    ///
    /// ```
    /// let five = 5;
    ///
    /// assert_eq!(6, my_crate::add_one(5));
    /// ```
    pub fn add_one(x: i32) -> i32 {
        x + 1
    }
}

pub fn do_it() {
    addr {};
    addr::add_one($0);
}
"#,
            expect![[r##"
                Adds one to the number given.

                # Examples

                ```
                let five = 5;

                assert_eq!(6, my_crate::add_one(5));
                ```
                ------
                fn add_one(x: i32) -> i32
                           ^^^^^^
            "##]],
        );
    }

    #[test]
    fn test_fn_signature_with_docs_from_actix() {
        check(
            r#"
trait Actor {
    /// Actor execution context type
    type Context;
}
trait WriteHandler<E>
where
    Self: Actor
{
    /// Method is called when writer finishes.
    ///
    /// By default this method stops actor's `Context`.
    fn finished(&mut self, ctx: &mut Self::Context) {}
}

fn foo(mut r: impl WriteHandler<()>) {
    r.finished($0);
}
"#,
            expect![[r#"
                Method is called when writer finishes.

                By default this method stops actor's `Context`.
                ------
                fn finished(&mut self, ctx: &mut <impl WriteHandler<()> as Actor>::Context)
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            "#]],
        );
    }

    #[test]
    fn call_info_bad_offset() {
        check(
            r#"
fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo $0 (3, ); }
"#,
            expect![[""]],
        );
    }

    #[test]
    fn outside_of_arg_list() {
        check(
            r#"
fn foo(a: u8) {}
fn f() {
    foo(123)$0
}
"#,
            expect![[]],
        );
        check(
            r#"
fn foo<T>(a: u8) {}
fn f() {
    foo::<u32>$0()
}
"#,
            expect![[]],
        );
        check(
            r#"
fn foo(a: u8) -> u8 {a}
fn bar(a: u8) -> u8 {a}
fn f() {
    foo(bar(123)$0)
}
"#,
            expect![[r#"
                fn foo(a: u8) -> u8
                       ^^^^^
            "#]],
        );
        check(
            r#"
struct Vec<T>(T);
struct Vec2<T>(T);
fn f() {
    let _: Vec2<Vec<u8>$0>
}
"#,
            expect![[r#"
                struct Vec2<T>
                            ^
            "#]],
        );
    }

    #[test]
    fn test_nested_method_in_lambda() {
        check(
            r#"
struct Foo;
impl Foo { fn bar(&self, _: u32) { } }

fn bar(_: u32) { }

fn main() {
    let foo = Foo;
    std::thread::spawn(move || foo.bar($0));
}
"#,
            expect![[r#"
                fn bar(&self, _: u32)
                              ^^^^^^
            "#]],
        );
    }

    #[test]
    fn works_for_tuple_structs() {
        check(
            r#"
/// A cool tuple struct
struct S(u32, i32);
fn main() {
    let s = S(0, $0);
}
"#,
            expect![[r#"
                A cool tuple struct
                ------
                struct S(u32, i32)
                         ---  ^^^
            "#]],
        );
    }

    #[test]
    fn tuple_struct_pat() {
        check(
            r#"
/// A cool tuple struct
struct S(u32, i32);
fn main() {
    let S(0, $0);
}
"#,
            expect![[r#"
                A cool tuple struct
                ------
                struct S (u32, i32)
                          ---  ^^^
            "#]],
        );
    }

    #[test]
    fn tuple_struct_pat_rest() {
        check(
            r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16);
fn main() {
    let S(0, .., $0);
}
"#,
            expect![[r#"
                A cool tuple struct
                ------
                struct S (u32, i32, f32, u16)
                          ---  ---  ---  ^^^
            "#]],
        );
        check(
            r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16, u8);
fn main() {
    let S(0, .., $0, 0);
}
"#,
            expect![[r#"
                A cool tuple struct
                ------
                struct S (u32, i32, f32, u16, u8)
                          ---  ---  ---  ^^^  --
            "#]],
        );
        check(
            r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16);
fn main() {
    let S($0, .., 1);
}
"#,
            expect![[r#"
                A cool tuple struct
                ------
                struct S (u32, i32, f32, u16)
                          ^^^  ---  ---  ---
            "#]],
        );
        check(
            r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16, u8);
fn main() {
    let S(1, .., 1, $0, 2);
}
"#,
            expect![[r#"
                A cool tuple struct
                ------
                struct S (u32, i32, f32, u16, u8)
                          ---  ---  ---  ^^^  --
            "#]],
        );
        check(
            r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16);
fn main() {
    let S(1, $0.., 1);
}
"#,
            expect![[r#"
                A cool tuple struct
                ------
                struct S (u32, i32, f32, u16)
                          ---  ^^^  ---  ---
            "#]],
        );
        check(
            r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16);
fn main() {
    let S(1, ..$0, 1);
}
"#,
            expect![[r#"
                A cool tuple struct
                ------
                struct S (u32, i32, f32, u16)
                          ---  ^^^  ---  ---
            "#]],
        );
    }

    #[test]
    fn generic_struct() {
        check(
            r#"
struct S<T>(T);
fn main() {
    let s = S($0);
}
"#,
            expect![[r#"
                struct S({unknown})
                         ^^^^^^^^^
            "#]],
        );
    }

    #[test]
    fn works_for_enum_variants() {
        check(
            r#"
enum E {
    /// A Variant
    A(i32),
    /// Another
    B,
    /// And C
    C { a: i32, b: i32 }
}

fn main() {
    let a = E::A($0);
}
"#,
            expect![[r#"
                A Variant
                ------
                enum E::A(i32)
                          ^^^
            "#]],
        );
    }

    #[test]
    fn cant_call_struct_record() {
        check(
            r#"
struct S { x: u32, y: i32 }
fn main() {
    let s = S($0);
}
"#,
            expect![[""]],
        );
    }

    #[test]
    fn cant_call_enum_record() {
        check(
            r#"
enum E {
    /// A Variant
    A(i32),
    /// Another
    B,
    /// And C
    C { a: i32, b: i32 }
}

fn main() {
    let a = E::C($0);
}
"#,
            expect![[""]],
        );
    }

    #[test]
    fn fn_signature_for_call_in_macro() {
        check(
            r#"
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
fn foo() { }
id! {
    fn bar() { foo($0); }
}
"#,
            expect![[r#"
                fn foo()
            "#]],
        );
    }

    #[test]
    fn fn_signature_for_method_call_defined_in_macro() {
        check(
            r#"
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
struct S;
id! {
    impl S {
        fn foo<'a>(&'a mut self) {}
    }
}
fn test() { S.foo($0); }
"#,
            expect![[r#"
                fn foo(&'a mut self)
            "#]],
        );
    }

    #[test]
    fn call_info_for_lambdas() {
        check(
            r#"
struct S;
fn foo(s: S) -> i32 { 92 }
fn main() {
    (|s| foo(s))($0)
}
        "#,
            expect![[r#"
                (s: S) -> i32
                 ^^^^
            "#]],
        )
    }

    #[test]
    fn call_info_for_fn_def_over_reference() {
        check(
            r#"
struct S;
fn foo(s: S) -> i32 { 92 }
fn main() {
    let bar = &&&&&foo;
    bar($0);
}
        "#,
            expect![[r#"
                fn foo(s: S) -> i32
                       ^^^^
            "#]],
        )
    }

    #[test]
    fn call_info_for_fn_ptr() {
        check(
            r#"
fn main(f: fn(i32, f64) -> char) {
    f(0, $0)
}
        "#,
            expect![[r#"
                (i32, f64) -> char
                 ---  ^^^
            "#]],
        )
    }

    #[test]
    fn call_info_for_unclosed_call() {
        check(
            r#"
fn foo(foo: u32, bar: u32) {}
fn main() {
    foo($0
}"#,
            expect![[r#"
                fn foo(foo: u32, bar: u32)
                       ^^^^^^^^  --------
            "#]],
        );
        // check with surrounding space
        check(
            r#"
fn foo(foo: u32, bar: u32) {}
fn main() {
    foo( $0
}"#,
            expect![[r#"
                fn foo(foo: u32, bar: u32)
                       ^^^^^^^^  --------
            "#]],
        )
    }

    #[test]
    fn test_multiline_argument() {
        check(
            r#"
fn callee(a: u8, b: u8) {}
fn main() {
    callee(match 0 {
        0 => 1,$0
    })
}"#,
            expect![[r#""#]],
        );
        check(
            r#"
fn callee(a: u8, b: u8) {}
fn main() {
    callee(match 0 {
        0 => 1,
    },$0)
}"#,
            expect![[r#"
                fn callee(a: u8, b: u8)
                          -----  ^^^^^
            "#]],
        );
        check(
            r#"
fn callee(a: u8, b: u8) {}
fn main() {
    callee($0match 0 {
        0 => 1,
    })
}"#,
            expect![[r#"
                fn callee(a: u8, b: u8)
                          ^^^^^  -----
            "#]],
        );
    }

    #[test]
    fn test_generics_simple() {
        check(
            r#"
/// Option docs.
enum Option<T> {
    Some(T),
    None,
}

fn f() {
    let opt: Option<$0
}
        "#,
            expect![[r#"
                Option docs.
                ------
                enum Option<T>
                            ^
            "#]],
        );
    }

    #[test]
    fn test_generics_on_variant() {
        check(
            r#"
/// Option docs.
enum Option<T> {
    /// Some docs.
    Some(T),
    /// None docs.
    None,
}

use Option::*;

fn f() {
    None::<$0
}
        "#,
            expect![[r#"
                None docs.
                ------
                enum Option<T>
                            ^
            "#]],
        );
    }

    #[test]
    fn test_lots_of_generics() {
        check(
            r#"
trait Tr<T> {}

struct S<T>(T);

impl<T> S<T> {
    fn f<G, H>(g: G, h: impl Tr<G>) where G: Tr<()> {}
}

fn f() {
    S::<u8>::f::<(), $0
}
        "#,
            expect![[r#"
                fn f<G: Tr<()>, H>
                     ---------  ^
            "#]],
        );
    }

    #[test]
    fn test_generics_in_trait_ufcs() {
        check(
            r#"
trait Tr {
    fn f<T: Tr, U>() {}
}

struct S;

impl Tr for S {}

fn f() {
    <S as Tr>::f::<$0
}
        "#,
            expect![[r#"
                fn f<T: Tr, U>
                     ^^^^^  -
            "#]],
        );
    }

    #[test]
    fn test_generics_in_method_call() {
        check(
            r#"
struct S;

impl S {
    fn f<T>(&self) {}
}

fn f() {
    S.f::<$0
}
        "#,
            expect![[r#"
                fn f<T>
                     ^
            "#]],
        );
    }

    #[test]
    fn test_generic_param_in_method_call() {
        check(
            r#"
struct Foo;
impl Foo {
    fn test<V>(&mut self, val: V) {}
}
fn sup() {
    Foo.test($0)
}
"#,
            expect![[r#"
                fn test(&mut self, val: V)
                                   ^^^^^^
            "#]],
        );
    }

    #[test]
    fn test_generic_kinds() {
        check(
            r#"
fn callee<'a, const A: u8, T, const C: u8>() {}

fn f() {
    callee::<'static, $0
}
        "#,
            expect![[r#"
                fn callee<'a, const A: u8, T, const C: u8>
                          --  ^^^^^^^^^^^  -  -----------
            "#]],
        );
        check(
            r#"
fn callee<'a, const A: u8, T, const C: u8>() {}

fn f() {
    callee::<NON_LIFETIME$0
}
        "#,
            expect![[r#"
                fn callee<'a, const A: u8, T, const C: u8>
                          --  ^^^^^^^^^^^  -  -----------
            "#]],
        );
    }

    #[test]
    fn test_trait_assoc_types() {
        check(
            r#"
trait Trait<'a, T> {
    type Assoc;
}
fn f() -> impl Trait<(), $0
            "#,
            expect![[r#"
                trait Trait<'a, T, Assoc = …>
                            --  -  ^^^^^^^^^
            "#]],
        );
        check(
            r#"
trait Iterator {
    type Item;
}
fn f() -> impl Iterator<$0
            "#,
            expect![[r#"
                trait Iterator<Item = …>
                               ^^^^^^^^
            "#]],
        );
        check(
            r#"
trait Iterator {
    type Item;
}
fn f() -> impl Iterator<Item = $0
            "#,
            expect![[r#"
                trait Iterator<Item = …>
                               ^^^^^^^^
            "#]],
        );
        check(
            r#"
trait Tr {
    type A;
    type B;
}
fn f() -> impl Tr<$0
            "#,
            expect![[r#"
                trait Tr<A = …, B = …>
                         ^^^^^  -----
            "#]],
        );
        check(
            r#"
trait Tr {
    type A;
    type B;
}
fn f() -> impl Tr<B$0
            "#,
            expect![[r#"
                trait Tr<A = …, B = …>
                         ^^^^^  -----
            "#]],
        );
        check(
            r#"
trait Tr {
    type A;
    type B;
}
fn f() -> impl Tr<B = $0
            "#,
            expect![[r#"
                trait Tr<B = …, A = …>
                         ^^^^^  -----
            "#]],
        );
        check(
            r#"
trait Tr {
    type A;
    type B;
}
fn f() -> impl Tr<B = (), $0
            "#,
            expect![[r#"
                trait Tr<B = …, A = …>
                         -----  ^^^^^
            "#]],
        );
    }

    #[test]
    fn test_supertrait_assoc() {
        check(
            r#"
trait Super {
    type SuperTy;
}
trait Sub: Super + Super {
    type SubTy;
}
fn f() -> impl Sub<$0
            "#,
            expect![[r#"
                trait Sub<SubTy = …, SuperTy = …>
                          ^^^^^^^^^  -----------
            "#]],
        );
    }

    #[test]
    fn no_assoc_types_outside_type_bounds() {
        check(
            r#"
trait Tr<T> {
    type Assoc;
}

impl Tr<$0
        "#,
            expect![[r#"
            trait Tr<T>
                     ^
        "#]],
        );
    }

    #[test]
    fn impl_trait() {
        // FIXME: Substitute type vars in impl trait (`U` -> `i8`)
        check(
            r#"
trait Trait<T> {}
struct Wrap<T>(T);
fn foo<U>(x: Wrap<impl Trait<U>>) {}
fn f() {
    foo::<i8>($0)
}
"#,
            expect![[r#"
                fn foo(x: Wrap<impl Trait<U>>)
                       ^^^^^^^^^^^^^^^^^^^^^^
            "#]],
        );
    }

    #[test]
    fn fully_qualified_syntax() {
        check(
            r#"
fn f() {
    trait A { fn foo(&self, other: Self); }
    A::foo(&self$0, other);
}
"#,
            expect![[r#"
                fn foo(self: &Self, other: Self)
                       ^^^^^^^^^^^  -----------
            "#]],
        );
    }

    #[test]
    fn help_for_generic_call() {
        check(
            r#"
fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
    f($0)
}
"#,
            expect![[r#"
                (u8, u16) -> i32
                 ^^  ---
            "#]],
        );
        check(
            r#"
fn f<T, F: FnOnce(&T, u16) -> &T>(f: F) {
    f($0)
}
"#,
            expect![[r#"
                (&T, u16) -> &T
                 ^^  ---
            "#]],
        );
    }

    #[test]
    fn regression_13579() {
        check(
            r#"
fn f() {
    take(2)($0);
}

fn take<C, Error>(
    count: C
) -> impl Fn() -> C  {
    move || count
}
"#,
            expect![[r#"
                () -> i32
            "#]],
        );
    }

    #[test]
    fn record_literal() {
        check(
            r#"
struct Strukt<T, U = ()> {
    t: T,
    u: U,
    unit: (),
}
fn f() {
    Strukt {
        u: 0,
        $0
    }
}
"#,
            expect![[r#"
                struct Strukt { u: i32, t: T, unit: () }
                                ------  ^^^^  --------
            "#]],
        );
    }

    #[test]
    fn record_literal_nonexistent_field() {
        check(
            r#"
struct Strukt {
    a: u8,
}
fn f() {
    Strukt {
        b: 8,
        $0
    }
}
"#,
            expect![[r#"
                struct Strukt { a: u8 }
                                -----
            "#]],
        );
    }

    #[test]
    fn tuple_variant_record_literal() {
        check(
            r#"
enum Opt {
    Some(u8),
}
fn f() {
    Opt::Some {$0}
}
"#,
            expect![[r#"
                enum Opt::Some { 0: u8 }
                                 ^^^^^
            "#]],
        );
        check(
            r#"
enum Opt {
    Some(u8),
}
fn f() {
    Opt::Some {0:0,$0}
}
"#,
            expect![[r#"
                enum Opt::Some { 0: u8 }
                                 -----
            "#]],
        );
    }

    #[test]
    fn record_literal_self() {
        check(
            r#"
struct S { t: u8 }
impl S {
    fn new() -> Self {
        Self { $0 }
    }
}
        "#,
            expect![[r#"
                struct S { t: u8 }
                           ^^^^^
            "#]],
        );
    }

    #[test]
    fn record_pat() {
        check(
            r#"
struct Strukt<T, U = ()> {
    t: T,
    u: U,
    unit: (),
}
fn f() {
    let Strukt {
        u: 0,
        $0
    }
}
"#,
            expect![[r#"
                struct Strukt { u: i32, t: T, unit: () }
                                ------  ^^^^  --------
            "#]],
        );
    }

    #[test]
    fn test_enum_in_nested_method_in_lambda() {
        check(
            r#"
enum A {
    A,
    B
}

fn bar(_: A) { }

fn main() {
    let foo = Foo;
    std::thread::spawn(move || { bar(A:$0) } );
}
"#,
            expect![[r#"
                fn bar(_: A)
                       ^^^^
            "#]],
        );
    }

    #[test]
    fn test_tuple_expr_free() {
        check(
            r#"
fn main() {
    (0$0, 1, 3);
}
"#,
            expect![[r#"
                (i32, i32, i32)
                 ^^^  ---  ---
            "#]],
        );
        check(
            r#"
fn main() {
    ($0 1, 3);
}
"#,
            expect![[r#"
                (i32, i32)
                 ^^^  ---
            "#]],
        );
        check(
            r#"
fn main() {
    (1, 3 $0);
}
"#,
            expect![[r#"
                (i32, i32)
                 ---  ^^^
            "#]],
        );
        check(
            r#"
fn main() {
    (1, 3 $0,);
}
"#,
            expect![[r#"
                (i32, i32)
                 ---  ^^^
            "#]],
        );
    }

    #[test]
    fn test_tuple_expr_expected() {
        check(
            r#"
fn main() {
    let _: (&str, u32, u32)= ($0, 1, 3);
}
"#,
            expect![[r#"
                (&str, u32, u32)
                 ^^^^  ---  ---
            "#]],
        );
        // FIXME: Should typeck report a 4-ary tuple for the expression here?
        check(
            r#"
fn main() {
    let _: (&str, u32, u32, u32) = ($0, 1, 3);
}
"#,
            expect![[r#"
                (&str, u32, u32)
                 ^^^^  ---  ---
            "#]],
        );
        check(
            r#"
fn main() {
    let _: (&str, u32, u32)= ($0, 1, 3, 5);
}
"#,
            expect![[r#"
                (&str, u32, u32, i32)
                 ^^^^  ---  ---  ---
            "#]],
        );
    }

    #[test]
    fn test_tuple_pat_free() {
        check(
            r#"
fn main() {
    let ($0, 1, 3);
}
"#,
            expect![[r#"
                ({unknown}, i32, i32)
                 ^^^^^^^^^  ---  ---
            "#]],
        );
        check(
            r#"
fn main() {
    let (0$0, 1, 3);
}
"#,
            expect![[r#"
                (i32, i32, i32)
                 ^^^  ---  ---
            "#]],
        );
        check(
            r#"
fn main() {
    let ($0 1, 3);
}
"#,
            expect![[r#"
                (i32, i32)
                 ^^^  ---
            "#]],
        );
        check(
            r#"
fn main() {
    let (1, 3 $0);
}
"#,
            expect![[r#"
                (i32, i32)
                 ---  ^^^
            "#]],
        );
        check(
            r#"
fn main() {
    let (1, 3 $0,);
}
"#,
            expect![[r#"
                (i32, i32)
                 ---  ^^^
            "#]],
        );
        check(
            r#"
fn main() {
    let (1, 3 $0, ..);
}
"#,
            expect![[r#"
                (i32, i32)
                 ---  ^^^
            "#]],
        );
        check(
            r#"
fn main() {
    let (1, 3, .., $0);
}
"#,
            // FIXME: This is wrong, this should not mark the last as active
            expect![[r#"
                (i32, i32)
                 ---  ^^^
            "#]],
        );
    }

    #[test]
    fn test_tuple_pat_expected() {
        check(
            r#"
fn main() {
    let (0$0, 1, 3): (i32, i32, i32);
}
"#,
            expect![[r#"
                (i32, i32, i32)
                 ^^^  ---  ---
            "#]],
        );
        check(
            r#"
fn main() {
    let ($0, 1, 3): (i32, i32, i32);
}
"#,
            expect![[r#"
                (i32, i32, i32)
                 ^^^  ---  ---
            "#]],
        );
        check(
            r#"
fn main() {
    let (1, 3 $0): (i32,);
}
"#,
            expect![[r#"
                (i32, i32)
                 ---  ^^^
            "#]],
        );
        check(
            r#"
fn main() {
    let (1, 3 $0, ..): (i32, i32, i32, i32);
}
"#,
            expect![[r#"
                (i32, i32, i32, i32)
                 ---  ^^^  ---  ---
            "#]],
        );
        check(
            r#"
fn main() {
    let (1, 3, .., $0): (i32, i32, i32);
}
"#,
            expect![[r#"
                (i32, i32, i32)
                 ---  ---  ^^^
            "#]],
        );
    }
    #[test]
    fn test_tuple_pat_expected_inferred() {
        check(
            r#"
fn main() {
    let (0$0, 1, 3) = (1, 2 ,3);
}
"#,
            expect![[r#"
                (i32, i32, i32)
                 ^^^  ---  ---
            "#]],
        );
        check(
            r#"
fn main() {
    let ($0 1, 3) = (1, 2, 3);
}
"#,
            // FIXME: Should typeck report a 3-ary tuple for the pattern here?
            expect![[r#"
                (i32, i32)
                 ^^^  ---
            "#]],
        );
        check(
            r#"
fn main() {
    let (1, 3 $0) = (1,);
}
"#,
            expect![[r#"
                (i32, i32)
                 ---  ^^^
            "#]],
        );
        check(
            r#"
fn main() {
    let (1, 3 $0, ..) = (1, 2, 3, 4);
}
"#,
            expect![[r#"
                (i32, i32, i32, i32)
                 ---  ^^^  ---  ---
            "#]],
        );
        check(
            r#"
fn main() {
    let (1, 3, .., $0) = (1, 2, 3);
}
"#,
            expect![[r#"
                (i32, i32, i32)
                 ---  ---  ^^^
            "#]],
        );
    }
}
