<commit_before>use std::ops::RangeInclusive;

use hir::db::HirDatabase;
use ra_syntax::{
    algo::replace_children,
    ast::{self, edit::IndentLevel, make, Pat::TupleStructPat},
    AstNode,
    SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
};

use crate::{
    assist_ctx::{Assist, AssistCtx},
    AssistId,
};

// Assist: convert_to_guarded_return
//
// Replace a large conditional with a guarded return.
//
// ```
// fn main() {
//     <|>if cond {
//         foo();
//         bar();
//     }
// }
// ```
// ->
// ```
// fn main() {
//     if !cond {
//         return;
//     }
//     foo();
//     bar();
// }
// ```
pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
    let cond = if_expr.condition()?;
    let mut if_let_ident: Option<String> = None;

    // Check if there is an IfLet that we can handle.
    match cond.pat() {
        None => {} // No IfLet, supported.
        Some(TupleStructPat(ref pat)) if pat.args().count() == 1usize => match &pat.path() {
            Some(p) => match p.qualifier() {
                None => if_let_ident = Some(p.syntax().text().to_string()),
                _ => return None,
            },
            _ => return None,
        },
        _ => return None, // Unsupported IfLet.
    };

    let expr = cond.expr()?;
    let then_block = if_expr.then_branch()?.block()?;
    if if_expr.else_branch().is_some() {
        return None;
    }

    let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::Block::cast)?;

    if parent_block.expr()? != if_expr.clone().into() {
        return None;
    }

    // check for early return and continue
    let first_in_then_block = then_block.syntax().first_child()?;
    if ast::ReturnExpr::can_cast(first_in_then_block.kind())
        || ast::ContinueExpr::can_cast(first_in_then_block.kind())
        || first_in_then_block
            .children()
            .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
    {
        return None;
    }

    let parent_container = parent_block.syntax().parent()?.parent()?;

    let early_expression = match parent_container.kind() {
        WHILE_EXPR | LOOP_EXPR => Some("continue"),
        FN_DEF => Some("return"),
        _ => None,
    }?;

    if then_block.syntax().first_child_or_token().map(|t| t.kind() == L_CURLY).is_none() {
        return None;
    }

    then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
    let cursor_position = ctx.frange.range.start();

    ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
        let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
        let new_block = match if_let_ident {
            None => {
                // If.
                let early_expression = &(early_expression.to_owned() + ";");
                let new_if_expr =
                    if_indent_level.increase_indent(make::if_expression(&expr, early_expression));
                let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
                let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
                let end_of_then =
                    if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
                        end_of_then.prev_sibling_or_token().unwrap()
                    } else {
                        end_of_then
                    };
                let mut new_if_and_then_statements =
                    new_if_expr.syntax().children_with_tokens().chain(
                        then_block_items
                            .syntax()
                            .children_with_tokens()
                            .skip(1)
                            .take_while(|i| *i != end_of_then),
                    );
                replace_children(
                    &parent_block.syntax(),
                    RangeInclusive::new(
                        if_expr.clone().syntax().clone().into(),
                        if_expr.syntax().clone().into(),
                    ),
                    &mut new_if_and_then_statements,
                )
            }
            Some(if_let_ident) => {
                // If-let.
                let new_match_expr = if_indent_level.increase_indent(make::let_match_early(
                    expr,
                    &if_let_ident,
                    early_expression,
                ));
                let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
                let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
                let end_of_then =
                    if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
                        end_of_then.prev_sibling_or_token().unwrap()
                    } else {
                        end_of_then
                    };
                let mut then_statements = new_match_expr.syntax().children_with_tokens().chain(
                    then_block_items
                        .syntax()
                        .children_with_tokens()
                        .skip(1)
                        .take_while(|i| *i != end_of_then),
                );
                replace_children(
                    &parent_block.syntax(),
                    RangeInclusive::new(
                        if_expr.clone().syntax().clone().into(),
                        if_expr.syntax().clone().into(),
                    ),
                    &mut then_statements,
                )
            }
        };
        edit.target(if_expr.syntax().text_range());
        edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap());
        edit.set_cursor(cursor_position);
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{check_assist, check_assist_not_applicable};

    #[test]
    fn convert_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                bar();
                if<|> true {
                    foo();

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                if<|> !true {
                    return;
                }
                foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main(n: Option<String>) {
                bar();
                if<|> let Some(n) = n {
                    foo(n);

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main(n: Option<String>) {
                bar();
                le<|>t n = match n {
                    Some(it) => it,
                    None => return,
                };
                foo(n);

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_let_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main(n: Option<String>) {
                bar();
                if<|> let Ok(n) = n {
                    foo(n);

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main(n: Option<String>) {
                bar();
                le<|>t n = match n {
                    Ok(it) => it,
                    None => return,
                };
                foo(n);

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                while true {
                    if<|> true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                while true {
                    if<|> !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                while true {
                    if<|> let Some(n) = n {
                        foo(n);
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                while true {
                    le<|>t n = match n {
                        Some(it) => it,
                        None => continue,
                    };
                    foo(n);
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if<|> true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                loop {
                    if<|> !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if<|> let Some(n) = n {
                        foo(n);
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                loop {
                    le<|>t n = match n {
                        Some(it) => it,
                        None => continue,
                    };
                    foo(n);
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    return;
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_loop() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if<|> true {
                        continue;
                    }
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_return() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    return
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_else_branch() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    foo();
                } else {
                    bar()
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_aftert_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    foo();
                }
                bar();
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_inside_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if false {
                    if<|> true {
                        foo();
                    }
                }
            }
            "#,
        );
    }
}
<commit_msg>Extract common parts of match arms in convert_to_guarded_return assist.<commit_after>use std::ops::RangeInclusive;

use hir::db::HirDatabase;
use ra_syntax::{
    algo::replace_children,
    ast::{self, edit::IndentLevel, make, Block, Pat::TupleStructPat},
    AstNode,
    SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
    SyntaxNode,
};

use crate::{
    assist_ctx::{Assist, AssistCtx},
    AssistId,
};

// Assist: convert_to_guarded_return
//
// Replace a large conditional with a guarded return.
//
// ```
// fn main() {
//     <|>if cond {
//         foo();
//         bar();
//     }
// }
// ```
// ->
// ```
// fn main() {
//     if !cond {
//         return;
//     }
//     foo();
//     bar();
// }
// ```
pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
    let cond = if_expr.condition()?;
    let mut if_let_ident: Option<String> = None;

    // Check if there is an IfLet that we can handle.
    match cond.pat() {
        None => {} // No IfLet, supported.
        Some(TupleStructPat(ref pat)) if pat.args().count() == 1usize => match &pat.path() {
            Some(p) => match p.qualifier() {
                None => if_let_ident = Some(p.syntax().text().to_string()),
                _ => return None,
            },
            _ => return None,
        },
        _ => return None, // Unsupported IfLet.
    };

    let expr = cond.expr()?;
    let then_block = if_expr.then_branch()?.block()?;
    if if_expr.else_branch().is_some() {
        return None;
    }

    let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::Block::cast)?;

    if parent_block.expr()? != if_expr.clone().into() {
        return None;
    }

    // check for early return and continue
    let first_in_then_block = then_block.syntax().first_child()?;
    if ast::ReturnExpr::can_cast(first_in_then_block.kind())
        || ast::ContinueExpr::can_cast(first_in_then_block.kind())
        || first_in_then_block
            .children()
            .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
    {
        return None;
    }

    let parent_container = parent_block.syntax().parent()?.parent()?;

    let early_expression = match parent_container.kind() {
        WHILE_EXPR | LOOP_EXPR => Some("continue"),
        FN_DEF => Some("return"),
        _ => None,
    }?;

    if then_block.syntax().first_child_or_token().map(|t| t.kind() == L_CURLY).is_none() {
        return None;
    }

    then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
    let cursor_position = ctx.frange.range.start();

    ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
        let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
        let new_block = match if_let_ident {
            None => {
                // If.
                let early_expression = &(early_expression.to_owned() + ";");
                let new_expr =
                    if_indent_level.increase_indent(make::if_expression(&expr, early_expression));
                replace(new_expr, &then_block, &parent_block, &if_expr)
            }
            Some(if_let_ident) => {
                // If-let.
                let new_expr = if_indent_level.increase_indent(make::let_match_early(
                    expr,
                    &if_let_ident,
                    early_expression,
                ));
                replace(new_expr, &then_block, &parent_block, &if_expr)
            }
        };
        edit.target(if_expr.syntax().text_range());
        edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap());
        edit.set_cursor(cursor_position);

        fn replace(
            new_expr: impl AstNode,
            then_block: &Block,
            parent_block: &Block,
            if_expr: &ast::IfExpr,
        ) -> SyntaxNode {
            let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
            let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
            let end_of_then =
                if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
                    end_of_then.prev_sibling_or_token().unwrap()
                } else {
                    end_of_then
                };
            let mut then_statements = new_expr.syntax().children_with_tokens().chain(
                then_block_items
                    .syntax()
                    .children_with_tokens()
                    .skip(1)
                    .take_while(|i| *i != end_of_then),
            );
            replace_children(
                &parent_block.syntax(),
                RangeInclusive::new(
                    if_expr.clone().syntax().clone().into(),
                    if_expr.syntax().clone().into(),
                ),
                &mut then_statements,
            )
        }
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{check_assist, check_assist_not_applicable};

    #[test]
    fn convert_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                bar();
                if<|> true {
                    foo();

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                if<|> !true {
                    return;
                }
                foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main(n: Option<String>) {
                bar();
                if<|> let Some(n) = n {
                    foo(n);

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main(n: Option<String>) {
                bar();
                le<|>t n = match n {
                    Some(it) => it,
                    None => return,
                };
                foo(n);

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_let_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main(n: Option<String>) {
                bar();
                if<|> let Ok(n) = n {
                    foo(n);

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main(n: Option<String>) {
                bar();
                le<|>t n = match n {
                    Ok(it) => it,
                    None => return,
                };
                foo(n);

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                while true {
                    if<|> true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                while true {
                    if<|> !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                while true {
                    if<|> let Some(n) = n {
                        foo(n);
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                while true {
                    le<|>t n = match n {
                        Some(it) => it,
                        None => continue,
                    };
                    foo(n);
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if<|> true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                loop {
                    if<|> !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if<|> let Some(n) = n {
                        foo(n);
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                loop {
                    le<|>t n = match n {
                        Some(it) => it,
                        None => continue,
                    };
                    foo(n);
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    return;
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_loop() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if<|> true {
                        continue;
                    }
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_return() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    return
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_else_branch() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    foo();
                } else {
                    bar()
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_aftert_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if<|> true {
                    foo();
                }
                bar();
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_inside_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if false {
                    if<|> true {
                        foo();
                    }
                }
            }
            "#,
        );
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::collections::{HashMap, HashSet};
use std::io::ErrorKind;
use std::fs::{OpenOptions, read_dir};
use std::os::linux::fs::MetadataExt;
use std::path::PathBuf;
use std::str::FromStr;

use nix::Errno;
use nix::sys::stat::{S_IFBLK, S_IFMT};
use serde_json;

use devicemapper::Device;

use super::super::errors::{EngineResult, EngineError, ErrorEnum};
use super::super::types::PoolUuid;

use super::blockdev::BlockDev;
use super::device::blkdev_size;
use super::engine::DevOwnership;
use super::metadata::{BDA, StaticHeader};
use super::range_alloc::RangeAllocator;
use super::serde_structs::PoolSave;

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a vector of devices for each pool.
pub fn find_all() -> EngineResult<HashMap<PoolUuid, Vec<PathBuf>>> {

    let mut pool_map = HashMap::new();
    for dir_e in try!(read_dir("/dev")) {
        let dir_e = try!(dir_e);
        let mode = try!(dir_e.metadata()).st_mode();

        // Device node can't belong to Stratis if it is not a block device
        if mode & S_IFMT.bits() != S_IFBLK.bits() {
            continue;
        }

        let devnode = dir_e.path();

        let f = OpenOptions::new().read(true).open(&devnode);

        // There are some reasons for OpenOptions::open() to return an error
        // which are not reasons for this method to return an error.
        // Try to distinguish. Non-error conditions are:
        //
        // 1. ENXIO: The device does not exist anymore. This means that the device
        // was volatile for some reason; in that case it can not belong to
        // Stratis so it is safe to ignore it.
        //
        // 2. ENOMEDIUM: The device has no medium. An example of this case is an
        // empty optical drive.
        //
        // Note that it is better to be conservative and return with an
        // error in any case where failure to read the device could result
        // in bad data for Stratis. Additional exceptions may be added,
        // but only with a complete justification.
        if f.is_err() {
            let err = f.unwrap_err();
            match err.kind() {
                ErrorKind::NotFound => {
                    continue;
                }
                _ => {
                    if let Some(errno) = err.raw_os_error() {
                        match Errno::from_i32(errno) {
                            Errno::ENXIO | Errno::ENOMEDIUM => continue,
                            _ => return Err(EngineError::Io(err)),
                        };
                    } else {
                        return Err(EngineError::Io(err));
                    }
                }
            }
        }

        let mut f = f.expect("unreachable if f is err");
        if let DevOwnership::Ours(uuid) = try!(StaticHeader::determine_ownership(&mut f)) {
            pool_map
                .entry(uuid)
                .or_insert_with(Vec::new)
                .push(devnode)
        };
    }

    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
pub fn get_metadata(pool_uuid: PoolUuid, devnodes: &[PathBuf]) -> EngineResult<Option<PoolSave>> {

    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes {
        let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(devnode))));
        if let Some(bda) = bda {
            if *bda.pool_uuid() == pool_uuid {
                bdas.push((devnode, bda));
            }
        }
    }

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = {
        match bdas.iter()
                  .filter_map(|&(_, ref bda)| bda.last_update_time())
                  .max() {
            Some(time) => time,
            None => return Ok(None),
        }
    };

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in
        bdas.iter()
            .filter(|&&(_, ref bda)| bda.last_update_time() == Some(most_recent_time)) {

        let poolsave = OpenOptions::new()
            .read(true)
            .open(devnode)
            .ok()
            .and_then(|mut f| bda.load_state(&mut f).ok())
            .and_then(|opt| opt)
            .and_then(|data| serde_json::from_slice(&data).ok());

        if poolsave.is_some() {
            return Ok(poolsave);
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data succesfully read";
    Err(EngineError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// this list of devnodes.
/// Returns an error if the blockdevs obtained do not match the metadata.
pub fn get_blockdevs(pool_uuid: PoolUuid,
                     pool_save: &PoolSave,
                     devnodes: &[PathBuf])
                     -> EngineResult<Vec<BlockDev>> {
    let segments = pool_save
        .flex_devs
        .meta_dev
        .iter()
        .chain(pool_save.flex_devs.thin_meta_dev.iter())
        .chain(pool_save.flex_devs.thin_data_dev.iter());

    let mut segment_table = HashMap::new();
    for seg in segments {
        segment_table
            .entry(seg.0)
            .or_insert_with(Vec::default)
            .push((seg.1, seg.2))
    }

    let mut blockdevs = vec![];
    let mut devices = HashSet::new();
    for dev in devnodes {
        let device = try!(Device::from_str(&dev.to_string_lossy()));

        // If we've seen this device already, skip it.
        if !devices.insert(device) {
            continue;
        }

        let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(dev))));
        if let Some(bda) = bda {
            if *bda.pool_uuid() == pool_uuid {
                let actual_size = try!(blkdev_size(&try!(OpenOptions::new().read(true).open(dev))))
                    .sectors();

                // If size of device has changed and is less, then it is
                // possible that the segments previously allocated for this
                // blockdev no longer exist. If that is the case,
                // RangeAllocator::new() will return an error.
                let allocator =
                    try!(RangeAllocator::new(actual_size,
                                             segment_table.get(bda.dev_uuid()).unwrap_or(&vec![])));

                blockdevs.push(BlockDev::new(device, dev.clone(), bda, allocator));
            }
        }
    }

    // Verify that blockdevs found match blockdevs recorded.
    let current_uuids: HashSet<_> = blockdevs.iter().map(|b| *b.uuid()).collect();
    let recorded_uuids: HashSet<_> = pool_save.block_devs.keys().map(|u| *u).collect();

    if current_uuids != recorded_uuids {
        let err_msg = "Recorded block dev UUIDs != discovered blockdev UUIDs";
        return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if blockdevs.len() != current_uuids.len() {
        let err_msg = "Duplicate block devices found in environment";
        return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    Ok(blockdevs)
}
<commit_msg>Use cloned() instead of more general map()<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::collections::{HashMap, HashSet};
use std::io::ErrorKind;
use std::fs::{OpenOptions, read_dir};
use std::os::linux::fs::MetadataExt;
use std::path::PathBuf;
use std::str::FromStr;

use nix::Errno;
use nix::sys::stat::{S_IFBLK, S_IFMT};
use serde_json;

use devicemapper::Device;

use super::super::errors::{EngineResult, EngineError, ErrorEnum};
use super::super::types::PoolUuid;

use super::blockdev::BlockDev;
use super::device::blkdev_size;
use super::engine::DevOwnership;
use super::metadata::{BDA, StaticHeader};
use super::range_alloc::RangeAllocator;
use super::serde_structs::PoolSave;

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a vector of devices for each pool.
pub fn find_all() -> EngineResult<HashMap<PoolUuid, Vec<PathBuf>>> {

    let mut pool_map = HashMap::new();
    for dir_e in try!(read_dir("/dev")) {
        let dir_e = try!(dir_e);
        let mode = try!(dir_e.metadata()).st_mode();

        // Device node can't belong to Stratis if it is not a block device
        if mode & S_IFMT.bits() != S_IFBLK.bits() {
            continue;
        }

        let devnode = dir_e.path();

        let f = OpenOptions::new().read(true).open(&devnode);

        // There are some reasons for OpenOptions::open() to return an error
        // which are not reasons for this method to return an error.
        // Try to distinguish. Non-error conditions are:
        //
        // 1. ENXIO: The device does not exist anymore. This means that the device
        // was volatile for some reason; in that case it can not belong to
        // Stratis so it is safe to ignore it.
        //
        // 2. ENOMEDIUM: The device has no medium. An example of this case is an
        // empty optical drive.
        //
        // Note that it is better to be conservative and return with an
        // error in any case where failure to read the device could result
        // in bad data for Stratis. Additional exceptions may be added,
        // but only with a complete justification.
        if f.is_err() {
            let err = f.unwrap_err();
            match err.kind() {
                ErrorKind::NotFound => {
                    continue;
                }
                _ => {
                    if let Some(errno) = err.raw_os_error() {
                        match Errno::from_i32(errno) {
                            Errno::ENXIO | Errno::ENOMEDIUM => continue,
                            _ => return Err(EngineError::Io(err)),
                        };
                    } else {
                        return Err(EngineError::Io(err));
                    }
                }
            }
        }

        let mut f = f.expect("unreachable if f is err");
        if let DevOwnership::Ours(uuid) = try!(StaticHeader::determine_ownership(&mut f)) {
            pool_map
                .entry(uuid)
                .or_insert_with(Vec::new)
                .push(devnode)
        };
    }

    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
pub fn get_metadata(pool_uuid: PoolUuid, devnodes: &[PathBuf]) -> EngineResult<Option<PoolSave>> {

    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes {
        let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(devnode))));
        if let Some(bda) = bda {
            if *bda.pool_uuid() == pool_uuid {
                bdas.push((devnode, bda));
            }
        }
    }

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = {
        match bdas.iter()
                  .filter_map(|&(_, ref bda)| bda.last_update_time())
                  .max() {
            Some(time) => time,
            None => return Ok(None),
        }
    };

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in
        bdas.iter()
            .filter(|&&(_, ref bda)| bda.last_update_time() == Some(most_recent_time)) {

        let poolsave = OpenOptions::new()
            .read(true)
            .open(devnode)
            .ok()
            .and_then(|mut f| bda.load_state(&mut f).ok())
            .and_then(|opt| opt)
            .and_then(|data| serde_json::from_slice(&data).ok());

        if poolsave.is_some() {
            return Ok(poolsave);
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data succesfully read";
    Err(EngineError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// this list of devnodes.
/// Returns an error if the blockdevs obtained do not match the metadata.
pub fn get_blockdevs(pool_uuid: PoolUuid,
                     pool_save: &PoolSave,
                     devnodes: &[PathBuf])
                     -> EngineResult<Vec<BlockDev>> {
    let segments = pool_save
        .flex_devs
        .meta_dev
        .iter()
        .chain(pool_save.flex_devs.thin_meta_dev.iter())
        .chain(pool_save.flex_devs.thin_data_dev.iter());

    let mut segment_table = HashMap::new();
    for seg in segments {
        segment_table
            .entry(seg.0)
            .or_insert_with(Vec::default)
            .push((seg.1, seg.2))
    }

    let mut blockdevs = vec![];
    let mut devices = HashSet::new();
    for dev in devnodes {
        let device = try!(Device::from_str(&dev.to_string_lossy()));

        // If we've seen this device already, skip it.
        if !devices.insert(device) {
            continue;
        }

        let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(dev))));
        if let Some(bda) = bda {
            if *bda.pool_uuid() == pool_uuid {
                let actual_size = try!(blkdev_size(&try!(OpenOptions::new().read(true).open(dev))))
                    .sectors();

                // If size of device has changed and is less, then it is
                // possible that the segments previously allocated for this
                // blockdev no longer exist. If that is the case,
                // RangeAllocator::new() will return an error.
                let allocator =
                    try!(RangeAllocator::new(actual_size,
                                             segment_table.get(bda.dev_uuid()).unwrap_or(&vec![])));

                blockdevs.push(BlockDev::new(device, dev.clone(), bda, allocator));
            }
        }
    }

    // Verify that blockdevs found match blockdevs recorded.
    let current_uuids: HashSet<_> = blockdevs.iter().map(|b| *b.uuid()).collect();
    let recorded_uuids: HashSet<_> = pool_save.block_devs.keys().cloned().collect();

    if current_uuids != recorded_uuids {
        let err_msg = "Recorded block dev UUIDs != discovered blockdev UUIDs";
        return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if blockdevs.len() != current_uuids.len() {
        let err_msg = "Duplicate block devices found in environment";
        return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    Ok(blockdevs)
}
<|endoftext|>


<commit_before>// run-pass
#![allow(dead_code)]

// Tests that unions aren't subject to unsafe non-zero/niche-filling optimizations.
//
// For example, if a union `U` can contain both a `&T` and a `*const T`, there's definitely no
// bit-value that an `Option<U>` could reuse as `None`; this test makes sure that isn't done.
//
// Secondly, this tests the status quo to not apply such optimizations to types containing unions
// even if they're theoretically possible. (discussion: https://github.com/rust-lang/rust/issues/36394)
//
// Notably this nails down part of the behavior that `MaybeUninit` assumes: that a
// `Option<MaybeUninit<&u8>>` does not take advantage of non-zero optimization, and thus is a safe
// construct.

use std::mem::{size_of, transmute};

union U1<A: Copy> {
    a: A,
}

union U2<A: Copy, B: Copy> {
    a: A,
    b: B,
}

fn main() {
    // Unions do not participate in niche-filling/non-zero optimization...
    assert!(size_of::<Option<U2<&u8, u8>>>() > size_of::<U2<&u8, u8>>());
    assert!(size_of::<Option<U2<&u8, ()>>>() > size_of::<U2<&u8, ()>>());

    // ...even when theoretically possible:
    assert!(size_of::<Option<U1<&u8>>>() > size_of::<U1<&u8>>());
    assert!(size_of::<Option<U2<&u8, &u8>>>() > size_of::<U2<&u8, &u8>>());

    // The unused bits of the () variant can have any value.
    let zeroed: U2<&u8, ()> = unsafe { transmute(std::ptr::null::<u8>()) };

    if let None = Some(zeroed) {
        panic!()
    }
}
<commit_msg>Add non-non-zero test to union-nonzero test<commit_after>// run-pass
#![allow(dead_code)]

// Tests that unions aren't subject to unsafe non-zero/niche-filling optimizations.
//
// For example, if a union `U` can contain both a `&T` and a `*const T`, there's definitely no
// bit-value that an `Option<U>` could reuse as `None`; this test makes sure that isn't done.
//
// Secondly, this tests the status quo to not apply such optimizations to types containing unions
// even if they're theoretically possible. (discussion: https://github.com/rust-lang/rust/issues/36394)
//
// Notably this nails down part of the behavior that `MaybeUninit` assumes: that a
// `Option<MaybeUninit<&u8>>` does not take advantage of non-zero optimization, and thus is a safe
// construct.

use std::mem::{size_of, transmute};

union U1<A: Copy> {
    a: A,
}

union U2<A: Copy, B: Copy> {
    a: A,
    b: B,
}

// Option<E> uses a value other than 0 and 1 as None
#[derive(Clone,Copy)]
enum E {
    A = 0,
    B = 1,
}

fn main() {
    // Unions do not participate in niche-filling/non-zero optimization...
    assert!(size_of::<Option<U2<&u8, u8>>>() > size_of::<U2<&u8, u8>>());
    assert!(size_of::<Option<U2<&u8, ()>>>() > size_of::<U2<&u8, ()>>());
    assert!(size_of::<Option<U2<u8, E>>>() > size_of::<U2<u8, E>>());

    // ...even when theoretically possible:
    assert!(size_of::<Option<U1<&u8>>>() > size_of::<U1<&u8>>());
    assert!(size_of::<Option<U2<&u8, &u8>>>() > size_of::<U2<&u8, &u8>>());

    // The unused bits of the () variant can have any value.
    let zeroed: U2<&u8, ()> = unsafe { transmute(std::ptr::null::<u8>()) };

    if let None = Some(zeroed) {
        panic!()
    }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};

use grep::regex::{RegexMatcher, RegexMatcherBuilder};
use grep::searcher::{Searcher, SearcherBuilder, Sink, SinkContext, SinkContextKind, SinkMatch};

use ignore::Walk;

use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(name = "fnr")]
/// Look for things, optionally replace them.
struct Opts {
    /// Run search in case insensitive mode
    #[structopt(short, long)]
    insensitive: bool,

    #[structopt(short, long)]
    prompt: bool,

    /// What to search for.
    #[structopt(name = "FIND")]
    find: String,

    /// What to replace it with. Required unless `prompt` is set.
    #[structopt(name = "REPLACE")]
    replace: Option<String>,

    /// Locations to search. Current directory if not specified.
    #[structopt(name = "PATH", parse(from_os_str))]
    paths: Vec<PathBuf>,
}

#[derive(Debug, Clone)]
struct Line(u64, String);

#[derive(Debug)]
struct SearchMatch {
    line: Line,
    context_pre: Vec<Line>,
    context_post: Vec<Line>,
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum MatchState {
    Init,
    Before,
    Match,
    After,
}

struct SearchMatchCollector {
    state: MatchState,

    cur_context_pre: Vec<Line>,
    cur_context_post: Vec<Line>,
    cur_match_line: Option<Line>,
}

impl SearchMatchCollector {
    fn new() -> SearchMatchCollector {
        return SearchMatchCollector {
            state: MatchState::Init,
            cur_match_line: None,
            cur_context_pre: Vec::new(),
            cur_context_post: Vec::new(),
        };
    }

    fn maybe_emit(&mut self) {
        if let Some(line) = &self.cur_match_line {
            for ctx in &self.cur_context_pre {
                println!("-- {:?}", ctx);
            }
            println!("-- {:?}", line);
            for ctx in &self.cur_context_post {
                println!("-- {:?}", ctx);
            }
            println!("---");

            self.reset();
        }
    }

    fn reset(&mut self) {
        self.state = MatchState::Init;

        self.cur_match_line = None;
        self.cur_context_pre.clear();
        self.cur_context_post.clear();
    }

    #[inline]
    fn transition(&mut self, next: MatchState) {
        match (self.state, next) {
            // No transition, nothing to do
            (cur, next) if cur == next => {}

            // Beginning a new match or ending a previous one
            (_, MatchState::Before) | (MatchState::After, _) => {
                self.maybe_emit();
            }

            (_prev, next) => {
                self.state = next;
            }
        }
    }
}

impl Drop for SearchMatchCollector {
    fn drop(&mut self) {
        self.maybe_emit();
    }
}

impl Sink for SearchMatchCollector {
    type Error = std::io::Error;

    fn matched(
        &mut self,
        _searcher: &Searcher,
        mat: &SinkMatch<'_>,
    ) -> Result<bool, std::io::Error> {
        self.transition(MatchState::Match);

        // TODO: handle errors
        let line = Line(
            mat.line_number().expect("grab line number"),
            String::from_utf8_lossy(mat.bytes()).to_string(),
        );

        self.cur_match_line = Some(line);

        Ok(true)
    }

    fn context(
        &mut self,
        _searcher: &Searcher,
        ctx: &SinkContext<'_>,
    ) -> Result<bool, std::io::Error> {
        // TODO: handle errors.
        // TODO: can we avoid the additional .to_string() by storing a COW instead?
        let line = Line(
            ctx.line_number().expect("grab line number"),
            String::from_utf8_lossy(ctx.bytes()).to_string(),
        );

        match ctx.kind() {
            &SinkContextKind::Before => {
                self.transition(MatchState::Before);
                self.cur_context_pre.push(line);
            }
            &SinkContextKind::After => {
                self.transition(MatchState::After);
                self.cur_context_post.push(line);
            }
            &SinkContextKind::Other => {}
        }

        Ok(true)
    }
}

fn handle_path(path: &Path, searcher: &mut Searcher, matcher: &RegexMatcher) {
    let mut sink = SearchMatchCollector::new();

    searcher
        .search_path(&matcher, path, &mut sink)
        .expect("search failed");
}

fn main() {
    let opts: Opts = Opts::from_args();
    println!("Parsed opts: {:?}", opts);

    let matcher = RegexMatcherBuilder::new()
        .case_insensitive(opts.insensitive)
        .build(&opts.find)
        .expect("bad pattern");

    let mut searcher = SearcherBuilder::new()
        .line_number(true)
        .before_context(3)
        .after_context(3)
        .build();

    for dir_entry in Walk::new("../") {
        let dir_entry = dir_entry.unwrap();
        if !dir_entry.path().is_file() {
            continue;
        }

        handle_path(dir_entry.path(), &mut searcher, &matcher);
    }
}
<commit_msg>Split processor from collector<commit_after>use std::path::PathBuf;

use grep::regex::RegexMatcherBuilder;
use grep::searcher::{Searcher, SearcherBuilder, Sink, SinkContext, SinkContextKind, SinkMatch};

use ignore::Walk;

use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(name = "fnr")]
/// Look for things, optionally replace them.
struct Opts {
    /// Run search in case insensitive mode
    #[structopt(short, long)]
    insensitive: bool,

    #[structopt(short, long)]
    prompt: bool,

    /// What to search for.
    #[structopt(name = "FIND")]
    find: String,

    /// What to replace it with. Required unless `prompt` is set.
    #[structopt(name = "REPLACE")]
    replace: Option<String>,

    /// Locations to search. Current directory if not specified.
    #[structopt(name = "PATH", parse(from_os_str))]
    paths: Vec<PathBuf>,
}

#[derive(Debug, Clone)]
struct Line(u64, String);

#[derive(Debug)]
struct SearchMatch {
    line: Line,
    context_pre: Vec<Line>,
    context_post: Vec<Line>,
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum MatchState {
    Init,
    Before,
    Match,
    After,
}

struct SearchMatchCollector<'a> {
    proc: &'a mut SearchMatchProcessor,
    state: MatchState,

    cur_context_pre: Vec<Line>,
    cur_context_post: Vec<Line>,
    cur_match_line: Option<Line>,
}

impl<'a> SearchMatchCollector<'a> {
    fn new(proc: &'a mut SearchMatchProcessor) -> SearchMatchCollector<'a> {
        return SearchMatchCollector {
            proc: proc,
            state: MatchState::Init,
            cur_match_line: None,
            cur_context_pre: Vec::new(),
            cur_context_post: Vec::new(),
        };
    }

    fn maybe_emit(&mut self) {
        if let Some(line) = &self.cur_match_line {
            // TODO: clones are wasteful here.
            let m = SearchMatch {
                line: line.clone(),
                context_pre: self.cur_context_pre.clone(),
                context_post: self.cur_context_post.clone(),
            };

            self.proc.handle(m);
            self.reset();
        }
    }

    fn reset(&mut self) {
        self.state = MatchState::Init;

        self.cur_match_line = None;
        self.cur_context_pre.clear();
        self.cur_context_post.clear();
    }

    #[inline]
    fn transition(&mut self, next: MatchState) {
        match (self.state, next) {
            // No transition, nothing to do
            (cur, next) if cur == next => {}

            // Beginning a new match or ending a previous one
            (_, MatchState::Before) | (MatchState::After, _) => {
                self.maybe_emit();
            }

            (_prev, next) => {
                self.state = next;
            }
        }
    }
}

impl<'a> Drop for SearchMatchCollector<'a> {
    fn drop(&mut self) {
        self.maybe_emit();
    }
}

impl<'a> Sink for SearchMatchCollector<'a> {
    type Error = std::io::Error;

    fn matched(
        &mut self,
        _searcher: &Searcher,
        mat: &SinkMatch<'_>,
    ) -> Result<bool, std::io::Error> {
        self.transition(MatchState::Match);

        // TODO: handle errors
        let line = Line(
            mat.line_number().expect("grab line number"),
            String::from_utf8_lossy(mat.bytes()).to_string(),
        );

        self.cur_match_line = Some(line);

        Ok(true)
    }

    fn context(
        &mut self,
        _searcher: &Searcher,
        ctx: &SinkContext<'_>,
    ) -> Result<bool, std::io::Error> {
        // TODO: handle errors.
        // TODO: can we avoid the additional .to_string() by storing a COW instead?
        let line = Line(
            ctx.line_number().expect("grab line number"),
            String::from_utf8_lossy(ctx.bytes()).to_string(),
        );

        match ctx.kind() {
            &SinkContextKind::Before => {
                self.transition(MatchState::Before);
                self.cur_context_pre.push(line);
            }
            &SinkContextKind::After => {
                self.transition(MatchState::After);
                self.cur_context_post.push(line);
            }
            &SinkContextKind::Other => {}
        }

        Ok(true)
    }
}

struct SearchMatchProcessor {}
impl SearchMatchProcessor {
    fn handle(&self, m: SearchMatch) {
        println!("---");

        for line in m.context_pre {
            print!("{}: {}", line.0, line.1);
        }
        print!("{}: {}", m.line.0, m.line.1);
        for line in m.context_post {
            print!("{}: {}", line.0, line.1);
        }
    }
}

fn main() {
    let opts: Opts = Opts::from_args();
    println!("Parsed opts: {:?}", opts);

    let matcher = RegexMatcherBuilder::new()
        .case_insensitive(opts.insensitive)
        .build(&opts.find)
        .expect("bad pattern");

    let mut searcher = SearcherBuilder::new()
        .line_number(true)
        .before_context(3)
        .after_context(3)
        .build();

    let mut processor = SearchMatchProcessor {};

    for dir_entry in Walk::new("../") {
        let dir_entry = dir_entry.unwrap();
        if !dir_entry.path().is_file() {
            continue;
        }

        let mut sink = SearchMatchCollector::new(&mut processor);

        searcher
            .search_path(&matcher, dir_entry.path(), &mut sink)
            .expect("search failed");
    }
}
<|endoftext|>


<commit_before>use super::{StringReader, UnmatchedBrace};

use rustc_ast::token::{self, Token, DelimToken};
use rustc_ast::tokenstream::{
    DelimSpan,
    IsJoint::{self, *},
    TokenStream, TokenTree, TreeAndJoint,
};
use rustc_ast_pretty::pprust::token_to_string;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::PResult;
use rustc_span::Span;

impl<'a> StringReader<'a> {
    crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
        let mut tt_reader = TokenTreesReader {
            string_reader: self,
            token: Token::dummy(),
            joint_to_prev: Joint,
            open_braces: Vec::new(),
            unmatched_braces: Vec::new(),
            matching_delim_spans: Vec::new(),
            last_unclosed_found_span: None,
            last_delim_empty_block_spans: FxHashMap::default(),
            matching_block_spans: Vec::new(),
        };
        let res = tt_reader.parse_all_token_trees();
        (res, tt_reader.unmatched_braces)
    }
}

struct TokenTreesReader<'a> {
    string_reader: StringReader<'a>,
    token: Token,
    joint_to_prev: IsJoint,
    /// Stack of open delimiters and their spans. Used for error message.
    open_braces: Vec<(token::DelimToken, Span)>,
    unmatched_braces: Vec<UnmatchedBrace>,
    /// The type and spans for all braces
    ///
    /// Used only for error recovery when arriving to EOF with mismatched braces.
    matching_delim_spans: Vec<(token::DelimToken, Span, Span)>,
    last_unclosed_found_span: Option<Span>,
    /// Collect empty block spans that might have been auto-inserted by editors.
    last_delim_empty_block_spans: FxHashMap<token::DelimToken, Span>,
    /// Collect the spans of braces (Open, Close). Used only
    /// for detecting if blocks are empty and only braces.
    matching_block_spans: Vec<(Span, Span)>,
}

impl<'a> TokenTreesReader<'a> {
    // Parse a stream of tokens into a list of `TokenTree`s, up to an `Eof`.
    fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> {
        let mut buf = TokenStreamBuilder::default();

        self.real_token();
        while self.token != token::Eof {
            buf.push(self.parse_token_tree()?);
        }

        Ok(buf.into_token_stream())
    }

    // Parse a stream of tokens into a list of `TokenTree`s, up to a `CloseDelim`.
    fn parse_token_trees_until_close_delim(&mut self) -> TokenStream {
        let mut buf = TokenStreamBuilder::default();
        loop {
            if let token::CloseDelim(..) = self.token.kind {
                return buf.into_token_stream();
            }

            match self.parse_token_tree() {
                Ok(tree) => buf.push(tree),
                Err(mut e) => {
                    e.emit();
                    return buf.into_token_stream();
                }
            }
        }
    }

    fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> {
        let sm = self.string_reader.sess.source_map();

        match self.token.kind {
            token::Eof => {
                let msg = "this file contains an unclosed delimiter";
                let mut err =
                    self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
                for &(_, sp) in &self.open_braces {
                    err.span_label(sp, "unclosed delimiter");
                    self.unmatched_braces.push(UnmatchedBrace {
                        expected_delim: token::DelimToken::Brace,
                        found_delim: None,
                        found_span: self.token.span,
                        unclosed_span: Some(sp),
                        candidate_span: None,
                    });
                }

                if let Some((delim, _)) = self.open_braces.last() {
                    if let Some((_, open_sp, close_sp)) =
                        self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| {
                            if let Some(close_padding) = sm.span_to_margin(*close_sp) {
                                if let Some(open_padding) = sm.span_to_margin(*open_sp) {
                                    return delim == d && close_padding != open_padding;
                                }
                            }
                            false
                        })
                    // these are in reverse order as they get inserted on close, but
                    {
                        // we want the last open/first close
                        err.span_label(*open_sp, "this delimiter might not be properly closed...");
                        err.span_label(
                            *close_sp,
                            "...as it matches this but it has different indentation",
                        );
                    }
                }
                Err(err)
            }
            token::OpenDelim(delim) => {
                // The span for beginning of the delimited section
                let pre_span = self.token.span;

                // Parse the open delimiter.
                self.open_braces.push((delim, self.token.span));
                self.real_token();

                // Parse the token trees within the delimiters.
                // We stop at any delimiter so we can try to recover if the user
                // uses an incorrect delimiter.
                let tts = self.parse_token_trees_until_close_delim();

                // Expand to cover the entire delimited token tree
                let delim_span = DelimSpan::from_pair(pre_span, self.token.span);

                match self.token.kind {
                    // Correct delimiter.
                    token::CloseDelim(d) if d == delim => {
                        let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
                        let close_brace_span = self.token.span;

                        if tts.is_empty() {
                            let empty_block_span = open_brace_span.to(close_brace_span);
                            if !sm.is_multiline(empty_block_span) {
                                // Only track if the block is in the form of `{}`, otherwise it is
                                // likely that it was written on purpose.
                                self.last_delim_empty_block_spans.insert(delim, empty_block_span);
                            }
                        }

                        match (open_brace, delim) {
                            //only add braces
                            (DelimToken::Brace, DelimToken::Brace) => {
                                self.matching_block_spans.push((open_brace_span, close_brace_span));
                            }
                            _ => {}
                        }

                        if self.open_braces.is_empty() {
                            // Clear up these spans to avoid suggesting them as we've found
                            // properly matched delimiters so far for an entire block.
                            self.matching_delim_spans.clear();
                        } else {
                            self.matching_delim_spans.push((
                                open_brace,
                                open_brace_span,
                                close_brace_span,
                            ));
                        }
                        // Parse the closing delimiter.
                        self.real_token();
                    }
                    // Incorrect delimiter.
                    token::CloseDelim(other) => {
                        let mut unclosed_delimiter = None;
                        let mut candidate = None;

                        if self.last_unclosed_found_span != Some(self.token.span) {
                            // do not complain about the same unclosed delimiter multiple times
                            self.last_unclosed_found_span = Some(self.token.span);
                            // This is a conservative error: only report the last unclosed
                            // delimiter. The previous unclosed delimiters could actually be
                            // closed! The parser just hasn't gotten to them yet.
                            if let Some(&(_, sp)) = self.open_braces.last() {
                                unclosed_delimiter = Some(sp);
                            };
                            if let Some(current_padding) = sm.span_to_margin(self.token.span) {
                                for (brace, brace_span) in &self.open_braces {
                                    if let Some(padding) = sm.span_to_margin(*brace_span) {
                                        // high likelihood of these two corresponding
                                        if current_padding == padding && brace == &other {
                                            candidate = Some(*brace_span);
                                        }
                                    }
                                }
                            }
                            let (tok, _) = self.open_braces.pop().unwrap();
                            self.unmatched_braces.push(UnmatchedBrace {
                                expected_delim: tok,
                                found_delim: Some(other),
                                found_span: self.token.span,
                                unclosed_span: unclosed_delimiter,
                                candidate_span: candidate,
                            });
                        } else {
                            self.open_braces.pop();
                        }

                        // If the incorrect delimiter matches an earlier opening
                        // delimiter, then don't consume it (it can be used to
                        // close the earlier one). Otherwise, consume it.
                        // E.g., we try to recover from:
                        // fn foo() {
                        //     bar(baz(
                        // }  // Incorrect delimiter but matches the earlier `{`
                        if !self.open_braces.iter().any(|&(b, _)| b == other) {
                            self.real_token();
                        }
                    }
                    token::Eof => {
                        // Silently recover, the EOF token will be seen again
                        // and an error emitted then. Thus we don't pop from
                        // self.open_braces here.
                    }
                    _ => {}
                }

                Ok(TokenTree::Delimited(delim_span, delim, tts).into())
            }
            token::CloseDelim(delim) => {
                // An unexpected closing delimiter (i.e., there is no
                // matching opening delimiter).
                let token_str = token_to_string(&self.token);
                let msg = format!("unexpected closing delimiter: `{}`", token_str);
                let mut err =
                    self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg);

                    // Braces are added at the end, so the last element is the biggest block
                    if let Some(parent) = self.matching_block_spans.last() {

                        if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) {
                            // Check if the (empty block) is in the last properly closed block
                            if (parent.0.to(parent.1)).contains(span) {
                                err.span_label(
                                    span,
                                    "block is empty, you might have not meant to close it",
                                );
                            }
                            else {
                                err.span_label(
                                    parent.0,
                                    "this opening brace...",
                                );

                                err.span_label(
                                    parent.1,
                                    "...matches this closing brace",
                                );
                            }
                        }
                        else {
                            err.span_label(
                                parent.0,
                                "this opening brace...",
                            );

                            err.span_label(
                                parent.1,
                                "...matches this closing brace",
                            );
                        }
                }

                err.span_label(self.token.span, "unexpected closing delimiter");
                Err(err)
            }
            _ => {
                let tt = TokenTree::Token(self.token.take());
                self.real_token();
                let is_joint = self.joint_to_prev == Joint && self.token.is_op();
                Ok((tt, if is_joint { Joint } else { NonJoint }))
            }
        }
    }

    fn real_token(&mut self) {
        self.joint_to_prev = Joint;
        loop {
            let token = self.string_reader.next_token();
            match token.kind {
                token::Whitespace | token::Comment | token::Shebang(_) | token::Unknown(_) => {
                    self.joint_to_prev = NonJoint;
                }
                _ => {
                    self.token = token;
                    return;
                }
            }
        }
    }
}

#[derive(Default)]
struct TokenStreamBuilder {
    buf: Vec<TreeAndJoint>,
}

impl TokenStreamBuilder {
    fn push(&mut self, (tree, joint): TreeAndJoint) {
        if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() {
            if let TokenTree::Token(token) = &tree {
                if let Some(glued) = prev_token.glue(token) {
                    self.buf.pop();
                    self.buf.push((TokenTree::Token(glued), joint));
                    return;
                }
            }
        }
        self.buf.push((tree, joint))
    }

    fn into_token_stream(self) -> TokenStream {
        TokenStream::new(self.buf)
    }
}
<commit_msg>Run fmt<commit_after>use super::{StringReader, UnmatchedBrace};

use rustc_ast::token::{self, DelimToken, Token};
use rustc_ast::tokenstream::{
    DelimSpan,
    IsJoint::{self, *},
    TokenStream, TokenTree, TreeAndJoint,
};
use rustc_ast_pretty::pprust::token_to_string;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::PResult;
use rustc_span::Span;

impl<'a> StringReader<'a> {
    crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
        let mut tt_reader = TokenTreesReader {
            string_reader: self,
            token: Token::dummy(),
            joint_to_prev: Joint,
            open_braces: Vec::new(),
            unmatched_braces: Vec::new(),
            matching_delim_spans: Vec::new(),
            last_unclosed_found_span: None,
            last_delim_empty_block_spans: FxHashMap::default(),
            matching_block_spans: Vec::new(),
        };
        let res = tt_reader.parse_all_token_trees();
        (res, tt_reader.unmatched_braces)
    }
}

struct TokenTreesReader<'a> {
    string_reader: StringReader<'a>,
    token: Token,
    joint_to_prev: IsJoint,
    /// Stack of open delimiters and their spans. Used for error message.
    open_braces: Vec<(token::DelimToken, Span)>,
    unmatched_braces: Vec<UnmatchedBrace>,
    /// The type and spans for all braces
    ///
    /// Used only for error recovery when arriving to EOF with mismatched braces.
    matching_delim_spans: Vec<(token::DelimToken, Span, Span)>,
    last_unclosed_found_span: Option<Span>,
    /// Collect empty block spans that might have been auto-inserted by editors.
    last_delim_empty_block_spans: FxHashMap<token::DelimToken, Span>,
    /// Collect the spans of braces (Open, Close). Used only
    /// for detecting if blocks are empty and only braces.
    matching_block_spans: Vec<(Span, Span)>,
}

impl<'a> TokenTreesReader<'a> {
    // Parse a stream of tokens into a list of `TokenTree`s, up to an `Eof`.
    fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> {
        let mut buf = TokenStreamBuilder::default();

        self.real_token();
        while self.token != token::Eof {
            buf.push(self.parse_token_tree()?);
        }

        Ok(buf.into_token_stream())
    }

    // Parse a stream of tokens into a list of `TokenTree`s, up to a `CloseDelim`.
    fn parse_token_trees_until_close_delim(&mut self) -> TokenStream {
        let mut buf = TokenStreamBuilder::default();
        loop {
            if let token::CloseDelim(..) = self.token.kind {
                return buf.into_token_stream();
            }

            match self.parse_token_tree() {
                Ok(tree) => buf.push(tree),
                Err(mut e) => {
                    e.emit();
                    return buf.into_token_stream();
                }
            }
        }
    }

    fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> {
        let sm = self.string_reader.sess.source_map();

        match self.token.kind {
            token::Eof => {
                let msg = "this file contains an unclosed delimiter";
                let mut err =
                    self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
                for &(_, sp) in &self.open_braces {
                    err.span_label(sp, "unclosed delimiter");
                    self.unmatched_braces.push(UnmatchedBrace {
                        expected_delim: token::DelimToken::Brace,
                        found_delim: None,
                        found_span: self.token.span,
                        unclosed_span: Some(sp),
                        candidate_span: None,
                    });
                }

                if let Some((delim, _)) = self.open_braces.last() {
                    if let Some((_, open_sp, close_sp)) =
                        self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| {
                            if let Some(close_padding) = sm.span_to_margin(*close_sp) {
                                if let Some(open_padding) = sm.span_to_margin(*open_sp) {
                                    return delim == d && close_padding != open_padding;
                                }
                            }
                            false
                        })
                    // these are in reverse order as they get inserted on close, but
                    {
                        // we want the last open/first close
                        err.span_label(*open_sp, "this delimiter might not be properly closed...");
                        err.span_label(
                            *close_sp,
                            "...as it matches this but it has different indentation",
                        );
                    }
                }
                Err(err)
            }
            token::OpenDelim(delim) => {
                // The span for beginning of the delimited section
                let pre_span = self.token.span;

                // Parse the open delimiter.
                self.open_braces.push((delim, self.token.span));
                self.real_token();

                // Parse the token trees within the delimiters.
                // We stop at any delimiter so we can try to recover if the user
                // uses an incorrect delimiter.
                let tts = self.parse_token_trees_until_close_delim();

                // Expand to cover the entire delimited token tree
                let delim_span = DelimSpan::from_pair(pre_span, self.token.span);

                match self.token.kind {
                    // Correct delimiter.
                    token::CloseDelim(d) if d == delim => {
                        let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
                        let close_brace_span = self.token.span;

                        if tts.is_empty() {
                            let empty_block_span = open_brace_span.to(close_brace_span);
                            if !sm.is_multiline(empty_block_span) {
                                // Only track if the block is in the form of `{}`, otherwise it is
                                // likely that it was written on purpose.
                                self.last_delim_empty_block_spans.insert(delim, empty_block_span);
                            }
                        }

                        match (open_brace, delim) {
                            //only add braces
                            (DelimToken::Brace, DelimToken::Brace) => {
                                self.matching_block_spans.push((open_brace_span, close_brace_span));
                            }
                            _ => {}
                        }

                        if self.open_braces.is_empty() {
                            // Clear up these spans to avoid suggesting them as we've found
                            // properly matched delimiters so far for an entire block.
                            self.matching_delim_spans.clear();
                        } else {
                            self.matching_delim_spans.push((
                                open_brace,
                                open_brace_span,
                                close_brace_span,
                            ));
                        }
                        // Parse the closing delimiter.
                        self.real_token();
                    }
                    // Incorrect delimiter.
                    token::CloseDelim(other) => {
                        let mut unclosed_delimiter = None;
                        let mut candidate = None;

                        if self.last_unclosed_found_span != Some(self.token.span) {
                            // do not complain about the same unclosed delimiter multiple times
                            self.last_unclosed_found_span = Some(self.token.span);
                            // This is a conservative error: only report the last unclosed
                            // delimiter. The previous unclosed delimiters could actually be
                            // closed! The parser just hasn't gotten to them yet.
                            if let Some(&(_, sp)) = self.open_braces.last() {
                                unclosed_delimiter = Some(sp);
                            };
                            if let Some(current_padding) = sm.span_to_margin(self.token.span) {
                                for (brace, brace_span) in &self.open_braces {
                                    if let Some(padding) = sm.span_to_margin(*brace_span) {
                                        // high likelihood of these two corresponding
                                        if current_padding == padding && brace == &other {
                                            candidate = Some(*brace_span);
                                        }
                                    }
                                }
                            }
                            let (tok, _) = self.open_braces.pop().unwrap();
                            self.unmatched_braces.push(UnmatchedBrace {
                                expected_delim: tok,
                                found_delim: Some(other),
                                found_span: self.token.span,
                                unclosed_span: unclosed_delimiter,
                                candidate_span: candidate,
                            });
                        } else {
                            self.open_braces.pop();
                        }

                        // If the incorrect delimiter matches an earlier opening
                        // delimiter, then don't consume it (it can be used to
                        // close the earlier one). Otherwise, consume it.
                        // E.g., we try to recover from:
                        // fn foo() {
                        //     bar(baz(
                        // }  // Incorrect delimiter but matches the earlier `{`
                        if !self.open_braces.iter().any(|&(b, _)| b == other) {
                            self.real_token();
                        }
                    }
                    token::Eof => {
                        // Silently recover, the EOF token will be seen again
                        // and an error emitted then. Thus we don't pop from
                        // self.open_braces here.
                    }
                    _ => {}
                }

                Ok(TokenTree::Delimited(delim_span, delim, tts).into())
            }
            token::CloseDelim(delim) => {
                // An unexpected closing delimiter (i.e., there is no
                // matching opening delimiter).
                let token_str = token_to_string(&self.token);
                let msg = format!("unexpected closing delimiter: `{}`", token_str);
                let mut err =
                    self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg);

                // Braces are added at the end, so the last element is the biggest block
                if let Some(parent) = self.matching_block_spans.last() {
                    if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) {
                        // Check if the (empty block) is in the last properly closed block
                        if (parent.0.to(parent.1)).contains(span) {
                            err.span_label(
                                span,
                                "block is empty, you might have not meant to close it",
                            );
                        } else {
                            err.span_label(parent.0, "this opening brace...");

                            err.span_label(parent.1, "...matches this closing brace");
                        }
                    } else {
                        err.span_label(parent.0, "this opening brace...");

                        err.span_label(parent.1, "...matches this closing brace");
                    }
                }

                err.span_label(self.token.span, "unexpected closing delimiter");
                Err(err)
            }
            _ => {
                let tt = TokenTree::Token(self.token.take());
                self.real_token();
                let is_joint = self.joint_to_prev == Joint && self.token.is_op();
                Ok((tt, if is_joint { Joint } else { NonJoint }))
            }
        }
    }

    fn real_token(&mut self) {
        self.joint_to_prev = Joint;
        loop {
            let token = self.string_reader.next_token();
            match token.kind {
                token::Whitespace | token::Comment | token::Shebang(_) | token::Unknown(_) => {
                    self.joint_to_prev = NonJoint;
                }
                _ => {
                    self.token = token;
                    return;
                }
            }
        }
    }
}

#[derive(Default)]
struct TokenStreamBuilder {
    buf: Vec<TreeAndJoint>,
}

impl TokenStreamBuilder {
    fn push(&mut self, (tree, joint): TreeAndJoint) {
        if let Some((TokenTree::Token(prev_token), Joint)) = self.buf.last() {
            if let TokenTree::Token(token) = &tree {
                if let Some(glued) = prev_token.glue(token) {
                    self.buf.pop();
                    self.buf.push((TokenTree::Token(glued), joint));
                    return;
                }
            }
        }
        self.buf.push((tree, joint))
    }

    fn into_token_stream(self) -> TokenStream {
        TokenStream::new(self.buf)
    }
}
<|endoftext|>