<commit_before>//! FIXME: write short doc here

use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
use itertools::Itertools;
use ra_ide_db::RootDatabase;
use ra_syntax::{
    ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
    match_ast, SyntaxNode, TextRange,
};

use crate::FileId;
use ast::DocCommentsOwner;
use ra_cfg::CfgExpr;
use std::fmt::Display;

#[derive(Debug)]
pub struct Runnable {
    pub range: TextRange,
    pub kind: RunnableKind,
    pub cfg_exprs: Vec<CfgExpr>,
}

#[derive(Debug)]
pub enum TestId {
    Name(String),
    Path(String),
}

impl Display for TestId {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            TestId::Name(name) => write!(f, "{}", name),
            TestId::Path(path) => write!(f, "{}", path),
        }
    }
}

#[derive(Debug)]
pub enum RunnableKind {
    Test { test_id: TestId, attr: TestAttr },
    TestMod { path: String },
    Bench { test_id: TestId },
    DocTest { test_id: TestId },
    Bin,
}

pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
    let sema = Semantics::new(db);
    let source_file = sema.parse(file_id);
    source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
}

fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> {
    match_ast! {
        match item {
            ast::FnDef(it) => runnable_fn(sema, it, file_id),
            ast::Module(it) => runnable_mod(sema, it, file_id),
            _ => None,
        }
    }
}

fn runnable_fn(
    sema: &Semantics<RootDatabase>,
    fn_def: ast::FnDef,
    file_id: FileId,
) -> Option<Runnable> {
    let name_string = fn_def.name()?.text().to_string();

    let kind = if name_string == "main" {
        RunnableKind::Bin
    } else {
        let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
            let def = sema.to_def(&fn_def)?;
            let impl_trait_name =
                def.as_assoc_item(sema.db).and_then(|assoc_item| {
                    match assoc_item.container(sema.db) {
                        hir::AssocItemContainer::Trait(trait_item) => {
                            Some(trait_item.name(sema.db).to_string())
                        }
                        hir::AssocItemContainer::ImplDef(impl_def) => impl_def
                            .target_ty(sema.db)
                            .as_adt()
                            .map(|adt| adt.name(sema.db).to_string()),
                    }
                });

            let path_iter = module
                .path_to_root(sema.db)
                .into_iter()
                .rev()
                .filter_map(|it| it.name(sema.db))
                .map(|name| name.to_string());

            let path = if let Some(impl_trait_name) = impl_trait_name {
                path_iter
                    .chain(std::iter::once(impl_trait_name))
                    .chain(std::iter::once(name_string))
                    .join("::")
            } else {
                path_iter.chain(std::iter::once(name_string)).join("::")
            };

            TestId::Path(path)
        } else {
            TestId::Name(name_string)
        };

        if has_test_related_attribute(&fn_def) {
            let attr = TestAttr::from_fn(&fn_def);
            RunnableKind::Test { test_id, attr }
        } else if fn_def.has_atom_attr("bench") {
            RunnableKind::Bench { test_id }
        } else if has_doc_test(&fn_def) {
            RunnableKind::DocTest { test_id }
        } else {
            return None;
        }
    };

    let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
    let cfg_exprs =
        attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();

    Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs })
}

#[derive(Debug)]
pub struct TestAttr {
    pub ignore: bool,
}

impl TestAttr {
    fn from_fn(fn_def: &ast::FnDef) -> TestAttr {
        let ignore = fn_def
            .attrs()
            .filter_map(|attr| attr.simple_name())
            .any(|attribute_text| attribute_text == "ignore");
        TestAttr { ignore }
    }
}

/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
/// `#[test_case(...)]`, `#[tokio::test]` and similar.
/// Also a regular `#[test]` annotation is supported.
///
/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
/// but it's better than not to have the runnables for the tests at all.
fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool {
    fn_def
        .attrs()
        .filter_map(|attr| attr.path())
        .map(|path| path.syntax().to_string().to_lowercase())
        .any(|attribute_text| attribute_text.contains("test"))
}

