<commit_before>//! Code common to structs, unions, and enum variants.

use crate::render::RenderContext;
use hir::{db::HirDatabase, HasAttrs, HasVisibility, HirDisplay, StructKind};
use ide_db::SnippetCap;
use itertools::Itertools;
use syntax::SmolStr;

/// A rendered struct, union, or enum variant, split into fields for actual
/// auto-completion (`literal`, using `field: ()`) and display in the
/// completions menu (`detail`, using `field: type`).
pub(crate) struct RenderedCompound {
    pub(crate) literal: String,
    pub(crate) detail: String,
}

/// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for
/// the `name` argument for an anonymous type.
pub(crate) fn render_record(
    db: &dyn HirDatabase,
    snippet_cap: Option<SnippetCap>,
    fields: &[hir::Field],
    name: Option<&str>,
) -> RenderedCompound {
    let fields = fields.iter();

    let (completions, types): (Vec<_>, Vec<_>) = fields
        .enumerate()
        .map(|(idx, field)| {
            (
                if snippet_cap.is_some() {
                    format!("{}: ${{{}:()}}", field.name(db), idx + 1)
                } else {
                    format!("{}: ()", field.name(db))
                },
                format!("{}: {}", field.name(db), field.ty(db).display(db)),
            )
        })
        .unzip();
    RenderedCompound {
        literal: format!("{} {{ {} }}", name.unwrap_or(""), completions.iter().format(", ")),
        detail: format!("{} {{ {} }}", name.unwrap_or(""), types.iter().format(", ")),
    }
}

/// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for
/// the `name` argument for an anonymous type.
pub(crate) fn render_tuple(
    db: &dyn HirDatabase,
    snippet_cap: Option<SnippetCap>,
    fields: &[hir::Field],
    name: Option<&str>,
) -> RenderedCompound {
    let fields = fields.iter();

    let (completions, types): (Vec<_>, Vec<_>) = fields
        .enumerate()
        .map(|(idx, field)| {
            (
                if snippet_cap.is_some() {
                    format!("${{{}:()}}", (idx + 1).to_string())
                } else {
                    "()".to_string()
                },
                field.ty(db).display(db).to_string(),
            )
        })
        .unzip();
    RenderedCompound {
        literal: format!("{}({})", name.unwrap_or(""), completions.iter().format(", ")),
        detail: format!("{}({})", name.unwrap_or(""), types.iter().format(", ")),
    }
}

/// Find all the visible fields in a given list. Returns the list of visible
/// fields, plus a boolean for whether the list is comprehensive (contains no
/// private fields and its item is not marked `#[non_exhaustive]`).
pub(crate) fn visible_fields(
    ctx: &RenderContext<'_>,
    fields: &[hir::Field],
    item: impl HasAttrs,
) -> Option<(Vec<hir::Field>, bool)> {
    let module = ctx.completion.module?;
    let n_fields = fields.len();
    let fields = fields
        .iter()
        .filter(|field| field.is_visible_from(ctx.db(), module))
        .copied()
        .collect::<Vec<_>>();

    let fields_omitted =
        n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
    Some((fields, fields_omitted))
}

/// Format a struct, etc. literal option for display in the completions menu.
pub(crate) fn format_literal_label(name: &str, kind: StructKind) -> SmolStr {
    match kind {
        StructKind::Tuple => SmolStr::from_iter([name, "(…)"]),
        StructKind::Record => SmolStr::from_iter([name, " {…}"]),
        StructKind::Unit => name.into(),
    }
}
<commit_msg>Reduce intermediate string allocations in render::compound::render_record and ::render_tuple<commit_after>//! Code common to structs, unions, and enum variants.

use crate::render::RenderContext;
use hir::{db::HirDatabase, HasAttrs, HasVisibility, HirDisplay, StructKind};
use ide_db::SnippetCap;
use itertools::Itertools;
use syntax::SmolStr;

/// A rendered struct, union, or enum variant, split into fields for actual
/// auto-completion (`literal`, using `field: ()`) and display in the
/// completions menu (`detail`, using `field: type`).
pub(crate) struct RenderedCompound {
    pub(crate) literal: String,
    pub(crate) detail: String,
}

/// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for
/// the `name` argument for an anonymous type.
pub(crate) fn render_record(
    db: &dyn HirDatabase,
    snippet_cap: Option<SnippetCap>,
    fields: &[hir::Field],
    name: Option<&str>,
) -> RenderedCompound {
    let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
        if snippet_cap.is_some() {
            f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1))
        } else {
            f(&format_args!("{}: ()", field.name(db)))
        }
    });

    let types = fields.iter().format_with(", ", |field, f| {
        f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db)))
    });

    RenderedCompound {
        literal: format!("{} {{ {} }}", name.unwrap_or(""), completions),
        detail: format!("{} {{ {} }}", name.unwrap_or(""), types),
    }
}

/// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for
/// the `name` argument for an anonymous type.
pub(crate) fn render_tuple(
    db: &dyn HirDatabase,
    snippet_cap: Option<SnippetCap>,
    fields: &[hir::Field],
    name: Option<&str>,
) -> RenderedCompound {
    let completions = fields.iter().enumerate().format_with(", ", |(idx, _), f| {
        if snippet_cap.is_some() {
            f(&format_args!("${{{}:()}}", idx + 1))
        } else {
            f(&format_args!("()"))
        }
    });

    let types = fields.iter().format_with(", ", |field, f| f(&field.ty(db).display(db)));

    RenderedCompound {
        literal: format!("{}({})", name.unwrap_or(""), completions),
        detail: format!("{}({})", name.unwrap_or(""), types),
    }
}

/// Find all the visible fields in a given list. Returns the list of visible
/// fields, plus a boolean for whether the list is comprehensive (contains no
/// private fields and its item is not marked `#[non_exhaustive]`).
pub(crate) fn visible_fields(
    ctx: &RenderContext<'_>,
    fields: &[hir::Field],
    item: impl HasAttrs,
) -> Option<(Vec<hir::Field>, bool)> {
    let module = ctx.completion.module?;
    let n_fields = fields.len();
    let fields = fields
        .iter()
        .filter(|field| field.is_visible_from(ctx.db(), module))
        .copied()
        .collect::<Vec<_>>();

    let fields_omitted =
        n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
    Some((fields, fields_omitted))
}

/// Format a struct, etc. literal option for display in the completions menu.
pub(crate) fn format_literal_label(name: &str, kind: StructKind) -> SmolStr {
    match kind {
        StructKind::Tuple => SmolStr::from_iter([name, "(…)"]),
        StructKind::Record => SmolStr::from_iter([name, " {…}"]),
        StructKind::Unit => name.into(),
    }
}
<|endoftext|>


<commit_before>//! Renderer for function calls.

use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
use ide_db::SymbolKind;
use itertools::Itertools;
use stdx::format_to;

use crate::{
    context::CompletionContext,
    item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit},
    render::{
        builder_ext::Params, compute_exact_name_match, compute_ref_match, compute_type_match,
        RenderContext,
    },
};

enum FuncType {
    Function,
    Method(Option<hir::Name>),
}

pub(crate) fn render_fn(
    ctx: RenderContext<'_>,
    import_to_add: Option<ImportEdit>,
    local_name: Option<hir::Name>,
    func: hir::Function,
) -> CompletionItem {
    let _p = profile::span("render_fn");
    render(ctx, local_name, func, FuncType::Function, import_to_add)
}

pub(crate) fn render_method(
    ctx: RenderContext<'_>,
    import_to_add: Option<ImportEdit>,
    receiver: Option<hir::Name>,
    local_name: Option<hir::Name>,
    func: hir::Function,
) -> CompletionItem {
    let _p = profile::span("render_method");
    render(ctx, local_name, func, FuncType::Method(receiver), import_to_add)
}

fn render(
    ctx @ RenderContext { completion, .. }: RenderContext<'_>,
    local_name: Option<hir::Name>,
    func: hir::Function,
    func_type: FuncType,
    import_to_add: Option<ImportEdit>,
) -> CompletionItem {
    let db = completion.db;

    let name = local_name.unwrap_or_else(|| func.name(db));
    let params = params(completion, func, &func_type);

    let call = match &func_type {
        FuncType::Method(Some(receiver)) => format!("{}.{}", receiver, &name).into(),
        _ => name.to_smol_str(),
    };
    let mut item = CompletionItem::new(
        if func.self_param(db).is_some() {
            CompletionItemKind::Method
        } else {
            CompletionItemKind::SymbolKind(SymbolKind::Function)
        },
        ctx.source_range(),
        call.clone(),
    );

    let ret_type = func.ret_type(db);
    let is_op_method = func
        .as_assoc_item(ctx.db())
        .and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
        .map_or(false, |trait_| completion.is_ops_trait(trait_));
    item.set_relevance(CompletionRelevance {
        type_match: compute_type_match(completion, &ret_type),
        exact_name_match: compute_exact_name_match(completion, &call),
        is_op_method,
        ..ctx.completion_relevance()
    });

    if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
        // FIXME
        // For now we don't properly calculate the edits for ref match
        // completions on methods, so we've disabled them. See #8058.
        if matches!(func_type, FuncType::Function) {
            item.ref_match(ref_match);
        }
    }

    item.set_documentation(ctx.docs(func))
        .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
        .detail(detail(db, func))
        .add_call_parens(completion, call, params);

    if import_to_add.is_none() {
        if let Some(actm) = func.as_assoc_item(db) {
            if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
                item.trait_name(trt.name(db).to_smol_str());
            }
        }
    }

    if let Some(import_to_add) = import_to_add {
        item.add_import(import_to_add);
    }
    item.lookup_by(name.to_smol_str());

    item.build()
}

fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
    let ret_ty = func.ret_type(db);
    let mut detail = format!("fn({})", params_display(db, func));
    if !ret_ty.is_unit() {
        format_to!(detail, " -> {}", ret_ty.display(db));
    }
    detail
}

fn params_display(db: &dyn HirDatabase, func: hir::Function) -> String {
    if let Some(self_param) = func.self_param(db) {
        let assoc_fn_params = func.assoc_fn_params(db);
        let params = assoc_fn_params
            .iter()
            .skip(1) // skip the self param because we are manually handling that
            .map(|p| p.ty().display(db));
        format!(
            "{}{}",
            self_param.display(db),
            params.format_with("", |display, f| {
                f(&", ")?;
                f(&display)
            })
        )
    } else {
        let assoc_fn_params = func.assoc_fn_params(db);
        assoc_fn_params.iter().map(|p| p.ty().display(db)).join(", ")
    }
}

fn params(ctx: &CompletionContext<'_>, func: hir::Function, func_type: &FuncType) -> Params {
    let (params, self_param) =
        if ctx.has_dot_receiver() || matches!(func_type, FuncType::Method(Some(_))) {
            (func.method_params(ctx.db).unwrap_or_default(), None)
        } else {
            let self_param = func.self_param(ctx.db);

            let mut assoc_params = func.assoc_fn_params(ctx.db);
            if self_param.is_some() {
                assoc_params.remove(0);
            }
            (assoc_params, self_param)
        };

    Params::Named(self_param, params)
}

#[cfg(test)]
mod tests {
    use crate::{
        tests::{check_edit, check_edit_with_config, TEST_CONFIG},
        CompletionConfig,
    };

    #[test]
    fn inserts_parens_for_function_calls() {
        cov_mark::check!(inserts_parens_for_function_calls);
        check_edit(
            "no_args",
            r#"
fn no_args() {}
fn main() { no_$0 }
"#,
            r#"
fn no_args() {}
fn main() { no_args()$0 }
"#,
        );

        check_edit(
            "with_args",
            r#"
fn with_args(x: i32, y: String) {}
fn main() { with_$0 }
"#,
            r#"
fn with_args(x: i32, y: String) {}
fn main() { with_args(${1:x}, ${2:y})$0 }
"#,
        );

        check_edit(
            "foo",
            r#"
struct S;
impl S {
    fn foo(&self) {}
}
fn bar(s: &S) { s.f$0 }
"#,
            r#"
struct S;
impl S {
    fn foo(&self) {}
}
fn bar(s: &S) { s.foo()$0 }
"#,
        );

        check_edit(
            "foo",
            r#"
struct S {}
impl S {
    fn foo(&self, x: i32) {}
}
fn bar(s: &S) {
    s.f$0
}
"#,
            r#"
struct S {}
impl S {
    fn foo(&self, x: i32) {}
}
fn bar(s: &S) {
    s.foo(${1:x})$0
}
"#,
        );

        check_edit(
            "foo",
            r#"
struct S {}
impl S {
    fn foo(&self, x: i32) {
        $0
    }
}
"#,
            r#"
struct S {}
impl S {
    fn foo(&self, x: i32) {
        self.foo(${1:x})$0
    }
}
"#,
        );
    }

    #[test]
    fn parens_for_method_call_as_assoc_fn() {
        check_edit(
            "foo",
            r#"
struct S;
impl S {
    fn foo(&self) {}
}
fn main() { S::f$0 }
"#,
            r#"
struct S;
impl S {
    fn foo(&self) {}
}
fn main() { S::foo(${1:&self})$0 }
"#,
        );
    }

    #[test]
    fn suppress_arg_snippets() {
        cov_mark::check!(suppress_arg_snippets);
        check_edit_with_config(
            CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
            "with_args",
            r#"
fn with_args(x: i32, y: String) {}
fn main() { with_$0 }
"#,
            r#"
fn with_args(x: i32, y: String) {}
fn main() { with_args($0) }
"#,
        );
    }

    #[test]
    fn strips_underscores_from_args() {
        check_edit(
            "foo",
            r#"
fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
fn main() { f$0 }
"#,
            r#"
fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
"#,
        );
    }

    #[test]
    fn insert_ref_when_matching_local_in_scope() {
        check_edit(
            "ref_arg",
            r#"
struct Foo {}
fn ref_arg(x: &Foo) {}
fn main() {
    let x = Foo {};
    ref_ar$0
}
"#,
            r#"
struct Foo {}
fn ref_arg(x: &Foo) {}
fn main() {
    let x = Foo {};
    ref_arg(${1:&x})$0
}
"#,
        );
    }

    #[test]
    fn insert_mut_ref_when_matching_local_in_scope() {
        check_edit(
            "ref_arg",
            r#"
struct Foo {}
fn ref_arg(x: &mut Foo) {}
fn main() {
    let x = Foo {};
    ref_ar$0
}
"#,
            r#"
struct Foo {}
fn ref_arg(x: &mut Foo) {}
fn main() {
    let x = Foo {};
    ref_arg(${1:&mut x})$0
}
"#,
        );
    }

    #[test]
    fn insert_ref_when_matching_local_in_scope_for_method() {
        check_edit(
            "apply_foo",
            r#"
struct Foo {}
struct Bar {}
impl Bar {
    fn apply_foo(&self, x: &Foo) {}
}

fn main() {
    let x = Foo {};
    let y = Bar {};
    y.$0
}
"#,
            r#"
struct Foo {}
struct Bar {}
impl Bar {
    fn apply_foo(&self, x: &Foo) {}
}

fn main() {
    let x = Foo {};
    let y = Bar {};
    y.apply_foo(${1:&x})$0
}
"#,
        );
    }

    #[test]
    fn trim_mut_keyword_in_func_completion() {
        check_edit(
            "take_mutably",
            r#"
fn take_mutably(mut x: &i32) {}

fn main() {
    take_m$0
}
"#,
            r#"
fn take_mutably(mut x: &i32) {}

fn main() {
    take_mutably(${1:x})$0
}
"#,
        );
    }
}
<commit_msg>[ide_completion] render if a function is async/const/unsafe in completion details<commit_after>//! Renderer for function calls.

use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
use ide_db::SymbolKind;
use itertools::Itertools;
use stdx::format_to;

use crate::{
    context::CompletionContext,
    item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit},
    render::{
        builder_ext::Params, compute_exact_name_match, compute_ref_match, compute_type_match,
        RenderContext,
    },
};

enum FuncType {
    Function,
    Method(Option<hir::Name>),
}

pub(crate) fn render_fn(
    ctx: RenderContext<'_>,
    import_to_add: Option<ImportEdit>,
    local_name: Option<hir::Name>,
    func: hir::Function,
) -> CompletionItem {
    let _p = profile::span("render_fn");
    render(ctx, local_name, func, FuncType::Function, import_to_add)
}

pub(crate) fn render_method(
    ctx: RenderContext<'_>,
    import_to_add: Option<ImportEdit>,
    receiver: Option<hir::Name>,
    local_name: Option<hir::Name>,
    func: hir::Function,
) -> CompletionItem {
    let _p = profile::span("render_method");
    render(ctx, local_name, func, FuncType::Method(receiver), import_to_add)
}

fn render(
    ctx @ RenderContext { completion, .. }: RenderContext<'_>,
    local_name: Option<hir::Name>,
    func: hir::Function,
    func_type: FuncType,
    import_to_add: Option<ImportEdit>,
) -> CompletionItem {
    let db = completion.db;

    let name = local_name.unwrap_or_else(|| func.name(db));
    let params = params(completion, func, &func_type);

    let call = match &func_type {
        FuncType::Method(Some(receiver)) => format!("{}.{}", receiver, &name).into(),
        _ => name.to_smol_str(),
    };
    let mut item = CompletionItem::new(
        if func.self_param(db).is_some() {
            CompletionItemKind::Method
        } else {
            CompletionItemKind::SymbolKind(SymbolKind::Function)
        },
        ctx.source_range(),
        call.clone(),
    );

    let ret_type = func.ret_type(db);
    let is_op_method = func
        .as_assoc_item(ctx.db())
        .and_then(|trait_| trait_.containing_trait_or_trait_impl(ctx.db()))
        .map_or(false, |trait_| completion.is_ops_trait(trait_));
    item.set_relevance(CompletionRelevance {
        type_match: compute_type_match(completion, &ret_type),
        exact_name_match: compute_exact_name_match(completion, &call),
        is_op_method,
        ..ctx.completion_relevance()
    });

    if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
        // FIXME
        // For now we don't properly calculate the edits for ref match
        // completions on methods, so we've disabled them. See #8058.
        if matches!(func_type, FuncType::Function) {
            item.ref_match(ref_match);
        }
    }

    item.set_documentation(ctx.docs(func))
        .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
        .detail(detail(db, func))
        .add_call_parens(completion, call, params);

    if import_to_add.is_none() {
        if let Some(actm) = func.as_assoc_item(db) {
            if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
                item.trait_name(trt.name(db).to_smol_str());
            }
        }
    }

    if let Some(import_to_add) = import_to_add {
        item.add_import(import_to_add);
    }
    item.lookup_by(name.to_smol_str());

    item.build()
}

fn detail(db: &dyn HirDatabase, func: hir::Function) -> String {
    let ret_ty = func.ret_type(db);
    let mut detail = String::new();

    if func.is_const(db) {
        format_to!(detail, "const ");
    }
    if func.is_async(db) {
        format_to!(detail, "async ");
    }
    if func.is_unsafe(db) {
        format_to!(detail, "unsafe ");
    }

    format_to!(detail, "fn({})", params_display(db, func));
    if !ret_ty.is_unit() {
        format_to!(detail, " -> {}", ret_ty.display(db));
    }
    detail
}

fn params_display(db: &dyn HirDatabase, func: hir::Function) -> String {
    if let Some(self_param) = func.self_param(db) {
        let assoc_fn_params = func.assoc_fn_params(db);
        let params = assoc_fn_params
            .iter()
            .skip(1) // skip the self param because we are manually handling that
            .map(|p| p.ty().display(db));
        format!(
            "{}{}",
            self_param.display(db),
            params.format_with("", |display, f| {
                f(&", ")?;
                f(&display)
            })
        )
    } else {
        let assoc_fn_params = func.assoc_fn_params(db);
        assoc_fn_params.iter().map(|p| p.ty().display(db)).join(", ")
    }
}

fn params(ctx: &CompletionContext<'_>, func: hir::Function, func_type: &FuncType) -> Params {
    let (params, self_param) =
        if ctx.has_dot_receiver() || matches!(func_type, FuncType::Method(Some(_))) {
            (func.method_params(ctx.db).unwrap_or_default(), None)
        } else {
            let self_param = func.self_param(ctx.db);

            let mut assoc_params = func.assoc_fn_params(ctx.db);
            if self_param.is_some() {
                assoc_params.remove(0);
            }
            (assoc_params, self_param)
        };

    Params::Named(self_param, params)
}

#[cfg(test)]
mod tests {
    use crate::{
        tests::{check_edit, check_edit_with_config, TEST_CONFIG},
        CompletionConfig,
    };

    #[test]
    fn inserts_parens_for_function_calls() {
        cov_mark::check!(inserts_parens_for_function_calls);
        check_edit(
            "no_args",
            r#"
fn no_args() {}
fn main() { no_$0 }
"#,
            r#"
fn no_args() {}
fn main() { no_args()$0 }
"#,
        );

        check_edit(
            "with_args",
            r#"
fn with_args(x: i32, y: String) {}
fn main() { with_$0 }
"#,
            r#"
fn with_args(x: i32, y: String) {}
fn main() { with_args(${1:x}, ${2:y})$0 }
"#,
        );

        check_edit(
            "foo",
            r#"
struct S;
impl S {
    fn foo(&self) {}
}
fn bar(s: &S) { s.f$0 }
"#,
            r#"
struct S;
impl S {
    fn foo(&self) {}
}
fn bar(s: &S) { s.foo()$0 }
"#,
        );

        check_edit(
            "foo",
            r#"
struct S {}
impl S {
    fn foo(&self, x: i32) {}
}
fn bar(s: &S) {
    s.f$0
}
"#,
            r#"
struct S {}
impl S {
    fn foo(&self, x: i32) {}
}
fn bar(s: &S) {
    s.foo(${1:x})$0
}
"#,
        );

        check_edit(
            "foo",
            r#"
struct S {}
impl S {
    fn foo(&self, x: i32) {
        $0
    }
}
"#,
            r#"
struct S {}
impl S {
    fn foo(&self, x: i32) {
        self.foo(${1:x})$0
    }
}
"#,
        );
    }

    #[test]
    fn parens_for_method_call_as_assoc_fn() {
        check_edit(
            "foo",
            r#"
struct S;
impl S {
    fn foo(&self) {}
}
fn main() { S::f$0 }
"#,
            r#"
struct S;
impl S {
    fn foo(&self) {}
}
fn main() { S::foo(${1:&self})$0 }
"#,
        );
    }

    #[test]
    fn suppress_arg_snippets() {
        cov_mark::check!(suppress_arg_snippets);
        check_edit_with_config(
            CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
            "with_args",
            r#"
fn with_args(x: i32, y: String) {}
fn main() { with_$0 }
"#,
            r#"
fn with_args(x: i32, y: String) {}
fn main() { with_args($0) }
"#,
        );
    }

    #[test]
    fn strips_underscores_from_args() {
        check_edit(
            "foo",
            r#"
fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
fn main() { f$0 }
"#,
            r#"
fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
"#,
        );
    }

    #[test]
    fn insert_ref_when_matching_local_in_scope() {
        check_edit(
            "ref_arg",
            r#"
struct Foo {}
fn ref_arg(x: &Foo) {}
fn main() {
    let x = Foo {};
    ref_ar$0
}
"#,
            r#"
struct Foo {}
fn ref_arg(x: &Foo) {}
fn main() {
    let x = Foo {};
    ref_arg(${1:&x})$0
}
"#,
        );
    }

    #[test]
    fn insert_mut_ref_when_matching_local_in_scope() {
        check_edit(
            "ref_arg",
            r#"
struct Foo {}
fn ref_arg(x: &mut Foo) {}
fn main() {
    let x = Foo {};
    ref_ar$0
}
"#,
            r#"
struct Foo {}
fn ref_arg(x: &mut Foo) {}
fn main() {
    let x = Foo {};
    ref_arg(${1:&mut x})$0
}
"#,
        );
    }

    #[test]
    fn insert_ref_when_matching_local_in_scope_for_method() {
        check_edit(
            "apply_foo",
            r#"
struct Foo {}
struct Bar {}
impl Bar {
    fn apply_foo(&self, x: &Foo) {}
}

fn main() {
    let x = Foo {};
    let y = Bar {};
    y.$0
}
"#,
            r#"
struct Foo {}
struct Bar {}
impl Bar {
    fn apply_foo(&self, x: &Foo) {}
}

fn main() {
    let x = Foo {};
    let y = Bar {};
    y.apply_foo(${1:&x})$0
}
"#,
        );
    }