fn has_doc_test(fn_def: &ast::FnDef) -> bool {
    fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```"))
}

fn runnable_mod(
    sema: &Semantics<RootDatabase>,
    module: ast::Module,
    file_id: FileId,
) -> Option<Runnable> {
    let has_test_function = module
        .item_list()?
        .items()
        .filter_map(|it| match it {
            ast::ModuleItem::FnDef(it) => Some(it),
            _ => None,
        })
        .any(|f| has_test_related_attribute(&f));
    if !has_test_function {
        return None;
    }
    let range = module.syntax().text_range();
    let module_def = sema.to_def(&module)?;

    let path = module_def
        .path_to_root(sema.db)
        .into_iter()
        .rev()
        .filter_map(|it| it.name(sema.db))
        .join("::");

    let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
    let cfg_exprs =
        attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();

    Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs })
}

#[cfg(test)]
mod tests {
    use insta::assert_debug_snapshot;

    use crate::mock_analysis::analysis_and_position;

    #[test]
    fn test_runnables() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        #[test]
        fn test_foo() {}

        #[test]
        #[ignore]
        fn test_foo() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
                cfg_exprs: [],
            },
            Runnable {
                range: 22..46,
                kind: Test {
                    test_id: Path(
                        "test_foo",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [],
            },
            Runnable {
                range: 47..81,
                kind: Test {
                    test_id: Path(
                        "test_foo",
                    ),
                    attr: TestAttr {
                        ignore: true,
                    },
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_doc_test() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        /// ```
        /// let x = 5;
        /// ```
        fn foo() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
                cfg_exprs: [],
            },
            Runnable {
                range: 22..64,
                kind: DocTest {
                    test_id: Path(
                        "foo",
                    ),
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_doc_test_in_impl() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        struct Data;
        impl Data {
            /// ```
            /// let x = 5;
            /// ```
            fn foo() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
                cfg_exprs: [],
            },
            Runnable {
                range: 51..105,
                kind: DocTest {
                    test_id: Path(
                        "Data::foo",
                    ),
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod test_mod {
            #[test]
            fn test_foo1() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..59,
                kind: TestMod {
                    path: "test_mod",
                },
                cfg_exprs: [],
            },
            Runnable {
                range: 28..57,
                kind: Test {
                    test_id: Path(
                        "test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_one_depth_layer_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod foo {
            mod test_mod {
                #[test]
                fn test_foo1() {}
            }
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 23..85,
                kind: TestMod {
                    path: "foo::test_mod",
                },
                cfg_exprs: [],
            },
            Runnable {
                range: 46..79,
                kind: Test {
                    test_id: Path(
                        "foo::test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_multiple_depth_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod foo {
            mod bar {
                mod test_mod {
                    #[test]
                    fn test_foo1() {}
                }
            }
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 41..115,
                kind: TestMod {
                    path: "foo::bar::test_mod",
                },
                cfg_exprs: [],
            },
            Runnable {
                range: 68..105,
                kind: Test {
                    test_id: Path(
                        "foo::bar::test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_with_feature() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs crate:foo cfg:feature=foo
        <|> //empty
        #[test]
        #[cfg(feature = "foo")]
        fn test_foo1() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..58,
                kind: Test {
                    test_id: Name(
                        "test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [
                    KeyValue {
                        key: "feature",
                        value: "foo",
                    },
                ],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_with_features() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs crate:foo cfg:feature=foo,feature=bar
        <|> //empty
        #[test]
        #[cfg(all(feature = "foo", feature = "bar"))]
        fn test_foo1() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..80,
                kind: Test {
                    test_id: Name(
                        "test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [
                    All(
                        [
                            KeyValue {
                                key: "feature",
                                value: "foo",
                            },
                            KeyValue {
                                key: "feature",
                                value: "bar",
                            },
                        ],
                    ),
                ],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_no_test_function_in_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod test_mod {
            fn foo1() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert!(runnables.is_empty())
    }
}
<commit_msg>Fix tests<commit_after>//! FIXME: write short doc here

use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
use itertools::Itertools;
use ra_ide_db::RootDatabase;
use ra_syntax::{
    ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
    match_ast, SyntaxNode, TextRange,
};

use crate::FileId;
use ast::DocCommentsOwner;
use ra_cfg::CfgExpr;
use std::fmt::Display;

#[derive(Debug)]
pub struct Runnable {
    pub range: TextRange,
    pub kind: RunnableKind,
    pub cfg_exprs: Vec<CfgExpr>,
}

#[derive(Debug)]
pub enum TestId {
    Name(String),
    Path(String),
}

impl Display for TestId {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            TestId::Name(name) => write!(f, "{}", name),
            TestId::Path(path) => write!(f, "{}", path),
        }
    }
}

#[derive(Debug)]
pub enum RunnableKind {
    Test { test_id: TestId, attr: TestAttr },
    TestMod { path: String },
    Bench { test_id: TestId },
    DocTest { test_id: TestId },
    Bin,
}

pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
    let sema = Semantics::new(db);
    let source_file = sema.parse(file_id);
    source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
}

fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> {
    match_ast! {
        match item {
            ast::FnDef(it) => runnable_fn(sema, it, file_id),
            ast::Module(it) => runnable_mod(sema, it, file_id),
            _ => None,
        }
    }
}

fn runnable_fn(
    sema: &Semantics<RootDatabase>,
    fn_def: ast::FnDef,
    file_id: FileId,
) -> Option<Runnable> {
    let name_string = fn_def.name()?.text().to_string();

    let kind = if name_string == "main" {
        RunnableKind::Bin
    } else {
        let test_id = match sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
            Some(module) => {
                let def = sema.to_def(&fn_def)?;
                let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| {
                    match assoc_item.container(sema.db) {
                        hir::AssocItemContainer::Trait(trait_item) => {
                            Some(trait_item.name(sema.db).to_string())
                        }
                        hir::AssocItemContainer::ImplDef(impl_def) => impl_def
                            .target_ty(sema.db)
                            .as_adt()
                            .map(|adt| adt.name(sema.db).to_string()),
                    }
                });

                let path_iter = module
                    .path_to_root(sema.db)
                    .into_iter()
                    .rev()
                    .filter_map(|it| it.name(sema.db))
                    .map(|name| name.to_string());

                let path = if let Some(impl_trait_name) = impl_trait_name {
                    path_iter
                        .chain(std::iter::once(impl_trait_name))
                        .chain(std::iter::once(name_string))
                        .join("::")
                } else {
                    path_iter.chain(std::iter::once(name_string)).join("::")
                };

                TestId::Path(path)
            }
            None => TestId::Name(name_string),
        };

        if has_test_related_attribute(&fn_def) {
            let attr = TestAttr::from_fn(&fn_def);
            RunnableKind::Test { test_id, attr }
        } else if fn_def.has_atom_attr("bench") {
            RunnableKind::Bench { test_id }
        } else if has_doc_test(&fn_def) {
            RunnableKind::DocTest { test_id }
        } else {
            return None;
        }
    };

    let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
    let cfg_exprs =
        attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();

    Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs })
}

#[derive(Debug)]
pub struct TestAttr {
    pub ignore: bool,
}

impl TestAttr {
    fn from_fn(fn_def: &ast::FnDef) -> TestAttr {
        let ignore = fn_def
            .attrs()
            .filter_map(|attr| attr.simple_name())
            .any(|attribute_text| attribute_text == "ignore");
        TestAttr { ignore }
    }
}

/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
/// `#[test_case(...)]`, `#[tokio::test]` and similar.
/// Also a regular `#[test]` annotation is supported.
///
/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
/// but it's better than not to have the runnables for the tests at all.
fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool {
    fn_def
        .attrs()
        .filter_map(|attr| attr.path())
        .map(|path| path.syntax().to_string().to_lowercase())
        .any(|attribute_text| attribute_text.contains("test"))
}

fn has_doc_test(fn_def: &ast::FnDef) -> bool {
    fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```"))
}

fn runnable_mod(
    sema: &Semantics<RootDatabase>,
    module: ast::Module,
    file_id: FileId,
) -> Option<Runnable> {
    let has_test_function = module
        .item_list()?
        .items()
        .filter_map(|it| match it {
            ast::ModuleItem::FnDef(it) => Some(it),
            _ => None,
        })
        .any(|f| has_test_related_attribute(&f));
    if !has_test_function {
        return None;
    }
    let range = module.syntax().text_range();
    let module_def = sema.to_def(&module)?;

    let path = module_def
        .path_to_root(sema.db)
        .into_iter()
        .rev()
        .filter_map(|it| it.name(sema.db))
        .join("::");

    let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
    let cfg_exprs =
        attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();

    Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs })
}

#[cfg(test)]
mod tests {
    use insta::assert_debug_snapshot;

    use crate::mock_analysis::analysis_and_position;

    #[test]
    fn test_runnables() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        #[test]
        fn test_foo() {}

        #[test]
        #[ignore]
        fn test_foo() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
                cfg_exprs: [],
            },
            Runnable {
                range: 22..46,
                kind: Test {
                    test_id: Path(
                        "test_foo",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [],
            },
            Runnable {
                range: 47..81,
                kind: Test {
                    test_id: Path(
                        "test_foo",
                    ),
                    attr: TestAttr {
                        ignore: true,
                    },
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_doc_test() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        /// ```
        /// let x = 5;
        /// ```
        fn foo() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
                cfg_exprs: [],
            },
            Runnable {
                range: 22..64,
                kind: DocTest {
                    test_id: Path(
                        "foo",
                    ),
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_doc_test_in_impl() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        struct Data;
        impl Data {
            /// ```
            /// let x = 5;
            /// ```
            fn foo() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
                cfg_exprs: [],
            },
            Runnable {
                range: 51..105,
                kind: DocTest {
                    test_id: Path(
                        "Data::foo",
                    ),
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod test_mod {
            #[test]
            fn test_foo1() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..59,
                kind: TestMod {
                    path: "test_mod",
                },
                cfg_exprs: [],
            },
            Runnable {
                range: 28..57,
                kind: Test {
                    test_id: Path(
                        "test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_one_depth_layer_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod foo {
            mod test_mod {
                #[test]
                fn test_foo1() {}
            }
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 23..85,
                kind: TestMod {
                    path: "foo::test_mod",
                },
                cfg_exprs: [],
            },
            Runnable {
                range: 46..79,
                kind: Test {
                    test_id: Path(
                        "foo::test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_multiple_depth_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod foo {
            mod bar {
                mod test_mod {
                    #[test]
                    fn test_foo1() {}
                }
            }
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 41..115,
                kind: TestMod {
                    path: "foo::bar::test_mod",
                },
                cfg_exprs: [],
            },
            Runnable {
                range: 68..105,
                kind: Test {
                    test_id: Path(
                        "foo::bar::test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_with_feature() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs crate:foo cfg:feature=foo
        <|> //empty
        #[test]
        #[cfg(feature = "foo")]
        fn test_foo1() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..58,
                kind: Test {
                    test_id: Path(
                        "test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [
                    KeyValue {
                        key: "feature",
                        value: "foo",
                    },
                ],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_with_features() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs crate:foo cfg:feature=foo,feature=bar
        <|> //empty
        #[test]
        #[cfg(all(feature = "foo", feature = "bar"))]
        fn test_foo1() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..80,
                kind: Test {
                    test_id: Path(
                        "test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
                cfg_exprs: [
                    All(
                        [
                            KeyValue {
                                key: "feature",
                                value: "foo",
                            },
                            KeyValue {
                                key: "feature",
                                value: "bar",
                            },
                        ],
                    ),
                ],
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_no_test_function_in_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod test_mod {
            fn foo1() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert!(runnables.is_empty())
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! See docs in build/expr/mod.rs

use build::expr::category::{Category, RvalueFunc};
use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
use hair::*;
use rustc::mir::*;
use rustc::ty;

use rustc_target::spec::abi::Abi;

impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
    /// Compile `expr`, storing the result into `destination`, which
    /// is assumed to be uninitialized.
    pub fn into_expr(
        &mut self,
        destination: &Place<'tcx>,
        mut block: BasicBlock,
        expr: Expr<'tcx>,
    ) -> BlockAnd<()> {
        debug!(
            "into_expr(destination={:?}, block={:?}, expr={:?})",
            destination, block, expr
        );

        // since we frequently have to reference `self` from within a
        // closure, where `self` would be shadowed, it's easier to
        // just use the name `this` uniformly
        let this = self;
        let expr_span = expr.span;
        let source_info = this.source_info(expr_span);

        let expr_is_block_or_scope = match expr.kind {
            ExprKind::Block { .. } => true,
            ExprKind::Scope { .. } => true,
            _ => false,
        };

        if !expr_is_block_or_scope {
            this.block_context.push(BlockFrame::SubExpr);
        }

        let block_and = match expr.kind {
            ExprKind::Scope {
                region_scope,
                lint_level,
                value,
            } => {
                let region_scope = (region_scope, source_info);
                this.in_scope(region_scope, lint_level, block, |this| {
                    this.into(destination, block, value)
                })
            }
            ExprKind::Block { body: ast_block } => {
                this.ast_block(destination, block, ast_block, source_info)
            }
            ExprKind::Match { discriminant, arms } => {
                this.match_expr(destination, expr_span, block, discriminant, arms)
            }
            ExprKind::NeverToAny { source } => {
                let source = this.hir.mirror(source);
                let is_call = match source.kind {
                    ExprKind::Call { .. } => true,
                    _ => false,
                };

                unpack!(block = this.as_local_rvalue(block, source));

                // This is an optimization. If the expression was a call then we already have an
                // unreachable block. Don't bother to terminate it and create a new one.
                if is_call {
                    block.unit()
                } else {
                    this.cfg
                        .terminate(block, source_info, TerminatorKind::Unreachable);
                    let end_block = this.cfg.start_new_block();
                    end_block.unit()
                }
            }
            ExprKind::If {
                condition: cond_expr,
                then: then_expr,
                otherwise: else_expr,
            } => {
                let operand = unpack!(block = this.as_local_operand(block, cond_expr));

                let mut then_block = this.cfg.start_new_block();
                let mut else_block = this.cfg.start_new_block();
                let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
                this.cfg.terminate(block, source_info, term);

                unpack!(then_block = this.into(destination, then_block, then_expr));
                else_block = if let Some(else_expr) = else_expr {
                    unpack!(this.into(destination, else_block, else_expr))
                } else {
                    // Body of the `if` expression without an `else` clause must return `()`, thus
                    // we implicitly generate a `else {}` if it is not specified.
                    this.cfg
                        .push_assign_unit(else_block, source_info, destination);
                    else_block
                };

                let join_block = this.cfg.start_new_block();
                this.cfg.terminate(
                    then_block,
                    source_info,
                    TerminatorKind::Goto { target: join_block },
                );
                this.cfg.terminate(
                    else_block,
                    source_info,
                    TerminatorKind::Goto { target: join_block },
                );

                join_block.unit()
            }
            ExprKind::LogicalOp { op, lhs, rhs } => {
                // And:
                //
                // [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
                //        |                          | (false)
                //        +----------false-----------+------------------> [false_block]
                //
                // Or:
                //
                // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
                //        | (true)                   | (false)
                //  [true_block]               [false_block]

                let (true_block, false_block, mut else_block, join_block) = (
                    this.cfg.start_new_block(),
                    this.cfg.start_new_block(),
                    this.cfg.start_new_block(),
                    this.cfg.start_new_block(),
                );

                let lhs = unpack!(block = this.as_local_operand(block, lhs));
                let blocks = match op {
                    LogicalOp::And => (else_block, false_block),
                    LogicalOp::Or => (true_block, else_block),
                };
                let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
                this.cfg.terminate(block, source_info, term);

                let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
                let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
                this.cfg.terminate(else_block, source_info, term);

                this.cfg.push_assign_constant(
                    true_block,
                    source_info,
                    destination,
                    Constant {
                        span: expr_span,
                        ty: this.hir.bool_ty(),
                        user_ty: None,
                        literal: this.hir.true_literal(),
                    },
                );

                this.cfg.push_assign_constant(
                    false_block,
                    source_info,
                    destination,
                    Constant {
                        span: expr_span,
                        ty: this.hir.bool_ty(),
                        user_ty: None,
                        literal: this.hir.false_literal(),
                    },
                );

                this.cfg.terminate(
                    true_block,
                    source_info,
                    TerminatorKind::Goto { target: join_block },
                );
                this.cfg.terminate(
                    false_block,
                    source_info,
                    TerminatorKind::Goto { target: join_block },
                );

                join_block.unit()
            }
            ExprKind::Loop {
                condition: opt_cond_expr,
                body,
            } => {
                // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block]
                //                  ^                               |
                //                  |                               0
                //                  |                               |
                //                  |                               v
                //           [body_block_end] <-/eval. body/-- [body_block]
                //
                // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
                //
                // [block]
                //    |
                //   [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
                //    |        ^                                         |
                // false link  |                                         |
                //    |        +-----------------------------------------+
                //    +-> [diverge_cleanup]
                // The false link is required to make sure borrowck considers unwinds through the
                // body, even when the exact code in the body cannot unwind

                let loop_block = this.cfg.start_new_block();
                let exit_block = this.cfg.start_new_block();

                // start the loop
                this.cfg.terminate(
                    block,
                    source_info,
                    TerminatorKind::Goto { target: loop_block },
                );

                this.in_breakable_scope(
                    Some(loop_block),
                    exit_block,
                    destination.clone(),
                    move |this| {
                        // conduct the test, if necessary
                        let body_block;
                        if let Some(cond_expr) = opt_cond_expr {
                            let loop_block_end;
                            let cond = unpack!(
                                loop_block_end = this.as_local_operand(loop_block, cond_expr)
                            );
                            body_block = this.cfg.start_new_block();
                            let term =
                                TerminatorKind::if_(this.hir.tcx(), cond, body_block, exit_block);
                            this.cfg.terminate(loop_block_end, source_info, term);

                            // if the test is false, there's no `break` to assign `destination`, so
                            // we have to do it; this overwrites any `break`-assigned value but it's
                            // always `()` anyway
                            this.cfg
                                .push_assign_unit(exit_block, source_info, destination);
                        } else {
                            body_block = this.cfg.start_new_block();
                            let diverge_cleanup = this.diverge_cleanup();
                            this.cfg.terminate(
                                loop_block,
                                source_info,
                                TerminatorKind::FalseUnwind {
                                    real_target: body_block,
                                    unwind: Some(diverge_cleanup),
                                },
                            )
                        }

                        // The “return” value of the loop body must always be an unit. We therefore
                        // introduce a unit temporary as the destination for the loop body.
                        let tmp = this.get_unit_temp();
                        // Execute the body, branching back to the test.
                        let body_block_end = unpack!(this.into(&tmp, body_block, body));
                        this.cfg.terminate(
                            body_block_end,
                            source_info,
                            TerminatorKind::Goto { target: loop_block },
                        );
                    },
                );
                exit_block.unit()
            }
            ExprKind::Call { ty, fun, args, from_hir_call } => {
                let intrinsic = match ty.sty {
                    ty::FnDef(def_id, _) => {
                        let f = ty.fn_sig(this.hir.tcx());
                        if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
                            Some(this.hir.tcx().item_name(def_id).as_str())
                        } else {
                            None
                        }
                    }
                    _ => None,
                };
                let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
                let fun = unpack!(block = this.as_local_operand(block, fun));
                if intrinsic == Some("move_val_init") {
                    // `move_val_init` has "magic" semantics - the second argument is
                    // always evaluated "directly" into the first one.

                    let mut args = args.into_iter();
                    let ptr = args.next().expect("0 arguments to `move_val_init`");
                    let val = args.next().expect("1 argument to `move_val_init`");
                    assert!(args.next().is_none(), ">2 arguments to `move_val_init`");

                    let ptr = this.hir.mirror(ptr);
                    let ptr_ty = ptr.ty;
                    // Create an *internal* temp for the pointer, so that unsafety
                    // checking won't complain about the raw pointer assignment.
                    let ptr_temp = this.local_decls.push(LocalDecl {
                        mutability: Mutability::Mut,
                        ty: ptr_ty,
                        user_ty: UserTypeProjections::none(),
                        name: None,
                        source_info,
                        visibility_scope: source_info.scope,
                        internal: true,
                        is_user_variable: None,
                        is_block_tail: None,
                    });
                    let ptr_temp = Place::Local(ptr_temp);
                    let block = unpack!(this.into(&ptr_temp, block, ptr));
                    this.into(&ptr_temp.deref(), block, val)
                } else {
                    let args: Vec<_> = args
                        .into_iter()
                        .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
                        .collect();

                    let success = this.cfg.start_new_block();
                    let cleanup = this.diverge_cleanup();
                    this.cfg.terminate(
                        block,
                        source_info,
                        TerminatorKind::Call {
                            func: fun,
                            args,
                            cleanup: Some(cleanup),
                            // FIXME(varkor): replace this with an uninhabitedness-based check.
                            // This requires getting access to the current module to call
                            // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
                            destination: if expr.ty.is_never() {
                                None
                            } else {
                                Some((destination.clone(), success))
                            },
                            from_hir_call,
                        },
                    );
                    success.unit()
                }
            }

            // These cases don't actually need a destination
            ExprKind::Assign { .. }
            | ExprKind::AssignOp { .. }
            | ExprKind::Continue { .. }
            | ExprKind::Break { .. }
            | ExprKind::InlineAsm { .. }
            | ExprKind::Return { .. } => {
                unpack!(block = this.stmt_expr(block, expr, None));
                this.cfg.push_assign_unit(block, source_info, destination);
                block.unit()
            }

            // Avoid creating a temporary
            ExprKind::VarRef { .. } |
            ExprKind::SelfRef |
            ExprKind::StaticRef { .. } |
            ExprKind::PlaceTypeAscription { .. } |
            ExprKind::ValueTypeAscription { .. } => {
                debug_assert!(Category::of(&expr.kind) == Some(Category::Place));

                let place = unpack!(block = this.as_place(block, expr));
                let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
                this.cfg
                    .push_assign(block, source_info, destination, rvalue);
                block.unit()
            }
            ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
                debug_assert!(Category::of(&expr.kind) == Some(Category::Place));

                // Create a "fake" temporary variable so that we check that the
                // value is Sized. Usually, this is caught in type checking, but
                // in the case of box expr there is no such check.
                if let Place::Projection(..) = destination {
                    this.local_decls
                        .push(LocalDecl::new_temp(expr.ty, expr.span));
                }

                debug_assert!(Category::of(&expr.kind) == Some(Category::Place));

                let place = unpack!(block = this.as_place(block, expr));
                let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
                this.cfg
                    .push_assign(block, source_info, destination, rvalue);
                block.unit()
            }

            // these are the cases that are more naturally handled by some other mode
            ExprKind::Unary { .. }
            | ExprKind::Binary { .. }
            | ExprKind::Box { .. }
            | ExprKind::Cast { .. }
            | ExprKind::Use { .. }
            | ExprKind::ReifyFnPointer { .. }
            | ExprKind::ClosureFnPointer { .. }
            | ExprKind::UnsafeFnPointer { .. }
            | ExprKind::Unsize { .. }
            | ExprKind::Repeat { .. }
            | ExprKind::Borrow { .. }
            | ExprKind::Array { .. }
            | ExprKind::Tuple { .. }
            | ExprKind::Adt { .. }
            | ExprKind::Closure { .. }
            | ExprKind::Literal { .. }
            | ExprKind::Yield { .. } => {
                debug_assert!(match Category::of(&expr.kind).unwrap() {
                    // should be handled above
                    Category::Rvalue(RvalueFunc::Into) => false,

                    // must be handled above or else we get an
                    // infinite loop in the builder; see
                    // e.g., `ExprKind::VarRef` above
                    Category::Place => false,

                    _ => true,
                });

                let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
                this.cfg.push_assign(block, source_info, destination, rvalue);
                block.unit()
            }
        };

        if !expr_is_block_or_scope {
            let popped = this.block_context.pop();
            assert!(popped.is_some());
        }

        block_and
    }
}
<commit_msg>Rollup merge of #56917 - sinkuu:mir_build_logicop, r=davidtwco<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! See docs in build/expr/mod.rs

use build::expr::category::{Category, RvalueFunc};
use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
use hair::*;
use rustc::mir::*;
use rustc::ty;

use rustc_target::spec::abi::Abi;

impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
    /// Compile `expr`, storing the result into `destination`, which
    /// is assumed to be uninitialized.
    pub fn into_expr(
        &mut self,
        destination: &Place<'tcx>,
        mut block: BasicBlock,
        expr: Expr<'tcx>,
    ) -> BlockAnd<()> {
        debug!(
            "into_expr(destination={:?}, block={:?}, expr={:?})",
            destination, block, expr
        );

        // since we frequently have to reference `self` from within a
        // closure, where `self` would be shadowed, it's easier to
        // just use the name `this` uniformly
        let this = self;
        let expr_span = expr.span;
        let source_info = this.source_info(expr_span);

        let expr_is_block_or_scope = match expr.kind {
            ExprKind::Block { .. } => true,
            ExprKind::Scope { .. } => true,
            _ => false,
        };

        if !expr_is_block_or_scope {
            this.block_context.push(BlockFrame::SubExpr);
        }

        let block_and = match expr.kind {
            ExprKind::Scope {
                region_scope,
                lint_level,
                value,
            } => {
                let region_scope = (region_scope, source_info);
                this.in_scope(region_scope, lint_level, block, |this| {
                    this.into(destination, block, value)
                })
            }
            ExprKind::Block { body: ast_block } => {
                this.ast_block(destination, block, ast_block, source_info)
            }
            ExprKind::Match { discriminant, arms } => {
                this.match_expr(destination, expr_span, block, discriminant, arms)
            }
            ExprKind::NeverToAny { source } => {
                let source = this.hir.mirror(source);
                let is_call = match source.kind {
                    ExprKind::Call { .. } => true,
                    _ => false,
                };

                unpack!(block = this.as_local_rvalue(block, source));

                // This is an optimization. If the expression was a call then we already have an
                // unreachable block. Don't bother to terminate it and create a new one.
                if is_call {
                    block.unit()
                } else {
                    this.cfg
                        .terminate(block, source_info, TerminatorKind::Unreachable);
                    let end_block = this.cfg.start_new_block();
                    end_block.unit()
                }
            }
            ExprKind::If {
                condition: cond_expr,
                then: then_expr,
                otherwise: else_expr,
            } => {
                let operand = unpack!(block = this.as_local_operand(block, cond_expr));

                let mut then_block = this.cfg.start_new_block();
                let mut else_block = this.cfg.start_new_block();
                let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
                this.cfg.terminate(block, source_info, term);

                unpack!(then_block = this.into(destination, then_block, then_expr));
                else_block = if let Some(else_expr) = else_expr {
                    unpack!(this.into(destination, else_block, else_expr))
                } else {
                    // Body of the `if` expression without an `else` clause must return `()`, thus
                    // we implicitly generate a `else {}` if it is not specified.
                    this.cfg
                        .push_assign_unit(else_block, source_info, destination);
                    else_block
                };

                let join_block = this.cfg.start_new_block();
                this.cfg.terminate(
                    then_block,
                    source_info,
                    TerminatorKind::Goto { target: join_block },
                );
                this.cfg.terminate(
                    else_block,
                    source_info,
                    TerminatorKind::Goto { target: join_block },
                );

                join_block.unit()
            }
            ExprKind::LogicalOp { op, lhs, rhs } => {
                // And:
                //
                // [block: If(lhs)] -true-> [else_block: dest = (rhs)]
                //        | (false)
                //  [shortcurcuit_block: dest = false]
                //
                // Or:
                //
                // [block: If(lhs)] -false-> [else_block: dest = (rhs)]
                //        | (true)
                //  [shortcurcuit_block: dest = true]

                let (shortcircuit_block, mut else_block, join_block) = (
                    this.cfg.start_new_block(),
                    this.cfg.start_new_block(),
                    this.cfg.start_new_block(),
                );

                let lhs = unpack!(block = this.as_local_operand(block, lhs));
                let blocks = match op {
                    LogicalOp::And => (else_block, shortcircuit_block),
                    LogicalOp::Or => (shortcircuit_block, else_block),
                };
                let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
                this.cfg.terminate(block, source_info, term);

                this.cfg.push_assign_constant(
                    shortcircuit_block,
                    source_info,
                    destination,
                    Constant {
                        span: expr_span,
                        ty: this.hir.bool_ty(),
                        user_ty: None,
                        literal: match op {
                            LogicalOp::And => this.hir.false_literal(),
                            LogicalOp::Or => this.hir.true_literal(),
                        },
                    },
                );
                this.cfg.terminate(
                    shortcircuit_block,
                    source_info,
                    TerminatorKind::Goto { target: join_block },
                );

                let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
                this.cfg.push_assign(
                    else_block,
                    source_info,
                    destination,
                    Rvalue::Use(rhs),
                );
                this.cfg.terminate(
                    else_block,
                    source_info,
                    TerminatorKind::Goto { target: join_block },
                );

                join_block.unit()
            }
            ExprKind::Loop {
                condition: opt_cond_expr,
                body,
            } => {
                // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block]
                //                  ^                               |
                //                  |                               0
                //                  |                               |
                //                  |                               v
                //           [body_block_end] <-/eval. body/-- [body_block]
                //
                // If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
                //
                // [block]
                //    |
                //   [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
                //    |        ^                                         |
                // false link  |                                         |
                //    |        +-----------------------------------------+
                //    +-> [diverge_cleanup]
                // The false link is required to make sure borrowck considers unwinds through the
                // body, even when the exact code in the body cannot unwind

                let loop_block = this.cfg.start_new_block();
                let exit_block = this.cfg.start_new_block();

                // start the loop
                this.cfg.terminate(
                    block,
                    source_info,
                    TerminatorKind::Goto { target: loop_block },
                );

                this.in_breakable_scope(
                    Some(loop_block),
                    exit_block,
                    destination.clone(),
                    move |this| {
                        // conduct the test, if necessary
                        let body_block;
                        if let Some(cond_expr) = opt_cond_expr {
                            let loop_block_end;
                            let cond = unpack!(
                                loop_block_end = this.as_local_operand(loop_block, cond_expr)
                            );
                            body_block = this.cfg.start_new_block();
                            let term =
                                TerminatorKind::if_(this.hir.tcx(), cond, body_block, exit_block);
                            this.cfg.terminate(loop_block_end, source_info, term);

                            // if the test is false, there's no `break` to assign `destination`, so
                            // we have to do it; this overwrites any `break`-assigned value but it's
                            // always `()` anyway
                            this.cfg
                                .push_assign_unit(exit_block, source_info, destination);
                        } else {
                            body_block = this.cfg.start_new_block();
                            let diverge_cleanup = this.diverge_cleanup();
                            this.cfg.terminate(
                                loop_block,
                                source_info,
                                TerminatorKind::FalseUnwind {
                                    real_target: body_block,
                                    unwind: Some(diverge_cleanup),
                                },
                            )
                        }

                        // The “return” value of the loop body must always be an unit. We therefore
                        // introduce a unit temporary as the destination for the loop body.
                        let tmp = this.get_unit_temp();
                        // Execute the body, branching back to the test.
                        let body_block_end = unpack!(this.into(&tmp, body_block, body));
                        this.cfg.terminate(
                            body_block_end,
                            source_info,
                            TerminatorKind::Goto { target: loop_block },
                        );
                    },
                );
                exit_block.unit()
            }
            ExprKind::Call { ty, fun, args, from_hir_call } => {
                let intrinsic = match ty.sty {
                    ty::FnDef(def_id, _) => {
                        let f = ty.fn_sig(this.hir.tcx());
                        if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
                            Some(this.hir.tcx().item_name(def_id).as_str())
                        } else {
                            None
                        }
                    }
                    _ => None,
                };
                let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
                let fun = unpack!(block = this.as_local_operand(block, fun));
                if intrinsic == Some("move_val_init") {
                    // `move_val_init` has "magic" semantics - the second argument is
                    // always evaluated "directly" into the first one.

                    let mut args = args.into_iter();
                    let ptr = args.next().expect("0 arguments to `move_val_init`");
                    let val = args.next().expect("1 argument to `move_val_init`");
                    assert!(args.next().is_none(), ">2 arguments to `move_val_init`");

                    let ptr = this.hir.mirror(ptr);
                    let ptr_ty = ptr.ty;
                    // Create an *internal* temp for the pointer, so that unsafety
                    // checking won't complain about the raw pointer assignment.
                    let ptr_temp = this.local_decls.push(LocalDecl {
                        mutability: Mutability::Mut,
                        ty: ptr_ty,
                        user_ty: UserTypeProjections::none(),
                        name: None,
                        source_info,
                        visibility_scope: source_info.scope,
                        internal: true,
                        is_user_variable: None,
                        is_block_tail: None,
                    });
                    let ptr_temp = Place::Local(ptr_temp);
                    let block = unpack!(this.into(&ptr_temp, block, ptr));
                    this.into(&ptr_temp.deref(), block, val)
                } else {
                    let args: Vec<_> = args
                        .into_iter()
                        .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
                        .collect();

                    let success = this.cfg.start_new_block();
                    let cleanup = this.diverge_cleanup();
                    this.cfg.terminate(
                        block,
                        source_info,
                        TerminatorKind::Call {
                            func: fun,
                            args,
                            cleanup: Some(cleanup),
                            // FIXME(varkor): replace this with an uninhabitedness-based check.
                            // This requires getting access to the current module to call
                            // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
                            destination: if expr.ty.is_never() {
                                None
                            } else {
                                Some((destination.clone(), success))
                            },
                            from_hir_call,
                        },
                    );
                    success.unit()
                }
            }

            // These cases don't actually need a destination
            ExprKind::Assign { .. }
            | ExprKind::AssignOp { .. }
            | ExprKind::Continue { .. }
            | ExprKind::Break { .. }
            | ExprKind::InlineAsm { .. }
            | ExprKind::Return { .. } => {
                unpack!(block = this.stmt_expr(block, expr, None));
                this.cfg.push_assign_unit(block, source_info, destination);
                block.unit()
            }

            // Avoid creating a temporary
            ExprKind::VarRef { .. } |
            ExprKind::SelfRef |
            ExprKind::StaticRef { .. } |
            ExprKind::PlaceTypeAscription { .. } |
            ExprKind::ValueTypeAscription { .. } => {
                debug_assert!(Category::of(&expr.kind) == Some(Category::Place));

                let place = unpack!(block = this.as_place(block, expr));
                let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
                this.cfg
                    .push_assign(block, source_info, destination, rvalue);
                block.unit()
            }
            ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
                debug_assert!(Category::of(&expr.kind) == Some(Category::Place));

                // Create a "fake" temporary variable so that we check that the
                // value is Sized. Usually, this is caught in type checking, but
                // in the case of box expr there is no such check.
                if let Place::Projection(..) = destination {
                    this.local_decls
                        .push(LocalDecl::new_temp(expr.ty, expr.span));
                }

                debug_assert!(Category::of(&expr.kind) == Some(Category::Place));

                let place = unpack!(block = this.as_place(block, expr));
                let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
                this.cfg
                    .push_assign(block, source_info, destination, rvalue);
                block.unit()
            }

            // these are the cases that are more naturally handled by some other mode
            ExprKind::Unary { .. }
            | ExprKind::Binary { .. }
            | ExprKind::Box { .. }
            | ExprKind::Cast { .. }
            | ExprKind::Use { .. }
            | ExprKind::ReifyFnPointer { .. }
            | ExprKind::ClosureFnPointer { .. }
            | ExprKind::UnsafeFnPointer { .. }
            | ExprKind::Unsize { .. }
            | ExprKind::Repeat { .. }
            | ExprKind::Borrow { .. }
            | ExprKind::Array { .. }
            | ExprKind::Tuple { .. }
            | ExprKind::Adt { .. }
            | ExprKind::Closure { .. }
            | ExprKind::Literal { .. }
            | ExprKind::Yield { .. } => {
                debug_assert!(match Category::of(&expr.kind).unwrap() {
                    // should be handled above
                    Category::Rvalue(RvalueFunc::Into) => false,

                    // must be handled above or else we get an
                    // infinite loop in the builder; see
                    // e.g., `ExprKind::VarRef` above
                    Category::Place => false,

                    _ => true,
                });

                let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
                this.cfg.push_assign(block, source_info, destination, rvalue);
                block.unit()
            }
        };

        if !expr_is_block_or_scope {
            let popped = this.block_context.pop();
            assert!(popped.is_some());
        }

        block_and
    }
}
<|endoftext|>


<commit_before>//! Generic node and node attribute.

use parser::binary::{Parser, ParserSource, Event, Attributes, Attribute, FbxFooter};
use parser::binary::Result as ParseResult;

/// Generic FBX node.
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
pub struct GenericNode {
    /// Node name.
    pub name: String,
    /// Node attributes.
    pub attributes: Vec<OwnedAttribute>,
    /// Child nodes.
    pub children: Vec<GenericNode>,
}

impl GenericNode {
    /// Creates a new `GenericNode`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Loads all sibling nodes from the given parser.
    ///
    /// This reads N `StartNode` and N+1 { `EndNode` or `EndFbx` }.
    pub fn load_from_parser<R: ParserSource, P: Parser<R>>(
        parser: &mut P)
        -> ParseResult<(Vec<GenericNode>, Option<FbxFooter>)> {
        let mut nodes = Vec::new();
        let mut footer = None;
        loop {
            let (name, attrs) = match parser.next_event()? {
                Event::StartFbx(_) => continue,
                Event::EndFbx(f) => {
                    footer = f.ok();
                    break;
                },
                Event::EndNode => break,
                Event::StartNode(node) => {
                    let name = node.name.to_owned();
                    let attrs = OwnedAttribute::load_attrs_from_parser_event(node.attributes)?;
                    (name, attrs)
                },
            };
            let children = GenericNode::load_from_parser(&mut parser.subtree_parser())?.0;
            let node = GenericNode {
                name: name,
                attributes: attrs,
                children: children,
            };
            nodes.push(node);
        }
        nodes.shrink_to_fit();
        Ok((nodes, footer))
    }
}

/// Owned node attribute.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum OwnedAttribute {
    /// `bool`.
    Bool(bool),
    /// `i16`.
    I16(i16),
    /// `i32`.
    I32(i32),
    /// `i64`.
    I64(i64),
    /// `f32`.
    F32(f32),
    /// `f64`.
    F64(f64),
    /// `Box<[bool]>`.
    ArrBool(Box<[bool]>),
    /// `Box<[i32]>`.
    ArrI32(Box<[i32]>),
    /// `Box<[i64]>`.
    ArrI64(Box<[i64]>),
    /// `Box<[f32]>`.
    ArrF32(Box<[f32]>),
    /// `Box<[f64]>`.
    ArrF64(Box<[f64]>),
    /// `String`.
    String(::std::result::Result<String, Vec<u8>>),
    /// `Box<[u8]>`.
    Binary(Box<[u8]>),
}

impl OwnedAttribute {
    /// Loads `OwnedAttribute`s from `parser::binary::Attributes`.
    pub fn load_attrs_from_parser_event<R: ParserSource>(mut attrs: Attributes<R>)
        -> ParseResult<Vec<Self>> {
        let mut result = Vec::with_capacity(attrs.num_attributes() as usize);
        while let Some(attr) = attrs.next_attribute()? {
            result.push(Self::load_from_parser_event(attr)?);
        }
        Ok(result)
    }

    /// Loads an `OwnedAttribute` from `parser::binary::Attribute`.
    pub fn load_from_parser_event<R: ParserSource>(attr: Attribute<R>) -> ::std::io::Result<Self> {
        use parser::binary::{PrimitiveAttribute, ArrayAttribute, SpecialAttributeType};
        Ok(match attr {
            Attribute::Primitive(PrimitiveAttribute::Bool(v)) => OwnedAttribute::Bool(v),
            Attribute::Primitive(PrimitiveAttribute::I16(v)) => OwnedAttribute::I16(v),
            Attribute::Primitive(PrimitiveAttribute::I32(v)) => OwnedAttribute::I32(v),
            Attribute::Primitive(PrimitiveAttribute::I64(v)) => OwnedAttribute::I64(v),
            Attribute::Primitive(PrimitiveAttribute::F32(v)) => OwnedAttribute::F32(v),
            Attribute::Primitive(PrimitiveAttribute::F64(v)) => OwnedAttribute::F64(v),
            Attribute::Array(ArrayAttribute::Bool(arr)) => {
                OwnedAttribute::ArrBool(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::I32(arr)) => {
                OwnedAttribute::ArrI32(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::I64(arr)) => {
                OwnedAttribute::ArrI64(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::F32(arr)) => {
                OwnedAttribute::ArrF32(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::F64(arr)) => {
                OwnedAttribute::ArrF64(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Special(v) => {
                match v.value_type() {
                    SpecialAttributeType::Binary => {
                        OwnedAttribute::Binary(v.into_vec()?.into_boxed_slice())
                    },
                    SpecialAttributeType::String => {
                        OwnedAttribute::String(match String::from_utf8(v.into_vec()?) {
                            Ok(s) => Ok(s),
                            Err(e) => Err(e.into_bytes()),
                        })
                    },
                }
            },
        })
    }
}
<commit_msg>Remove `PartialOrd` impl from `GenericNode`<commit_after>//! Generic node and node attribute.

use parser::binary::{Parser, ParserSource, Event, Attributes, Attribute, FbxFooter};
use parser::binary::Result as ParseResult;

/// Generic FBX node.
#[derive(Default, Debug, Clone, PartialEq)]
pub struct GenericNode {
    /// Node name.
    pub name: String,
    /// Node attributes.
    pub attributes: Vec<OwnedAttribute>,
    /// Child nodes.
    pub children: Vec<GenericNode>,
}

impl GenericNode {
    /// Creates a new `GenericNode`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Loads all sibling nodes from the given parser.
    ///
    /// This reads N `StartNode` and N+1 { `EndNode` or `EndFbx` }.
    pub fn load_from_parser<R: ParserSource, P: Parser<R>>(
        parser: &mut P)
        -> ParseResult<(Vec<GenericNode>, Option<FbxFooter>)> {
        let mut nodes = Vec::new();
        let mut footer = None;
        loop {
            let (name, attrs) = match parser.next_event()? {
                Event::StartFbx(_) => continue,
                Event::EndFbx(f) => {
                    footer = f.ok();
                    break;
                },
                Event::EndNode => break,
                Event::StartNode(node) => {
                    let name = node.name.to_owned();
                    let attrs = OwnedAttribute::load_attrs_from_parser_event(node.attributes)?;
                    (name, attrs)
                },
            };
            let children = GenericNode::load_from_parser(&mut parser.subtree_parser())?.0;
            let node = GenericNode {
                name: name,
                attributes: attrs,
                children: children,
            };
            nodes.push(node);
        }
        nodes.shrink_to_fit();
        Ok((nodes, footer))
    }
}

/// Owned node attribute.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum OwnedAttribute {
    /// `bool`.
    Bool(bool),
    /// `i16`.
    I16(i16),
    /// `i32`.
    I32(i32),
    /// `i64`.
    I64(i64),
    /// `f32`.
    F32(f32),
    /// `f64`.
    F64(f64),
    /// `Box<[bool]>`.
    ArrBool(Box<[bool]>),
    /// `Box<[i32]>`.
    ArrI32(Box<[i32]>),
    /// `Box<[i64]>`.
    ArrI64(Box<[i64]>),
    /// `Box<[f32]>`.
    ArrF32(Box<[f32]>),
    /// `Box<[f64]>`.
    ArrF64(Box<[f64]>),
    /// `String`.
    String(::std::result::Result<String, Vec<u8>>),
    /// `Box<[u8]>`.
    Binary(Box<[u8]>),
}

impl OwnedAttribute {
    /// Loads `OwnedAttribute`s from `parser::binary::Attributes`.
    pub fn load_attrs_from_parser_event<R: ParserSource>(mut attrs: Attributes<R>)
        -> ParseResult<Vec<Self>> {
        let mut result = Vec::with_capacity(attrs.num_attributes() as usize);
        while let Some(attr) = attrs.next_attribute()? {
            result.push(Self::load_from_parser_event(attr)?);
        }
        Ok(result)
    }

    /// Loads an `OwnedAttribute` from `parser::binary::Attribute`.
    pub fn load_from_parser_event<R: ParserSource>(attr: Attribute<R>) -> ::std::io::Result<Self> {
        use parser::binary::{PrimitiveAttribute, ArrayAttribute, SpecialAttributeType};
        Ok(match attr {
            Attribute::Primitive(PrimitiveAttribute::Bool(v)) => OwnedAttribute::Bool(v),
            Attribute::Primitive(PrimitiveAttribute::I16(v)) => OwnedAttribute::I16(v),
            Attribute::Primitive(PrimitiveAttribute::I32(v)) => OwnedAttribute::I32(v),
            Attribute::Primitive(PrimitiveAttribute::I64(v)) => OwnedAttribute::I64(v),
            Attribute::Primitive(PrimitiveAttribute::F32(v)) => OwnedAttribute::F32(v),
            Attribute::Primitive(PrimitiveAttribute::F64(v)) => OwnedAttribute::F64(v),
            Attribute::Array(ArrayAttribute::Bool(arr)) => {
                OwnedAttribute::ArrBool(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::I32(arr)) => {
                OwnedAttribute::ArrI32(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::I64(arr)) => {
                OwnedAttribute::ArrI64(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::F32(arr)) => {
                OwnedAttribute::ArrF32(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::F64(arr)) => {
                OwnedAttribute::ArrF64(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Special(v) => {
                match v.value_type() {
                    SpecialAttributeType::Binary => {
                        OwnedAttribute::Binary(v.into_vec()?.into_boxed_slice())
                    },
                    SpecialAttributeType::String => {
                        OwnedAttribute::String(match String::from_utf8(v.into_vec()?) {
                            Ok(s) => Ok(s),
                            Err(e) => Err(e.into_bytes()),
                        })
                    },
                }
            },
        })
    }
}
<|endoftext|>


<commit_before>//! Generic node and node attribute.

use parser::binary::{Result, Parser, ParserSource, Event, Attributes, Attribute, FbxFooter};

/// Generic FBX node.
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
pub struct GenericNode {
    /// Node name.
    pub name: String,
    /// Node attributes.
    pub attributes: Vec<OwnedAttribute>,
    /// Child nodes.
    pub children: Vec<GenericNode>,
}

impl GenericNode {
    /// Creates a new `GenericNode`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Loads all sibling nodes from the given parser.
    ///
    /// This reads N `StartNode` and N+1 { `EndNode` or `EndFbx` }.
    pub fn load_from_parser<R: ParserSource, P: Parser<R>>(
        parser: &mut P)
        -> Result<(Vec<GenericNode>, Option<FbxFooter>)> {
        let mut nodes = Vec::new();
        let mut footer = None;
        loop {
            let (name, attrs) = match parser.next_event()? {
                Event::StartFbx(_) => continue,
                Event::EndFbx(f) => {
                    footer = f.ok();
                    break;
                },
                Event::EndNode => break,
                Event::StartNode(node) => {
                    let name = node.name.to_owned();
                    let attrs = OwnedAttribute::load_attrs_from_parser_event(node.attributes)?;
                    (name, attrs)
                },
            };
            let children = GenericNode::load_from_parser(parser)?.0;
            let node = GenericNode {
                name: name,
                attributes: attrs,
                children: children,
            };
            nodes.push(node);
        }
        nodes.shrink_to_fit();
        Ok((nodes, footer))
    }
}

/// Owned node attribute.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum OwnedAttribute {
    /// `bool`.
    Bool(bool),
    /// `i16`.
    I16(i16),
    /// `i32`.
    I32(i32),
    /// `i64`.
    I64(i64),
    /// `f32`.
    F32(f32),
    /// `f64`.
    F64(f64),
    /// `Box<[bool]>`.
    ArrBool(Box<[bool]>),
    /// `Box<[i32]>`.
    ArrI32(Box<[i32]>),
    /// `Box<[i64]>`.
    ArrI64(Box<[i64]>),
    /// `Box<[f32]>`.
    ArrF32(Box<[f32]>),
    /// `Box<[f64]>`.
    ArrF64(Box<[f64]>),
    /// `String`.
    String(::std::result::Result<String, Vec<u8>>),
    /// `Box<[u8]>`.
    Binary(Box<[u8]>),
}

impl OwnedAttribute {
    /// Loads `OwnedAttribute`s from `parser::binary::Attributes`.
    pub fn load_attrs_from_parser_event<R: ParserSource>(mut attrs: Attributes<R>)
        -> Result<Vec<Self>> {
        let mut result = Vec::with_capacity(attrs.num_attributes() as usize);
        while let Some(attr) = attrs.next_attribute()? {
            result.push(Self::load_from_parser_event(attr)?);
        }
        Ok(result)
    }

    /// Loads an `OwnedAttribute` from `parser::binary::Attribute`.
    pub fn load_from_parser_event<R: ParserSource>(attr: Attribute<R>) -> ::std::io::Result<Self> {
        use parser::binary::{PrimitiveAttribute, ArrayAttribute, SpecialAttributeType};
        Ok(match attr {
            Attribute::Primitive(PrimitiveAttribute::Bool(v)) => OwnedAttribute::Bool(v),
            Attribute::Primitive(PrimitiveAttribute::I16(v)) => OwnedAttribute::I16(v),
            Attribute::Primitive(PrimitiveAttribute::I32(v)) => OwnedAttribute::I32(v),
            Attribute::Primitive(PrimitiveAttribute::I64(v)) => OwnedAttribute::I64(v),
            Attribute::Primitive(PrimitiveAttribute::F32(v)) => OwnedAttribute::F32(v),
            Attribute::Primitive(PrimitiveAttribute::F64(v)) => OwnedAttribute::F64(v),
            Attribute::Array(ArrayAttribute::Bool(arr)) => {
                OwnedAttribute::ArrBool(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::I32(arr)) => {
                OwnedAttribute::ArrI32(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::I64(arr)) => {
                OwnedAttribute::ArrI64(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::F32(arr)) => {
                OwnedAttribute::ArrF32(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::F64(arr)) => {
                OwnedAttribute::ArrF64(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Special(v) => {
                match v.value_type() {
                    SpecialAttributeType::Binary => {
                        OwnedAttribute::Binary(v.into_vec()?.into_boxed_slice())
                    },
                    SpecialAttributeType::String => {
                        OwnedAttribute::String(match String::from_utf8(v.into_vec()?) {
                            Ok(s) => Ok(s),
                            Err(e) => Err(e.into_bytes()),
                        })
                    },
                }
            },
        })
    }
}
<commit_msg>Use `SubtreeParser` to parse subtree<commit_after>//! Generic node and node attribute.

use parser::binary::{Result, Parser, ParserSource, Event, Attributes, Attribute, FbxFooter};

/// Generic FBX node.
#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
pub struct GenericNode {
    /// Node name.
    pub name: String,
    /// Node attributes.
    pub attributes: Vec<OwnedAttribute>,
    /// Child nodes.
    pub children: Vec<GenericNode>,
}

impl GenericNode {
    /// Creates a new `GenericNode`.
    pub fn new() -> Self {
        Default::default()
    }

    /// Loads all sibling nodes from the given parser.
    ///
    /// This reads N `StartNode` and N+1 { `EndNode` or `EndFbx` }.
    pub fn load_from_parser<R: ParserSource, P: Parser<R>>(
        parser: &mut P)
        -> Result<(Vec<GenericNode>, Option<FbxFooter>)> {
        let mut nodes = Vec::new();
        let mut footer = None;
        loop {
            let (name, attrs) = match parser.next_event()? {
                Event::StartFbx(_) => continue,
                Event::EndFbx(f) => {
                    footer = f.ok();
                    break;
                },
                Event::EndNode => break,
                Event::StartNode(node) => {
                    let name = node.name.to_owned();
                    let attrs = OwnedAttribute::load_attrs_from_parser_event(node.attributes)?;
                    (name, attrs)
                },
            };
            let children = GenericNode::load_from_parser(&mut parser.subtree_parser())?.0;
            let node = GenericNode {
                name: name,
                attributes: attrs,
                children: children,
            };
            nodes.push(node);
        }
        nodes.shrink_to_fit();
        Ok((nodes, footer))
    }
}

/// Owned node attribute.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum OwnedAttribute {
    /// `bool`.
    Bool(bool),
    /// `i16`.
    I16(i16),
    /// `i32`.
    I32(i32),
    /// `i64`.
    I64(i64),
    /// `f32`.
    F32(f32),
    /// `f64`.
    F64(f64),
    /// `Box<[bool]>`.
    ArrBool(Box<[bool]>),
    /// `Box<[i32]>`.
    ArrI32(Box<[i32]>),
    /// `Box<[i64]>`.
    ArrI64(Box<[i64]>),
    /// `Box<[f32]>`.
    ArrF32(Box<[f32]>),
    /// `Box<[f64]>`.
    ArrF64(Box<[f64]>),
    /// `String`.
    String(::std::result::Result<String, Vec<u8>>),
    /// `Box<[u8]>`.
    Binary(Box<[u8]>),
}

impl OwnedAttribute {
    /// Loads `OwnedAttribute`s from `parser::binary::Attributes`.
    pub fn load_attrs_from_parser_event<R: ParserSource>(mut attrs: Attributes<R>)
        -> Result<Vec<Self>> {
        let mut result = Vec::with_capacity(attrs.num_attributes() as usize);
        while let Some(attr) = attrs.next_attribute()? {
            result.push(Self::load_from_parser_event(attr)?);
        }
        Ok(result)
    }

    /// Loads an `OwnedAttribute` from `parser::binary::Attribute`.
    pub fn load_from_parser_event<R: ParserSource>(attr: Attribute<R>) -> ::std::io::Result<Self> {
        use parser::binary::{PrimitiveAttribute, ArrayAttribute, SpecialAttributeType};
        Ok(match attr {
            Attribute::Primitive(PrimitiveAttribute::Bool(v)) => OwnedAttribute::Bool(v),
            Attribute::Primitive(PrimitiveAttribute::I16(v)) => OwnedAttribute::I16(v),
            Attribute::Primitive(PrimitiveAttribute::I32(v)) => OwnedAttribute::I32(v),
            Attribute::Primitive(PrimitiveAttribute::I64(v)) => OwnedAttribute::I64(v),
            Attribute::Primitive(PrimitiveAttribute::F32(v)) => OwnedAttribute::F32(v),
            Attribute::Primitive(PrimitiveAttribute::F64(v)) => OwnedAttribute::F64(v),
            Attribute::Array(ArrayAttribute::Bool(arr)) => {
                OwnedAttribute::ArrBool(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::I32(arr)) => {
                OwnedAttribute::ArrI32(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::I64(arr)) => {
                OwnedAttribute::ArrI64(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::F32(arr)) => {
                OwnedAttribute::ArrF32(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Array(ArrayAttribute::F64(arr)) => {
                OwnedAttribute::ArrF64(arr.into_vec()?.into_boxed_slice())
            },
            Attribute::Special(v) => {
                match v.value_type() {
                    SpecialAttributeType::Binary => {
                        OwnedAttribute::Binary(v.into_vec()?.into_boxed_slice())
                    },
                    SpecialAttributeType::String => {
                        OwnedAttribute::String(match String::from_utf8(v.into_vec()?) {
                            Ok(s) => Ok(s),
                            Err(e) => Err(e.into_bytes()),
                        })
                    },
                }
            },
        })
    }
}
<|endoftext|>