    #[test]
    fn trim_mut_keyword_in_func_completion() {
        check_edit(
            "take_mutably",
            r#"
fn take_mutably(mut x: &i32) {}

fn main() {
    take_m$0
}
"#,
            r#"
fn take_mutably(mut x: &i32) {}

fn main() {
    take_mutably(${1:x})$0
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>// src/models/transaction_model.rs

/// Transaction Model

// Import Modules
// External
use ::bson::oid::ObjectId;
use ::chrono::{DateTime, Duration, Local, TimeZone};
use ::chrono::offset::utc::UTC;
// Utilities
use ::common::validation::validators as Validators;
use ::common::validation::validation_result::{ValidationResult};

/// Transaction
#[derive(Clone)]
pub struct TransactionModel {
    pub id: Option<ObjectId>,
    pub money_map_id: Option<ObjectId>,
    pub account_id: Option<ObjectId>,
    pub datetime: Option<DateTime<UTC>>,
    pub payee: Option<String>,
    pub description: Option<String>,
    pub category_id: Option<ObjectId>,
    pub amount: Option<f64>,
    pub transaction_type: Option<String>,
    pub status: Option<String>
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct PubTransactionModel {
    pub id: Option<ObjectId>,
    pub money_map_id: Option<ObjectId>,
    pub account_id: Option<ObjectId>,
    pub datetime: Option<String>,
    pub payee: Option<String>,
    pub description: Option<String>,
    pub category_id: Option<ObjectId>,
    pub amount: Option<f64>,
    pub transaction_type: Option<String>,
    pub status: Option<String>
}

// Transaction Model Methods
impl TransactionModel{

    /// Create TransactionModel from PubTransactionModel
    ///
    /// # Arguments
    /// `account` - PubTransactionModel
    ///
    /// # Returns
    /// `TransactionModel`
    pub fn new(transaction: &PubTransactionModel) -> TransactionModel{
        TransactionModel{
            id: transaction.get_id(),
            money_map_id: transaction.get_money_map_id(),
            account_id: transaction.get_account_id(),
            datetime: None,
            payee: transaction.get_payee(),
            description: transaction.get_description(),
            category_id: transaction.get_category_id(),
            amount: transaction.get_amount(),
            transaction_type: transaction.get_transaction_type(),
            status: transaction.get_status()
        }
    }

    /// Get ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - ID
    pub fn get_id(&self) -> Option<ObjectId>{
        self.id.clone()
    }

    /// Set ID
    ///
    /// # Arguments
    /// `self`
    pub fn set_id(&mut self, id: ObjectId) {
        self.id = Some(id);
    }

    /// Get Money Map ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Money Map ID
    pub fn get_money_map_id(&mut self) -> Option<ObjectId>{
        self.money_map_id.clone()
    }

    /// Get Account ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Account ID
    pub fn get_account_id(&self) -> Option<ObjectId>{
        self.account_id.clone()
    }

    /// Get Datetime
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<DateTime<UTC>>` - Datetime
    pub fn get_datetime(&self) -> Option<DateTime<UTC>>{
        self.datetime.clone()
    }

    /// Set Datetime
    ///
    /// # Arguments
    /// `self`
    /// `datetime`
    pub fn set_datetime(&mut self, datetime: Option<DateTime<UTC>>){
        self.datetime = datetime;
    }

    /// Get Payee
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Entity
    pub fn get_payee(&self) -> Option<String>{
        self.payee.clone()
    }

    /// Get Description
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - description
    pub fn get_description(&self) -> Option<String>{
        self.description.clone()
    }

    /// Get Category ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Category ID
    pub fn get_category_id(&self) -> Option<ObjectId>{
        self.category_id.clone()
    }

    /// Get Amount
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<f64>` - Amount
    pub fn get_amount(&self) -> Option<f64>{
        self.amount.clone()
    }

    /// Get Transaction Type
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Transaction Type
    pub fn get_transaction_type(&self) -> Option<String>{
        self.transaction_type.clone()
    }

    /// Get Status
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Status
    pub fn get_status(&self) -> Option<String>{
        self.status.clone()
    }

    /// Set Status
    ///
    /// # Arguments
    /// `self`
    /// `status` The Status
    pub fn set_status(&mut self, status: Option<String>){
        self.status = status;
    }
}

// Pub Transaction Model Methods
impl PubTransactionModel{

    /// Create PubTransactionModel from TransactionModel
    ///
    /// # Arguments
    /// `account` - TransactionModel
    ///
    /// # Returns
    /// 'PubTransactionModel'
    pub fn new(mut transaction: TransactionModel) -> PubTransactionModel{
        PubTransactionModel{
            id: transaction.get_id(),
            money_map_id: transaction.get_id(),
            account_id: transaction.get_id(),
            datetime: match transaction.get_datetime(){
                Some(timestamp) => {
                    Some(timestamp.to_string())
                },
                None => None
            },
            payee: transaction.get_payee(),
            description: transaction.get_description(),
            category_id: transaction.get_category_id(),
            amount: transaction.get_amount(),
            transaction_type: transaction.get_transaction_type(),
            status: transaction.get_status()
        }
    }

    /// Get ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// 'Option<ObjectId>' - ID
    pub fn get_id(&self) -> Option<ObjectId>{
        self.id.clone()
    }

    /// Get Money Map ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Money Map ID
    pub fn get_money_map_id(&self) -> Option<ObjectId>{
        self.money_map_id.clone()
    }

    /// Set Money Map ID
    ///
    /// # Arguments
    /// `self`
    /// `mm_id` Money Map ID
    pub fn set_money_map_id(&mut self, mm_id: Option<ObjectId>){
        self.money_map_id = mm_id
    }

    /// Get Account ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Account ID
    pub fn get_account_id(&self) -> Option<ObjectId>{
        self.account_id.clone()
    }

    /// Set Account ID
    ///
    /// # Arguments
    /// `self`
    /// `acc_id` Account ID
    pub fn set_account_id(&mut self, acc_id: Option<ObjectId>){
        self.account_id = acc_id
    }

    /// Get Datetime
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Datetime
    pub fn get_datetime(&self) -> Option<String>{
        self.datetime.clone()
    }

    /// Get Payee
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Entity
    pub fn get_payee(&self) -> Option<String>{
        self.payee.clone()
    }

    /// Get Description
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - description
    pub fn get_description(&self) -> Option<String>{
        self.description.clone()
    }

    /// Get Category ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Category ID
    pub fn get_category_id(&self) -> Option<ObjectId>{
        self.category_id.clone()
    }

    /// Get Amount
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<f64>` - Amount
    pub fn get_amount(&self) -> Option<f64>{
        self.amount.clone()
    }

    /// Get Transaction Type
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Transaction Type
    pub fn get_transaction_type(&self) -> Option<String>{
        self.transaction_type.clone()
    }

    /// Get Status
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Status
    pub fn get_status(&self) -> Option<String>{
        self.status.clone()
    }

    /// Validate Transaction
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `ValidationResult` - validation result
    pub fn validate(&self) -> ValidationResult{
        // Valid Options
        let transaction_types = vec!["credit".to_string(), "debit".to_string()];

        //validate Transaction
        let mut validation_result = ValidationResult::new();
        if !Validators::not_empty_string(self.payee.clone()){
            validation_result.add_error("payee".to_string(), "Payee is required.".to_string());
        }
        if !Validators::not_empty(self.amount.clone()){
            validation_result.add_error("amount".to_string(), "Amount is required.".to_string());
        }
        // TODO: Validate amount, must be numeric
        if !Validators::not_empty_string(self.transaction_type.clone()){
            validation_result.add_error("transaction_type".to_string(), "Transaction Type is required.".to_string());
        }else{
            // Validate transaction_type
            if !transaction_types.contains(&self.transaction_type.clone().unwrap()){
                validation_result.add_error("transaction_type".to_string(), "Transaction Type is not valid.".to_string());
            }
        }

        validation_result
    }//end validate
}
<commit_msg>Add transaction amount non negative validation<commit_after>// src/models/transaction_model.rs

/// Transaction Model

// Import Modules
// External
use ::bson::oid::ObjectId;
use ::chrono::{DateTime, Duration, Local, TimeZone};
use ::chrono::offset::utc::UTC;
// Utilities
use ::common::validation::validators as Validators;
use ::common::validation::validation_result::{ValidationResult};

/// Transaction
#[derive(Clone)]
pub struct TransactionModel {
    pub id: Option<ObjectId>,
    pub money_map_id: Option<ObjectId>,
    pub account_id: Option<ObjectId>,
    pub datetime: Option<DateTime<UTC>>,
    pub payee: Option<String>,
    pub description: Option<String>,
    pub category_id: Option<ObjectId>,
    pub amount: Option<f64>,
    pub transaction_type: Option<String>,
    pub status: Option<String>
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct PubTransactionModel {
    pub id: Option<ObjectId>,
    pub money_map_id: Option<ObjectId>,
    pub account_id: Option<ObjectId>,
    pub datetime: Option<String>,
    pub payee: Option<String>,
    pub description: Option<String>,
    pub category_id: Option<ObjectId>,
    pub amount: Option<f64>,
    pub transaction_type: Option<String>,
    pub status: Option<String>
}

// Transaction Model Methods
impl TransactionModel{

    /// Create TransactionModel from PubTransactionModel
    ///
    /// # Arguments
    /// `account` - PubTransactionModel
    ///
    /// # Returns
    /// `TransactionModel`
    pub fn new(transaction: &PubTransactionModel) -> TransactionModel{
        TransactionModel{
            id: transaction.get_id(),
            money_map_id: transaction.get_money_map_id(),
            account_id: transaction.get_account_id(),
            datetime: None,
            payee: transaction.get_payee(),
            description: transaction.get_description(),
            category_id: transaction.get_category_id(),
            amount: transaction.get_amount(),
            transaction_type: transaction.get_transaction_type(),
            status: transaction.get_status()
        }
    }

    /// Get ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - ID
    pub fn get_id(&self) -> Option<ObjectId>{
        self.id.clone()
    }

    /// Set ID
    ///
    /// # Arguments
    /// `self`
    pub fn set_id(&mut self, id: ObjectId) {
        self.id = Some(id);
    }

    /// Get Money Map ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Money Map ID
    pub fn get_money_map_id(&mut self) -> Option<ObjectId>{
        self.money_map_id.clone()
    }

    /// Get Account ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Account ID
    pub fn get_account_id(&self) -> Option<ObjectId>{
        self.account_id.clone()
    }

    /// Get Datetime
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<DateTime<UTC>>` - Datetime
    pub fn get_datetime(&self) -> Option<DateTime<UTC>>{
        self.datetime.clone()
    }

    /// Set Datetime
    ///
    /// # Arguments
    /// `self`
    /// `datetime`
    pub fn set_datetime(&mut self, datetime: Option<DateTime<UTC>>){
        self.datetime = datetime;
    }

    /// Get Payee
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Entity
    pub fn get_payee(&self) -> Option<String>{
        self.payee.clone()
    }

    /// Get Description
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - description
    pub fn get_description(&self) -> Option<String>{
        self.description.clone()
    }

    /// Get Category ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Category ID
    pub fn get_category_id(&self) -> Option<ObjectId>{
        self.category_id.clone()
    }

    /// Get Amount
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<f64>` - Amount
    pub fn get_amount(&self) -> Option<f64>{
        self.amount.clone()
    }

    /// Get Transaction Type
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Transaction Type
    pub fn get_transaction_type(&self) -> Option<String>{
        self.transaction_type.clone()
    }

    /// Get Status
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Status
    pub fn get_status(&self) -> Option<String>{
        self.status.clone()
    }

    /// Set Status
    ///
    /// # Arguments
    /// `self`
    /// `status` The Status
    pub fn set_status(&mut self, status: Option<String>){
        self.status = status;
    }
}

// Pub Transaction Model Methods
impl PubTransactionModel{

    /// Create PubTransactionModel from TransactionModel
    ///
    /// # Arguments
    /// `account` - TransactionModel
    ///
    /// # Returns
    /// 'PubTransactionModel'
    pub fn new(mut transaction: TransactionModel) -> PubTransactionModel{
        PubTransactionModel{
            id: transaction.get_id(),
            money_map_id: transaction.get_id(),
            account_id: transaction.get_id(),
            datetime: match transaction.get_datetime(){
                Some(timestamp) => {
                    Some(timestamp.to_string())
                },
                None => None
            },
            payee: transaction.get_payee(),
            description: transaction.get_description(),
            category_id: transaction.get_category_id(),
            amount: transaction.get_amount(),
            transaction_type: transaction.get_transaction_type(),
            status: transaction.get_status()
        }
    }

    /// Get ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// 'Option<ObjectId>' - ID
    pub fn get_id(&self) -> Option<ObjectId>{
        self.id.clone()
    }

    /// Get Money Map ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Money Map ID
    pub fn get_money_map_id(&self) -> Option<ObjectId>{
        self.money_map_id.clone()
    }

    /// Set Money Map ID
    ///
    /// # Arguments
    /// `self`
    /// `mm_id` Money Map ID
    pub fn set_money_map_id(&mut self, mm_id: Option<ObjectId>){
        self.money_map_id = mm_id
    }

    /// Get Account ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Account ID
    pub fn get_account_id(&self) -> Option<ObjectId>{
        self.account_id.clone()
    }

    /// Set Account ID
    ///
    /// # Arguments
    /// `self`
    /// `acc_id` Account ID
    pub fn set_account_id(&mut self, acc_id: Option<ObjectId>){
        self.account_id = acc_id
    }

    /// Get Datetime
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Datetime
    pub fn get_datetime(&self) -> Option<String>{
        self.datetime.clone()
    }

    /// Get Payee
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Entity
    pub fn get_payee(&self) -> Option<String>{
        self.payee.clone()
    }

    /// Get Description
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - description
    pub fn get_description(&self) -> Option<String>{
        self.description.clone()
    }

    /// Get Category ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Category ID
    pub fn get_category_id(&self) -> Option<ObjectId>{
        self.category_id.clone()
    }

    /// Get Amount
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<f64>` - Amount
    pub fn get_amount(&self) -> Option<f64>{
        self.amount.clone()
    }

    /// Get Transaction Type
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Transaction Type
    pub fn get_transaction_type(&self) -> Option<String>{
        self.transaction_type.clone()
    }

    /// Get Status
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Status
    pub fn get_status(&self) -> Option<String>{
        self.status.clone()
    }

    /// Validate Transaction
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `ValidationResult` - validation result
    pub fn validate(&self) -> ValidationResult{
        // Valid Options
        let transaction_types = vec!["credit".to_string(), "debit".to_string()];

        //validate Transaction
        let mut validation_result = ValidationResult::new();
        if !Validators::not_empty_string(self.payee.clone()){
            validation_result.add_error("payee".to_string(), "Payee is required.".to_string());
        }
        if !Validators::not_empty(self.amount.clone()){
            validation_result.add_error("amount".to_string(), "Amount is required.".to_string());
        }else{
            // Validate transaction_type
            if self.amount.unwrap() < 0 {
                validation_result.add_error("amount".to_string(), "Amount must be non-negative.".to_string());
            }
        }
        if !Validators::not_empty_string(self.transaction_type.clone()){
            validation_result.add_error("transaction_type".to_string(), "Transaction Type is required.".to_string());
        }else{
            // Validate transaction_type
            if !transaction_types.contains(&self.transaction_type.clone().unwrap()){
                validation_result.add_error("transaction_type".to_string(), "Transaction Type is not valid.".to_string());
            }
        }

        validation_result
    }//end validate
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use protocol::Driver;
use storage::Cache;
use tcp_transport::stats::TransportStats;

use super::CmdReceiver;
use super::TransportId;

type StatsMap = HashMap<TransportId, TransportStats>;

pub struct DriverTask {
    cmd_rx: CmdReceiver,
}

impl DriverTask {
    pub fn new(cmd_rx: CmdReceiver) -> DriverTask {
        DriverTask { cmd_rx: cmd_rx }
    }

    // TODO this doesn't seem to belong here
    fn compute_stats_sums(&self, map: &StatsMap) -> TransportStats {
        let mut total_stats = TransportStats::new();

        for (key, value) in map {
            total_stats.bytes_read += value.bytes_read;
            total_stats.bytes_written += value.bytes_written;
        }

        total_stats
    }

    pub fn run(&self) {
        let cache = Cache::new(64 * 1024 * 1024); // 64mb
        let mut driver = Driver::new(cache);

        // Here we store metrics per transport
        let mut transport_stats: StatsMap = HashMap::new();

        loop {
            // Receive command
            let (id, resp_tx, cmd, stats) = self.cmd_rx.recv().unwrap();
            println!("Driver received from {:?}: {:?}", id, cmd);

            // Update our stats store
            transport_stats.insert(id, stats);

            // Update the driver's view of all transport metrics
            let total_stats = self.compute_stats_sums(&transport_stats);
            driver.update_transport_stats(total_stats);

            // Execute the command
            let resp = driver.run(cmd);

            // Send response
            println!("Driver sending to {:?}: {:?}", id, &resp);
            resp_tx.send(resp).unwrap();
        }
    }
}
<commit_msg>extract method out of struct<commit_after>use std::collections::HashMap;

use protocol::Driver;
use storage::Cache;
use tcp_transport::stats::TransportStats;

use super::CmdReceiver;
use super::TransportId;

type StatsMap = HashMap<TransportId, TransportStats>;

fn compute_stats_sums(map: &StatsMap) -> TransportStats {
    let mut total_stats = TransportStats::new();

    for (key, value) in map {
        total_stats.bytes_read += value.bytes_read;
        total_stats.bytes_written += value.bytes_written;
    }

    total_stats
}

pub struct DriverTask {
    cmd_rx: CmdReceiver,
}

impl DriverTask {
    pub fn new(cmd_rx: CmdReceiver) -> DriverTask {
        DriverTask { cmd_rx: cmd_rx }
    }

    pub fn run(&self) {
        let cache = Cache::new(64 * 1024 * 1024); // 64mb
        let mut driver = Driver::new(cache);

        // Here we store metrics per transport
        let mut transport_stats: StatsMap = HashMap::new();

        loop {
            // Receive command
            let (id, resp_tx, cmd, stats) = self.cmd_rx.recv().unwrap();
            println!("Driver received from {:?}: {:?}", id, cmd);

            // Update our stats store
            transport_stats.insert(id, stats);

            // Update the driver's view of all transport metrics
            let total_stats = compute_stats_sums(&transport_stats);
            driver.update_transport_stats(total_stats);

            // Execute the command
            let resp = driver.run(cmd);

            // Send response
            println!("Driver sending to {:?}: {:?}", id, &resp);
            resp_tx.send(resp).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>use std::convert::{From, Into};
use std::error::Error;

#[derive(Debug)]
#[derive(Clone)]
#[derive(PartialEq)]
#[derive(Eq)]
// #[derive(Display)]
pub enum FileIDType {
    UUID,
}

impl FileIDType {

    fn parse(s: &str) -> Option<FileIDType> {
        unimplemented!()
    }

}

impl Into<String> for FileIDType {

    fn into(self) -> String {
        match self {
            FileIDType::UUID    => String::from("UUID"),
        }
    }
}

<commit_msg>Fixup FileIDType::parse<commit_after>use std::convert::{From, Into};
use std::error::Error;

#[derive(Debug)]
#[derive(Clone)]
#[derive(PartialEq)]
#[derive(Eq)]
// #[derive(Display)]
pub enum FileIDType {
    UUID,
}

impl FileIDType {

    pub fn parse(s: &str) -> Option<FileIDType> {
        unimplemented!()
    }

}

impl Into<String> for FileIDType {

    fn into(self) -> String {
        match self {
            FileIDType::UUID    => String::from("UUID"),
        }
    }
}

<|endoftext|>


<commit_before>// Copyright (c) IxMilia.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

extern crate dxf;
use self::dxf::entities::*;
use self::dxf::enums::*;
use self::dxf::*;

use std::time::Duration;

mod test_helpers;
use test_helpers::helpers::*;

#[test]
fn empty_header() {
    let _file = parse_drawing(
        vec!["0", "SECTION", "2", "HEADER", "0", "ENDSEC", "0", "EOF"]
            .join("\n")
            .as_str(),
    );
}

#[test]
fn specific_header_values() {
    let file = from_section(
        "HEADER",
        "
  9
$ACADMAINTVER
 70
16
  9
$ACADVER
  1
AC1012
  9
$ANGBASE
 50
5.5E1
  9
$ANGDIR
 70
1
  9
$ATTMODE
 70
1
  9
$AUNITS
 70
3
  9
$AUPREC
 70
7
  9
$CLAYER
  8
<current layer>
  9
$LUNITS
 70
6
  9
$LUPREC
 70
7"
        .trim_start(),
    );
    assert_eq!(16, file.header.maintenance_version);
    assert_eq!(AcadVersion::R13, file.header.version);
    assert_eq!(55.0, file.header.angle_zero_direction);
    assert_eq!(AngleDirection::Clockwise, file.header.angle_direction);
    assert_eq!(
        AttributeVisibility::Normal,
        file.header.attribute_visibility
    );
    assert_eq!(AngleFormat::Radians, file.header.angle_unit_format);
    assert_eq!(7, file.header.angle_unit_precision);
    assert_eq!("<current layer>", file.header.current_layer);
    assert_eq!(UnitFormat::Architectural, file.header.unit_format);
    assert_eq!(7, file.header.unit_precision);
}

#[test]
fn read_alternate_version() {
    let file = from_section(
        "HEADER",
        vec!["  9", "$ACADVER", "  1", "15.05"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!(AcadVersion::R2000, file.header.version);
}

#[test]
fn read_invalid_version() {
    let file = from_section(
        "HEADER",
        vec!["  9", "$ACADVER", "  1", "AC3.14159"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!(AcadVersion::R12, file.header.version);
}

#[test]
fn read_multi_value_variable() {
    let file = from_section(
        "HEADER",
        vec!["9", "$EXTMIN", "10", "1.1", "20", "2.2", "30", "3.3"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!(
        Point::new(1.1, 2.2, 3.3),
        file.header.minimum_drawing_extents
    )
}

#[test]
fn write_multiple_value_variable() {
    let mut file = Drawing::default();
    file.header.minimum_drawing_extents = Point::new(1.1, 2.2, 3.3);
    assert!(to_test_string(&file).contains(
        vec!["9", "$EXTMIN", " 10", "1.1", " 20", "2.2", " 30", "3.3"]
            .join("\r\n")
            .as_str()
    ));
}

#[test]
fn normalize_header() {
    let mut header = Header::default();
    header.default_text_height = -1.0; // $TEXTSIZE; normalized to 0.2
    header.trace_width = 0.0; // $TRACEWID; normalized to 0.05
    header.text_style = String::new(); // $TEXTSTYLE; normalized to "STANDARD"
    header.current_layer = String::new(); // $CLAYER; normalized to "0"
    header.current_entity_line_type = String::new(); // $CELTYPE; normalized to "BYLAYER"
    header.dimension_style_name = String::new(); // $DIMSTYLE; normalized to "STANDARD"
    header.file_name = String::new(); // $MENU; normalized to "."
    header.normalize();
    assert_eq!(0.2, header.default_text_height);
    assert_eq!(0.05, header.trace_width);
    assert_eq!("STANDARD", header.text_style);
    assert_eq!("0", header.current_layer);
    assert_eq!("BYLAYER", header.current_entity_line_type);
    assert_eq!("STANDARD", header.dimension_style_name);
    assert_eq!(".", header.file_name);
}

#[test]
fn read_header_flags() {
    let file = from_section(
        "HEADER",
        vec!["9", "$OSMODE", "70", "12"].join("\r\n").as_str(),
    );
    assert!(!file.header.get_end_point_snap());
    assert!(!file.header.get_mid_point_snap());
    assert!(file.header.get_center_snap());
    assert!(file.header.get_node_snap());
    assert!(!file.header.get_quadrant_snap());
    assert!(!file.header.get_intersection_snap());
    assert!(!file.header.get_insertion_snap());
    assert!(!file.header.get_perpendicular_snap());
    assert!(!file.header.get_tangent_snap());
    assert!(!file.header.get_nearest_snap());
    assert!(!file.header.get_apparent_intersection_snap());
    assert!(!file.header.get_extension_snap());
    assert!(!file.header.get_parallel_snap());
}

#[test]
fn write_header_flags() {
    let mut file = Drawing::default();
    file.header.set_end_point_snap(false);
    file.header.set_mid_point_snap(false);
    file.header.set_center_snap(true);
    file.header.set_node_snap(true);
    file.header.set_quadrant_snap(false);
    file.header.set_intersection_snap(false);
    file.header.set_insertion_snap(false);
    file.header.set_perpendicular_snap(false);
    file.header.set_tangent_snap(false);
    file.header.set_nearest_snap(false);
    file.header.set_apparent_intersection_snap(false);
    file.header.set_extension_snap(false);
    file.header.set_parallel_snap(false);
    assert_contains(&file, vec!["  9", "$OSMODE", " 70", "    12"].join("\r\n"));
}

#[test]
fn read_variable_with_different_codes() {
    // read $CMLSTYLE as code 7
    let file = from_section(
        "HEADER",
        vec!["  9", "$CMLSTYLE", "  7", "cml-style-7"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!("cml-style-7", file.header.current_multiline_style);

    // read $CMLSTYLE as code 2
    let file = from_section(
        "HEADER",
        vec!["  9", "$CMLSTYLE", "  2", "cml-style-2"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!("cml-style-2", file.header.current_multiline_style);
}

#[test]
fn write_variable_with_different_codes() {
    // R13 writes $CMLSTYLE as a code 7
    let mut file = Drawing::default();
    file.header.version = AcadVersion::R13;
    file.header.current_multiline_style = String::from("cml-style-7");
    assert_contains(
        &file,
        vec!["  9", "$CMLSTYLE", "  7", "cml-style-7"].join("\r\n"),
    );

    // R14+ writes $CMLSTYLE as a code 2
    let mut file = Drawing::default();
    file.header.version = AcadVersion::R14;
    file.header.current_multiline_style = String::from("cml-style-2");
    assert_contains(
        &file,
        vec!["  9", "$CMLSTYLE", "  2", "cml-style-2"].join("\r\n"),
    );
}

#[test]
fn read_drawing_edit_duration() {
    let file = from_section(
        "HEADER",
        vec!["  9", "$TDINDWG", " 40", "100.0"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!(Duration::from_secs(100), file.header.time_in_drawing);
}

#[test]
fn write_proper_handseed_on_new_file() {
    let mut drawing = Drawing::default();
    drawing
        .entities
        .push(Entity::new(EntityType::Line(Line::new(
            Point::origin(),
            Point::origin(),
        ))));
    assert_contains(&drawing, vec!["  9", "$HANDSEED", "  5", "2"].join("\r\n"));
}

#[test]
fn write_proper_handseed_on_read_file() {
    let mut drawing = from_section(
        "HEADER",
        vec!["  9", "$HANDSEED", "  5", "2"].join("\r\n").as_str(),
    );
    drawing
        .entities
        .push(Entity::new(EntityType::Line(Line::new(
            Point::origin(),
            Point::origin(),
        ))));
    assert_contains(&drawing, vec!["  9", "$HANDSEED", "  5", "3"].join("\r\n"));
}

#[test]
fn dont_write_suppressed_variables() {
    let mut drawing = Drawing::default();
    drawing.header.version = AcadVersion::R2004;
    assert_not_contains(&drawing, vec!["  9", "$HIDETEXT", "290", "0"].join("\r\n"));
}
<commit_msg>make test more explicit<commit_after>// Copyright (c) IxMilia.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

extern crate dxf;
use self::dxf::entities::*;
use self::dxf::enums::*;
use self::dxf::*;

use std::time::Duration;

mod test_helpers;
use test_helpers::helpers::*;

#[test]
fn empty_header() {
    let _file = parse_drawing(
        vec!["0", "SECTION", "2", "HEADER", "0", "ENDSEC", "0", "EOF"]
            .join("\n")
            .as_str(),
    );
}

#[test]
fn specific_header_values() {
    let file = from_section(
        "HEADER",
        "
  9
$ACADMAINTVER
 70
16
  9
$ACADVER
  1
AC1012
  9
$ANGBASE
 50
5.5E1
  9
$ANGDIR
 70
1
  9
$ATTMODE
 70
1
  9
$AUNITS
 70
3
  9
$AUPREC
 70
7
  9
$CLAYER
  8
<current layer>
  9
$LUNITS
 70
6
  9
$LUPREC
 70
7"
        .trim_start(),
    );
    assert_eq!(16, file.header.maintenance_version);
    assert_eq!(AcadVersion::R13, file.header.version);
    assert_eq!(55.0, file.header.angle_zero_direction);
    assert_eq!(AngleDirection::Clockwise, file.header.angle_direction);
    assert_eq!(
        AttributeVisibility::Normal,
        file.header.attribute_visibility
    );
    assert_eq!(AngleFormat::Radians, file.header.angle_unit_format);
    assert_eq!(7, file.header.angle_unit_precision);
    assert_eq!("<current layer>", file.header.current_layer);
    assert_eq!(UnitFormat::Architectural, file.header.unit_format);
    assert_eq!(7, file.header.unit_precision);
}

#[test]
fn read_alternate_version() {
    let file = from_section(
        "HEADER",
        vec!["  9", "$ACADVER", "  1", "15.05"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!(AcadVersion::R2000, file.header.version);
}

#[test]
fn read_invalid_version() {
    let file = from_section(
        "HEADER",
        vec!["  9", "$ACADVER", "  1", "AC3.14159"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!(AcadVersion::R12, file.header.version);
}

#[test]
fn read_multi_value_variable() {
    let file = from_section(
        "HEADER",
        vec!["9", "$EXTMIN", "10", "1.1", "20", "2.2", "30", "3.3"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!(
        Point::new(1.1, 2.2, 3.3),
        file.header.minimum_drawing_extents
    )
}

#[test]
fn write_multiple_value_variable() {
    let mut file = Drawing::default();
    file.header.minimum_drawing_extents = Point::new(1.1, 2.2, 3.3);
    assert!(to_test_string(&file).contains(
        vec!["9", "$EXTMIN", " 10", "1.1", " 20", "2.2", " 30", "3.3"]
            .join("\r\n")
            .as_str()
    ));
}

#[test]
fn normalize_header() {
    let mut header = Header::default();
    header.default_text_height = -1.0; // $TEXTSIZE; normalized to 0.2
    header.trace_width = 0.0; // $TRACEWID; normalized to 0.05
    header.text_style = String::new(); // $TEXTSTYLE; normalized to "STANDARD"
    header.current_layer = String::new(); // $CLAYER; normalized to "0"
    header.current_entity_line_type = String::new(); // $CELTYPE; normalized to "BYLAYER"
    header.dimension_style_name = String::new(); // $DIMSTYLE; normalized to "STANDARD"
    header.file_name = String::new(); // $MENU; normalized to "."
    header.normalize();
    assert_eq!(0.2, header.default_text_height);
    assert_eq!(0.05, header.trace_width);
    assert_eq!("STANDARD", header.text_style);
    assert_eq!("0", header.current_layer);
    assert_eq!("BYLAYER", header.current_entity_line_type);
    assert_eq!("STANDARD", header.dimension_style_name);
    assert_eq!(".", header.file_name);
}

#[test]
fn read_header_flags() {
    let file = from_section(
        "HEADER",
        vec!["9", "$OSMODE", "70", "12"].join("\r\n").as_str(),
    );
    assert!(!file.header.get_end_point_snap());
    assert!(!file.header.get_mid_point_snap());
    assert!(file.header.get_center_snap());
    assert!(file.header.get_node_snap());
    assert!(!file.header.get_quadrant_snap());
    assert!(!file.header.get_intersection_snap());
    assert!(!file.header.get_insertion_snap());
    assert!(!file.header.get_perpendicular_snap());
    assert!(!file.header.get_tangent_snap());
    assert!(!file.header.get_nearest_snap());
    assert!(!file.header.get_apparent_intersection_snap());
    assert!(!file.header.get_extension_snap());
    assert!(!file.header.get_parallel_snap());
}

#[test]
fn write_header_flags() {
    let mut file = Drawing::default();
    file.header.set_end_point_snap(false);
    file.header.set_mid_point_snap(false);
    file.header.set_center_snap(true);
    file.header.set_node_snap(true);
    file.header.set_quadrant_snap(false);
    file.header.set_intersection_snap(false);
    file.header.set_insertion_snap(false);
    file.header.set_perpendicular_snap(false);
    file.header.set_tangent_snap(false);
    file.header.set_nearest_snap(false);
    file.header.set_apparent_intersection_snap(false);
    file.header.set_extension_snap(false);
    file.header.set_parallel_snap(false);
    assert_contains(&file, vec!["  9", "$OSMODE", " 70", "    12"].join("\r\n"));
}

#[test]
fn read_variable_with_different_codes() {
    // read $CMLSTYLE as code 7
    let file = from_section(
        "HEADER",
        vec!["  9", "$CMLSTYLE", "  7", "cml-style-7"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!("cml-style-7", file.header.current_multiline_style);

    // read $CMLSTYLE as code 2
    let file = from_section(
        "HEADER",
        vec!["  9", "$CMLSTYLE", "  2", "cml-style-2"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!("cml-style-2", file.header.current_multiline_style);
}

#[test]
fn write_variable_with_different_codes() {
    // R13 writes $CMLSTYLE as a code 7
    let mut file = Drawing::default();
    file.header.version = AcadVersion::R13;
    file.header.current_multiline_style = String::from("cml-style-7");
    assert_contains(
        &file,
        vec!["  9", "$CMLSTYLE", "  7", "cml-style-7"].join("\r\n"),
    );

    // R14+ writes $CMLSTYLE as a code 2
    let mut file = Drawing::default();
    file.header.version = AcadVersion::R14;
    file.header.current_multiline_style = String::from("cml-style-2");
    assert_contains(
        &file,
        vec!["  9", "$CMLSTYLE", "  2", "cml-style-2"].join("\r\n"),
    );
}

#[test]
fn read_drawing_edit_duration() {
    let file = from_section(
        "HEADER",
        vec!["  9", "$TDINDWG", " 40", "100.0"]
            .join("\r\n")
            .as_str(),
    );
    assert_eq!(Duration::from_secs(100), file.header.time_in_drawing);
}

#[test]
fn write_proper_handseed_on_new_file() {
    let mut drawing = Drawing::default();
    drawing
        .entities
        .push(Entity::new(EntityType::Line(Line::new(
            Point::origin(),
            Point::origin(),
        ))));
    assert_contains(&drawing, vec!["  9", "$HANDSEED", "  5", "2"].join("\r\n"));
}

#[test]
fn write_proper_handseed_on_read_file() {
    let mut drawing = from_section(
        "HEADER",
        vec!["  9", "$HANDSEED", "  5", "2"].join("\r\n").as_str(),
    );
    drawing
        .entities
        .push(Entity::new(EntityType::Line(Line::new(
            Point::origin(),
            Point::origin(),
        ))));
    assert_contains(&drawing, vec!["  9", "$HANDSEED", "  5", "3"].join("\r\n"));
}

#[test]
fn dont_write_suppressed_variables() {
    let mut drawing = Drawing::default();
    drawing.header.version = AcadVersion::R2004;
    assert_contains(&drawing, vec!["9", "$HIDETEXT", "280"].join("\r\n"));
    assert_not_contains(&drawing, vec!["9", "$HIDETEXT", "290"].join("\r\n"));
}
<|endoftext|>


<commit_before>use syntax::{
    ast::{Expr, GenericArg},
    ast::{LetStmt, Type::InferType},
    AstNode, TextRange,
};

use crate::{
    assist_context::{AssistContext, Assists},
    AssistId, AssistKind,
};

// Assist: replace_turbofish_with_explicit_type
//
// Converts `::<_>` to an explicit type assignment.
//
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a = make$0::<i32>();
// }
// ```
// ->
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a: i32 = make();
// }
// ```
pub(crate) fn replace_turbofish_with_explicit_type(
    acc: &mut Assists,
    ctx: &AssistContext<'_>,
) -> Option<()> {
    let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;

    let initializer = let_stmt.initializer()?;

    let generic_args = match &initializer {
        Expr::MethodCallExpr(ce) => ce.generic_arg_list()?,
        Expr::CallExpr(ce) => {
            if let Expr::PathExpr(pe) = ce.expr()? {
                pe.path()?.segment()?.generic_arg_list()?
            } else {
                cov_mark::hit!(not_applicable_if_non_path_function_call);
                return None;
            }
        }
        _ => {
            cov_mark::hit!(not_applicable_if_non_function_call_initializer);
            return None;
        }
    };

    // Find range of ::<_>
    let colon2 = generic_args.coloncolon_token()?;
    let r_angle = generic_args.r_angle_token()?;
    let turbofish_range = TextRange::new(colon2.text_range().start(), r_angle.text_range().end());

    let turbofish_args: Vec<GenericArg> = generic_args.generic_args().into_iter().collect();

    // Find type of ::<_>
    if turbofish_args.len() != 1 {
        cov_mark::hit!(not_applicable_if_not_single_arg);
        return None;
    }

    // An improvement would be to check that this is correctly part of the return value of the
    // function call, or sub in the actual return type.
    let turbofish_type = &turbofish_args[0];

    let initializer_start = initializer.syntax().text_range().start();
    if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
        cov_mark::hit!(not_applicable_outside_turbofish);
        return None;
    }

    if let None = let_stmt.colon_token() {
        // If there's no colon in a let statement, then there is no explicit type.
        // let x = fn::<...>();
        let ident_range = let_stmt.pat()?.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace turbofish with explicit type",
            TextRange::new(initializer_start, turbofish_range.end()),
            |builder| {
                builder.insert(ident_range.end(), format!(": {}", turbofish_type));
                builder.delete(turbofish_range);
            },
        );
    } else if let Some(InferType(t)) = let_stmt.ty() {
        // If there's a type inference underscore, we can offer to replace it with the type in
        // the turbofish.
        // let x: _ = fn::<...>();
        let underscore_range = t.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace `_` with turbofish type",
            turbofish_range,
            |builder| {
                builder.replace(underscore_range, turbofish_type.to_string());
                builder.delete(turbofish_range);
            },
        );
    }

    None
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn replaces_turbofish_for_vec_string() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_method_calls() {
        // foo.make() is a method call which uses a different expr in the let initializer
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = foo.make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = foo.make();
}
"#,
        );
    }

    #[test]
    fn replace_turbofish_target() {
        check_assist_target(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = $0make::<Vec<String>>();
}
"#,
            r#"make::<Vec<String>>"#,
        );
    }

    #[test]
    fn not_applicable_outside_turbofish() {
        cov_mark::check!(not_applicable_outside_turbofish);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let $0a = make::<Vec<String>>();
}
"#,
        );
    }

    #[test]
    fn replace_inferred_type_placeholder() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: _ = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn not_applicable_constant_initializer() {
        cov_mark::check!(not_applicable_if_non_function_call_initializer);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = "foo"$0;
}
"#,
        );
    }

    #[test]
    fn not_applicable_non_path_function_call() {
        cov_mark::check!(not_applicable_if_non_path_function_call);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    $0let a = (|| {})();
}
"#,
        );
    }

    #[test]
    fn non_applicable_multiple_generic_args() {
        cov_mark::check!(not_applicable_if_not_single_arg);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>, i32>();
}
"#,
        );
    }
}
<commit_msg>Auto merge of #13151 - ChayimFriedman2:replace-turbofish-other-type, r=Veykril<commit_after>use hir::HirDisplay;
use syntax::{
    ast::{Expr, GenericArg, GenericArgList},
    ast::{LetStmt, Type::InferType},
    AstNode, TextRange,
};

use crate::{
    assist_context::{AssistContext, Assists},
    AssistId, AssistKind,
};

// Assist: replace_turbofish_with_explicit_type
//
// Converts `::<_>` to an explicit type assignment.
//
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a = make$0::<i32>();
// }
// ```
// ->
// ```
// fn make<T>() -> T { ) }
// fn main() {
//     let a: i32 = make();
// }
// ```
pub(crate) fn replace_turbofish_with_explicit_type(
    acc: &mut Assists,
    ctx: &AssistContext<'_>,
) -> Option<()> {
    let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;

    let initializer = let_stmt.initializer()?;

    let generic_args = generic_arg_list(&initializer)?;

    // Find range of ::<_>
    let colon2 = generic_args.coloncolon_token()?;
    let r_angle = generic_args.r_angle_token()?;
    let turbofish_range = TextRange::new(colon2.text_range().start(), r_angle.text_range().end());

    let turbofish_args: Vec<GenericArg> = generic_args.generic_args().into_iter().collect();

    // Find type of ::<_>
    if turbofish_args.len() != 1 {
        cov_mark::hit!(not_applicable_if_not_single_arg);
        return None;
    }

    // An improvement would be to check that this is correctly part of the return value of the
    // function call, or sub in the actual return type.
    let returned_type = match ctx.sema.type_of_expr(&initializer) {
        Some(returned_type) if !returned_type.original.contains_unknown() => {
            let module = ctx.sema.scope(let_stmt.syntax())?.module();
            returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
        }
        _ => {
            cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
            turbofish_args[0].to_string()
        }
    };

    let initializer_start = initializer.syntax().text_range().start();
    if ctx.offset() > turbofish_range.end() || ctx.offset() < initializer_start {
        cov_mark::hit!(not_applicable_outside_turbofish);
        return None;
    }

    if let None = let_stmt.colon_token() {
        // If there's no colon in a let statement, then there is no explicit type.
        // let x = fn::<...>();
        let ident_range = let_stmt.pat()?.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace turbofish with explicit type",
            TextRange::new(initializer_start, turbofish_range.end()),
            |builder| {
                builder.insert(ident_range.end(), format!(": {}", returned_type));
                builder.delete(turbofish_range);
            },
        );
    } else if let Some(InferType(t)) = let_stmt.ty() {
        // If there's a type inference underscore, we can offer to replace it with the type in
        // the turbofish.
        // let x: _ = fn::<...>();
        let underscore_range = t.syntax().text_range();

        return acc.add(
            AssistId("replace_turbofish_with_explicit_type", AssistKind::RefactorRewrite),
            "Replace `_` with turbofish type",
            turbofish_range,
            |builder| {
                builder.replace(underscore_range, returned_type);
                builder.delete(turbofish_range);
            },
        );
    }

    None
}

fn generic_arg_list(expr: &Expr) -> Option<GenericArgList> {
    match expr {
        Expr::MethodCallExpr(expr) => expr.generic_arg_list(),
        Expr::CallExpr(expr) => {
            if let Expr::PathExpr(pe) = expr.expr()? {
                pe.path()?.segment()?.generic_arg_list()
            } else {
                cov_mark::hit!(not_applicable_if_non_path_function_call);
                return None;
            }
        }
        Expr::AwaitExpr(expr) => generic_arg_list(&expr.expr()?),
        Expr::TryExpr(expr) => generic_arg_list(&expr.expr()?),
        _ => {
            cov_mark::hit!(not_applicable_if_non_function_call_initializer);
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn replaces_turbofish_for_vec_string() {
        cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_method_calls() {
        // foo.make() is a method call which uses a different expr in the let initializer
        cov_mark::check!(fallback_to_turbofish_type_if_type_info_not_available);
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = foo.make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = foo.make();
}
"#,
        );
    }

    #[test]
    fn replace_turbofish_target() {
        check_assist_target(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = $0make::<Vec<String>>();
}
"#,
            r#"make::<Vec<String>>"#,
        );
    }

    #[test]
    fn not_applicable_outside_turbofish() {
        cov_mark::check!(not_applicable_outside_turbofish);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let $0a = make::<Vec<String>>();
}
"#,
        );
    }

    #[test]
    fn replace_inferred_type_placeholder() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: _ = make$0::<Vec<String>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Vec<String> = make();
}
"#,
        );
    }

    #[test]
    fn not_applicable_constant_initializer() {
        cov_mark::check!(not_applicable_if_non_function_call_initializer);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = "foo"$0;
}
"#,
        );
    }

    #[test]
    fn not_applicable_non_path_function_call() {
        cov_mark::check!(not_applicable_if_non_path_function_call);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    $0let a = (|| {})();
}
"#,
        );
    }

    #[test]
    fn non_applicable_multiple_generic_args() {
        cov_mark::check!(not_applicable_if_not_single_arg);
        check_assist_not_applicable(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Vec<String>, i32>();
}
"#,
        );
    }

    #[test]
    fn replaces_turbofish_for_known_type() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
fn make<T>() -> T {}
fn main() {
    let a = make$0::<i32>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: i32 = make();
}
"#,
        );
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
//- minicore: option
fn make<T>() -> T {}
fn main() {
    let a = make$0::<Option<bool>>();
}
"#,
            r#"
fn make<T>() -> T {}
fn main() {
    let a: Option<bool> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_turbofish_not_same_type() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
//- minicore: option
fn make<T>() -> Option<T> {}
fn main() {
    let a = make$0::<u128>();
}
"#,
            r#"
fn make<T>() -> Option<T> {}
fn main() {
    let a: Option<u128> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_turbofish_for_type_with_defaulted_generic_param() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
struct HasDefault<T, U = i32>(T, U);
fn make<T>() -> HasDefault<T> {}
fn main() {
    let a = make$0::<bool>();
}
"#,
            r#"
struct HasDefault<T, U = i32>(T, U);
fn make<T>() -> HasDefault<T> {}
fn main() {
    let a: HasDefault<bool> = make();
}
"#,
        );
    }

    #[test]
    fn replaces_turbofish_try_await() {
        check_assist(
            replace_turbofish_with_explicit_type,
            r#"
//- minicore: option, future
struct Fut<T>(T);
impl<T> core::future::Future for Fut<T> {
    type Output = Option<T>;
}
fn make<T>() -> Fut<T> {}
fn main() {
    let a = make$0::<bool>().await?;
}
"#,
            r#"
struct Fut<T>(T);
impl<T> core::future::Future for Fut<T> {
    type Output = Option<T>;
}
fn make<T>() -> Fut<T> {}
fn main() {
    let a: bool = make().await?;
}
"#,
        );
    }
}
<|endoftext|>