<commit_before>//! Translation skeletton that traverses the whole WebAssembly module and call helper functions
//! to deal with each part of it.
use wasmparser::{ParserState, SectionCode, ParserInput, Parser, WasmDecoder, BinaryReaderError};
use sections_translator::{SectionParsingError, parse_function_signatures, parse_import_section,
                          parse_function_section, parse_export_section, parse_memory_section,
                          parse_global_section, parse_table_section, parse_elements_section,
                          parse_data_section};
use translation_utils::{type_to_type, Import, SignatureIndex, FunctionIndex};
use cretonne::ir::{Function, Type};
use code_translator::translate_function_body;
use cton_frontend::ILBuilder;
use std::collections::HashMap;
use runtime::WasmRuntime;

/// Output of the [`translate_module`](fn.translate_module.html) function.
pub struct TranslationResult {
    /// The translated functions.
    pub functions: Vec<Function>,
    /// When present, the index of the function defined as `start` of the module.
    ///
    /// Note that this is a WebAssembly function index and not an index into the `functions` vector
    /// above. The imported functions are numbered before the local functions.
    pub start_index: Option<FunctionIndex>,
}

/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cretonne IL
/// [`Function`](../cretonne/ir/function/struct.Function.html).
/// Returns the functions and also the mappings for imported functions and signature between the
/// indexes in the wasm module and the indexes inside each functions.
pub fn translate_module(
    data: &[u8],
    runtime: &mut WasmRuntime,
) -> Result<TranslationResult, String> {
    let mut parser = Parser::new(data);
    match *parser.read() {
        ParserState::BeginWasm { .. } => {}
        ParserState::Error(BinaryReaderError { message, offset }) => {
            return Err(format!("at offset {}: {}", offset, message));
        }
        ref s => panic!("modules should begin properly: {:?}", s),
    }
    let mut signatures = None;
    let mut functions: Option<Vec<SignatureIndex>> = None;
    let mut globals = Vec::new();
    let mut exports: Option<HashMap<FunctionIndex, String>> = None;
    let mut next_input = ParserInput::Default;
    let mut function_index: FunctionIndex = 0;
    let mut start_index: Option<FunctionIndex> = None;
    loop {
        match *parser.read_with_input(next_input) {
            ParserState::BeginSection { code: SectionCode::Type, .. } => {
                match parse_function_signatures(&mut parser, runtime) {
                    Ok(sigs) => signatures = Some(sigs),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the type section: {}", s))
                    }
                };
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Import, .. } => {
                match parse_import_section(&mut parser, runtime) {
                    Ok(imps) => {
                        for import in imps {
                            match import {
                                Import::Function { sig_index } => {
                                    functions = match functions {
                                        None => Some(vec![sig_index as SignatureIndex]),
                                        Some(mut funcs) => {
                                            funcs.push(sig_index as SignatureIndex);
                                            Some(funcs)
                                        }
                                    };
                                    function_index += 1;
                                }
                                Import::Memory(mem) => {
                                    runtime.declare_memory(mem);
                                }
                                Import::Global(glob) => {
                                    runtime.declare_global(glob);
                                    globals.push(glob);
                                }
                                Import::Table(tab) => {
                                    runtime.declare_table(tab);
                                }
                            }
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the import section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Function, .. } => {
                match parse_function_section(&mut parser, runtime) {
                    Ok(funcs) => {
                        match functions {
                            None => functions = Some(funcs),
                            Some(ref mut imps) => imps.extend(funcs),
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the function section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Table, .. } => {
                match parse_table_section(&mut parser, runtime) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the table section: {}", s))
                    }
                }
            }
            ParserState::BeginSection { code: SectionCode::Memory, .. } => {
                match parse_memory_section(&mut parser) {
                    Ok(mems) => {
                        for mem in mems {
                            runtime.declare_memory(mem);
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the memory section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Global, .. } => {
                match parse_global_section(&mut parser, runtime) {
                    Ok(mut globs) => globals.append(&mut globs),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the global section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Export, .. } => {
                match parse_export_section(&mut parser) {
                    Ok(exps) => exports = Some(exps),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the export section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Start, .. } => {
                match *parser.read() {
                    ParserState::StartSectionEntry(index) => {
                        start_index = Some(index as FunctionIndex)
                    }
                    _ => return Err(String::from("wrong content in the start section")),
                }
                match *parser.read() {
                    ParserState::EndSection => {}
                    _ => return Err(String::from("wrong content in the start section")),
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Element, .. } => {
                match parse_elements_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the element section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Code, .. } => {
                // The code section begins
                break;
            }
            ParserState::EndSection => {
                next_input = ParserInput::Default;
            }
            ParserState::EndWasm => {
                return Ok(TranslationResult {
                    functions: Vec::new(),
                    start_index: None,
                })
            }
            ParserState::BeginSection { code: SectionCode::Data, .. } => {
                match parse_data_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the data section: {}", s))
                    }
                }
            }
            _ => return Err(String::from("wrong content in the preamble")),
        };
    }
    // At this point we've entered the code section
    // First we check that we have all that is necessary to translate a function.
    let signatures = match signatures {
        None => Vec::new(),
        Some(sigs) => sigs,
    };
    let functions = match functions {
        None => return Err(String::from("missing a function section")),
        Some(functions) => functions,
    };
    let mut il_functions: Vec<Function> = Vec::new();
    let mut il_builder = ILBuilder::new();
    runtime.begin_translation();
    loop {
        let locals: Vec<(usize, Type)> = match *parser.read() {
            ParserState::BeginFunctionBody { ref locals, .. } => {
                locals
                    .iter()
                    .map(|&(index, ref ty)| {
                        (
                            index as usize,
                            match type_to_type(ty) {
                                Ok(ty) => ty,
                                Err(()) => panic!("unsupported type for local variable"),
                            },
                        )
                    })
                    .collect()
            }
            ParserState::EndSection => break,
            _ => return Err(String::from("wrong content in code section")),
        };
        let signature = signatures[functions[function_index]].clone();
        match translate_function_body(
            &mut parser,
            function_index,
            &signature,
            &locals,
            &exports,
            &mut il_builder,
            runtime,
        ) {
            Ok(il_func) => il_functions.push(il_func),
            Err(s) => return Err(s),
        }
        function_index += 1;
    }
    loop {
        match *parser.read() {
            ParserState::BeginSection { code: SectionCode::Data, .. } => {
                match parse_data_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the data section: {}", s))
                    }
                }
            }
            ParserState::EndWasm => {
                return Ok(TranslationResult {
                    functions: il_functions,
                    start_index,
                })
            }
            _ => (),
        }
    }
}
<commit_msg>Replace a match with a .unwrap_or_default().<commit_after>//! Translation skeletton that traverses the whole WebAssembly module and call helper functions
//! to deal with each part of it.
use wasmparser::{ParserState, SectionCode, ParserInput, Parser, WasmDecoder, BinaryReaderError};
use sections_translator::{SectionParsingError, parse_function_signatures, parse_import_section,
                          parse_function_section, parse_export_section, parse_memory_section,
                          parse_global_section, parse_table_section, parse_elements_section,
                          parse_data_section};
use translation_utils::{type_to_type, Import, SignatureIndex, FunctionIndex};
use cretonne::ir::{Function, Type};
use code_translator::translate_function_body;
use cton_frontend::ILBuilder;
use std::collections::HashMap;
use runtime::WasmRuntime;

/// Output of the [`translate_module`](fn.translate_module.html) function.
pub struct TranslationResult {
    /// The translated functions.
    pub functions: Vec<Function>,
    /// When present, the index of the function defined as `start` of the module.
    ///
    /// Note that this is a WebAssembly function index and not an index into the `functions` vector
    /// above. The imported functions are numbered before the local functions.
    pub start_index: Option<FunctionIndex>,
}

/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cretonne IL
/// [`Function`](../cretonne/ir/function/struct.Function.html).
/// Returns the functions and also the mappings for imported functions and signature between the
/// indexes in the wasm module and the indexes inside each functions.
pub fn translate_module(
    data: &[u8],
    runtime: &mut WasmRuntime,
) -> Result<TranslationResult, String> {
    let mut parser = Parser::new(data);
    match *parser.read() {
        ParserState::BeginWasm { .. } => {}
        ParserState::Error(BinaryReaderError { message, offset }) => {
            return Err(format!("at offset {}: {}", offset, message));
        }
        ref s => panic!("modules should begin properly: {:?}", s),
    }
    let mut signatures = None;
    let mut functions: Option<Vec<SignatureIndex>> = None;
    let mut globals = Vec::new();
    let mut exports: Option<HashMap<FunctionIndex, String>> = None;
    let mut next_input = ParserInput::Default;
    let mut function_index: FunctionIndex = 0;
    let mut start_index: Option<FunctionIndex> = None;
    loop {
        match *parser.read_with_input(next_input) {
            ParserState::BeginSection { code: SectionCode::Type, .. } => {
                match parse_function_signatures(&mut parser, runtime) {
                    Ok(sigs) => signatures = Some(sigs),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the type section: {}", s))
                    }
                };
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Import, .. } => {
                match parse_import_section(&mut parser, runtime) {
                    Ok(imps) => {
                        for import in imps {
                            match import {
                                Import::Function { sig_index } => {
                                    functions = match functions {
                                        None => Some(vec![sig_index as SignatureIndex]),
                                        Some(mut funcs) => {
                                            funcs.push(sig_index as SignatureIndex);
                                            Some(funcs)
                                        }
                                    };
                                    function_index += 1;
                                }
                                Import::Memory(mem) => {
                                    runtime.declare_memory(mem);
                                }
                                Import::Global(glob) => {
                                    runtime.declare_global(glob);
                                    globals.push(glob);
                                }
                                Import::Table(tab) => {
                                    runtime.declare_table(tab);
                                }
                            }
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the import section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Function, .. } => {
                match parse_function_section(&mut parser, runtime) {
                    Ok(funcs) => {
                        match functions {
                            None => functions = Some(funcs),
                            Some(ref mut imps) => imps.extend(funcs),
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the function section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Table, .. } => {
                match parse_table_section(&mut parser, runtime) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the table section: {}", s))
                    }
                }
            }
            ParserState::BeginSection { code: SectionCode::Memory, .. } => {
                match parse_memory_section(&mut parser) {
                    Ok(mems) => {
                        for mem in mems {
                            runtime.declare_memory(mem);
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the memory section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Global, .. } => {
                match parse_global_section(&mut parser, runtime) {
                    Ok(mut globs) => globals.append(&mut globs),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the global section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Export, .. } => {
                match parse_export_section(&mut parser) {
                    Ok(exps) => exports = Some(exps),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the export section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Start, .. } => {
                match *parser.read() {
                    ParserState::StartSectionEntry(index) => {
                        start_index = Some(index as FunctionIndex)
                    }
                    _ => return Err(String::from("wrong content in the start section")),
                }
                match *parser.read() {
                    ParserState::EndSection => {}
                    _ => return Err(String::from("wrong content in the start section")),
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Element, .. } => {
                match parse_elements_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the element section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Code, .. } => {
                // The code section begins
                break;
            }
            ParserState::EndSection => {
                next_input = ParserInput::Default;
            }
            ParserState::EndWasm => {
                return Ok(TranslationResult {
                    functions: Vec::new(),
                    start_index: None,
                })
            }
            ParserState::BeginSection { code: SectionCode::Data, .. } => {
                match parse_data_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the data section: {}", s))
                    }
                }
            }
            _ => return Err(String::from("wrong content in the preamble")),
        };
    }
    // At this point we've entered the code section
    // First we check that we have all that is necessary to translate a function.
    let signatures = signatures.unwrap_or_default();
    let functions = match functions {
        None => return Err(String::from("missing a function section")),
        Some(functions) => functions,
    };
    let mut il_functions: Vec<Function> = Vec::new();
    let mut il_builder = ILBuilder::new();
    runtime.begin_translation();
    loop {
        let locals: Vec<(usize, Type)> = match *parser.read() {
            ParserState::BeginFunctionBody { ref locals, .. } => {
                locals
                    .iter()
                    .map(|&(index, ref ty)| {
                        (
                            index as usize,
                            match type_to_type(ty) {
                                Ok(ty) => ty,
                                Err(()) => panic!("unsupported type for local variable"),
                            },
                        )
                    })
                    .collect()
            }
            ParserState::EndSection => break,
            _ => return Err(String::from("wrong content in code section")),
        };
        let signature = signatures[functions[function_index]].clone();
        match translate_function_body(
            &mut parser,
            function_index,
            &signature,
            &locals,
            &exports,
            &mut il_builder,
            runtime,
        ) {
            Ok(il_func) => il_functions.push(il_func),
            Err(s) => return Err(s),
        }
        function_index += 1;
    }
    loop {
        match *parser.read() {
            ParserState::BeginSection { code: SectionCode::Data, .. } => {
                match parse_data_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the data section: {}", s))
                    }
                }
            }
            ParserState::EndWasm => {
                return Ok(TranslationResult {
                    functions: il_functions,
                    start_index,
                })
            }
            _ => (),
        }
    }
}
<|endoftext|>


<commit_before>use rustc::hir::*;
use rustc::lint::*;
use utils::{get_trait_def_id, implements_trait, higher, match_path, paths, span_lint};

/// **What it does:** Checks for iteration that is guaranteed to be infinite.
///
/// **Why is this bad?** While there may be places where this is acceptable
/// (e.g. in event streams), in most cases this is simply an error.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// repeat(1_u8).iter().collect::<Vec<_>>()
/// ```
declare_lint! {
    pub INFINITE_ITER,
    Warn,
    "infinite iteration"
}

/// **What it does:** Checks for iteration that may be infinite.
///
/// **Why is this bad?** While there may be places where this is acceptable
/// (e.g. in event streams), in most cases this is simply an error.
///
/// **Known problems:** The code may have a condition to stop iteration, but
/// this lint is not clever enough to analyze it.
///
/// **Example:**
/// ```rust
/// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
/// ```
declare_lint! {
    pub MAYBE_INFINITE_ITER,
    Allow,
    "possible infinite iteration"
}

#[derive(Copy, Clone)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(INFINITE_ITER, MAYBE_INFINITE_ITER)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        let (lint, msg) = match complete_infinite_iter(cx, expr) {
            True => (INFINITE_ITER, "infinite iteration detected"),
            Unknown => (MAYBE_INFINITE_ITER,
                        "possible infinite iteration detected"),
            False => { return; }
        };
        span_lint(cx, lint, expr.span, msg)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum TriState {
    True,
    Unknown,
    False
}

use self::TriState::{True, Unknown, False};

impl TriState {
    fn and(self, b: Self) -> Self {
        match (self, b) {
            (False, _) | (_, False) => False,
            (Unknown, _) | (_, Unknown) => Unknown,
            _ => True
        }
    }

    fn or(self, b: Self) -> Self {
        match (self, b) {
            (True, _) | (_, True) => True,
            (Unknown, _) | (_, Unknown) => Unknown,
            _ => False
        }
    }
}

impl From<bool> for TriState {
    fn from(b: bool) -> Self {
        if b { True } else { False }
    }
}

#[derive(Copy, Clone)]
enum Heuristic {
    Always,
    First,
    Any,
    All
}

use self::Heuristic::{Always, First, Any, All};

// here we use the `TriState` as (Finite, Possible Infinite, Infinite)
static HEURISTICS : &[(&str, usize, Heuristic, TriState)] = &[
    ("zip", 2, All, True),
    ("chain", 2, Any, True),
    ("cycle", 1, Always, True),
    ("map", 2, First, True),
    ("by_ref", 1, First, True),
    ("cloned", 1, First, True),
    ("rev", 1, First, True),
    ("inspect", 1, First, True),
    ("enumerate", 1, First, True),
    ("peekable", 2, First, True),
    ("fuse", 1, First, True),
    ("skip", 2, First, True),
    ("skip_while", 1, First, True),
    ("filter", 2, First, True),
    ("filter_map", 2, First, True),
    ("flat_map", 2, First, True),
    ("unzip", 1, First, True),
    ("take_while", 2, First, Unknown),
    ("scan", 3, First, Unknown)
];

fn is_infinite(cx: &LateContext, expr: &Expr) -> TriState {
    match expr.node {
        ExprMethodCall(ref method, _, ref args) => {
            for &(name, len, heuristic, cap) in HEURISTICS.iter() {
                if method.name == name && args.len() == len {
                    return (match heuristic {
                        Always => True,
                        First => is_infinite(cx, &args[0]),
                        Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
                        All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
                    }).and(cap);
                }
            }
            if method.name == "flat_map" && args.len() == 2 {
                if let ExprClosure(_, _, body_id, _) = args[1].node {
                    let body = cx.tcx.hir.body(body_id);
                    return is_infinite(cx, &body.value);
                }
            }
            False
        },
        ExprBlock(ref block) =>
            block.expr.as_ref().map_or(False, |e| is_infinite(cx, e)),
        ExprBox(ref e) | ExprAddrOf(_, ref e) => is_infinite(cx, e),
        ExprCall(ref path, _) => {
            if let ExprPath(ref qpath) = path.node {
                match_path(qpath, &paths::REPEAT).into()
            } else { False }
        },
        ExprStruct(..) => {
            higher::range(expr).map_or(false, |r| r.end.is_none()).into()
        },
        _ => False
    }
}

static POSSIBLY_COMPLETING_METHODS : &[(&str, usize)] = &[
    ("find", 2),
    ("rfind", 2),
    ("position", 2),
    ("rposition", 2),
    ("any", 2),
    ("all", 2)
];

static COMPLETING_METHODS : &[(&str, usize)] = &[
    ("count", 1),
    ("collect", 1),
    ("fold", 3),
    ("for_each", 2),
    ("partition", 2),
    ("max", 1),
    ("max_by", 2),
    ("max_by_key", 2),
    ("min", 1),
    ("min_by", 2),
    ("min_by_key", 2),
    ("sum", 1),
    ("product", 1)
];

fn complete_infinite_iter(cx: &LateContext, expr: &Expr) -> TriState {
    match expr.node {
        ExprMethodCall(ref method, _, ref args) => {
            for &(name, len) in COMPLETING_METHODS.iter() {
                if method.name == name && args.len() == len {
                    return is_infinite(cx, &args[0]);
                }
            }
            for &(name, len) in POSSIBLY_COMPLETING_METHODS.iter() {
                if method.name == name && args.len() == len {
                    return Unknown.and(is_infinite(cx, &args[0]));
                }
            }
            if method.name == "last" && args.len() == 1 &&
                    get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR).map_or(false,
                        |id| !implements_trait(cx,
                                               cx.tables.expr_ty(&args[0]),
                                               id,
                                               &[])) {
                return is_infinite(cx, &args[0]);
            }
        },
        ExprBinary(op, ref l, ref r) => {
            if op.node.is_comparison() {
                return is_infinite(cx, l).and(is_infinite(cx, r)).and(Unknown)
            }
        }, //TODO: ExprLoop + Match
        _ => ()
    }
    False
}
<commit_msg>fix match_path -> match_qpath rename<commit_after>use rustc::hir::*;
use rustc::lint::*;
use utils::{get_trait_def_id, implements_trait, higher, match_qpath, paths, span_lint};

/// **What it does:** Checks for iteration that is guaranteed to be infinite.
///
/// **Why is this bad?** While there may be places where this is acceptable
/// (e.g. in event streams), in most cases this is simply an error.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// repeat(1_u8).iter().collect::<Vec<_>>()
/// ```
declare_lint! {
    pub INFINITE_ITER,
    Warn,
    "infinite iteration"
}

/// **What it does:** Checks for iteration that may be infinite.
///
/// **Why is this bad?** While there may be places where this is acceptable
/// (e.g. in event streams), in most cases this is simply an error.
///
/// **Known problems:** The code may have a condition to stop iteration, but
/// this lint is not clever enough to analyze it.
///
/// **Example:**
/// ```rust
/// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
/// ```
declare_lint! {
    pub MAYBE_INFINITE_ITER,
    Allow,
    "possible infinite iteration"
}

#[derive(Copy, Clone)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(INFINITE_ITER, MAYBE_INFINITE_ITER)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        let (lint, msg) = match complete_infinite_iter(cx, expr) {
            True => (INFINITE_ITER, "infinite iteration detected"),
            Unknown => (MAYBE_INFINITE_ITER,
                        "possible infinite iteration detected"),
            False => { return; }
        };
        span_lint(cx, lint, expr.span, msg)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum TriState {
    True,
    Unknown,
    False
}

use self::TriState::{True, Unknown, False};

impl TriState {
    fn and(self, b: Self) -> Self {
        match (self, b) {
            (False, _) | (_, False) => False,
            (Unknown, _) | (_, Unknown) => Unknown,
            _ => True
        }
    }

    fn or(self, b: Self) -> Self {
        match (self, b) {
            (True, _) | (_, True) => True,
            (Unknown, _) | (_, Unknown) => Unknown,
            _ => False
        }
    }
}

impl From<bool> for TriState {
    fn from(b: bool) -> Self {
        if b { True } else { False }
    }
}

#[derive(Copy, Clone)]
enum Heuristic {
    Always,
    First,
    Any,
    All
}

use self::Heuristic::{Always, First, Any, All};

// here we use the `TriState` as (Finite, Possible Infinite, Infinite)
static HEURISTICS : &[(&str, usize, Heuristic, TriState)] = &[
    ("zip", 2, All, True),
    ("chain", 2, Any, True),
    ("cycle", 1, Always, True),
    ("map", 2, First, True),
    ("by_ref", 1, First, True),
    ("cloned", 1, First, True),
    ("rev", 1, First, True),
    ("inspect", 1, First, True),
    ("enumerate", 1, First, True),
    ("peekable", 2, First, True),
    ("fuse", 1, First, True),
    ("skip", 2, First, True),
    ("skip_while", 1, First, True),
    ("filter", 2, First, True),
    ("filter_map", 2, First, True),
    ("flat_map", 2, First, True),
    ("unzip", 1, First, True),
    ("take_while", 2, First, Unknown),
    ("scan", 3, First, Unknown)
];

fn is_infinite(cx: &LateContext, expr: &Expr) -> TriState {
    match expr.node {
        ExprMethodCall(ref method, _, ref args) => {
            for &(name, len, heuristic, cap) in HEURISTICS.iter() {
                if method.name == name && args.len() == len {
                    return (match heuristic {
                        Always => True,
                        First => is_infinite(cx, &args[0]),
                        Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
                        All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
                    }).and(cap);
                }
            }
            if method.name == "flat_map" && args.len() == 2 {
                if let ExprClosure(_, _, body_id, _) = args[1].node {
                    let body = cx.tcx.hir.body(body_id);
                    return is_infinite(cx, &body.value);
                }
            }
            False
        },
        ExprBlock(ref block) =>
            block.expr.as_ref().map_or(False, |e| is_infinite(cx, e)),
        ExprBox(ref e) | ExprAddrOf(_, ref e) => is_infinite(cx, e),
        ExprCall(ref path, _) => {
            if let ExprPath(ref qpath) = path.node {
                match_qpath(qpath, &paths::REPEAT).into()
            } else { False }
        },
        ExprStruct(..) => {
            higher::range(expr).map_or(false, |r| r.end.is_none()).into()
        },
        _ => False
    }
}

static POSSIBLY_COMPLETING_METHODS : &[(&str, usize)] = &[
    ("find", 2),
    ("rfind", 2),
    ("position", 2),
    ("rposition", 2),
    ("any", 2),
    ("all", 2)
];

static COMPLETING_METHODS : &[(&str, usize)] = &[
    ("count", 1),
    ("collect", 1),
    ("fold", 3),
    ("for_each", 2),
    ("partition", 2),
    ("max", 1),
    ("max_by", 2),
    ("max_by_key", 2),
    ("min", 1),
    ("min_by", 2),
    ("min_by_key", 2),
    ("sum", 1),
    ("product", 1)
];

fn complete_infinite_iter(cx: &LateContext, expr: &Expr) -> TriState {
    match expr.node {
        ExprMethodCall(ref method, _, ref args) => {
            for &(name, len) in COMPLETING_METHODS.iter() {
                if method.name == name && args.len() == len {
                    return is_infinite(cx, &args[0]);
                }
            }
            for &(name, len) in POSSIBLY_COMPLETING_METHODS.iter() {
                if method.name == name && args.len() == len {
                    return Unknown.and(is_infinite(cx, &args[0]));
                }
            }
            if method.name == "last" && args.len() == 1 &&
                    get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR).map_or(false,
                        |id| !implements_trait(cx,
                                               cx.tables.expr_ty(&args[0]),
                                               id,
                                               &[])) {
                return is_infinite(cx, &args[0]);
            }
        },
        ExprBinary(op, ref l, ref r) => {
            if op.node.is_comparison() {
                return is_infinite(cx, l).and(is_infinite(cx, r)).and(Unknown)
            }
        }, //TODO: ExprLoop + Match
        _ => ()
    }
    False
}
<|endoftext|>


<commit_before>mod crosspointer_transmute;
mod transmute_float_to_int;
mod transmute_int_to_bool;
mod transmute_int_to_char;
mod transmute_int_to_float;
mod transmute_ptr_to_ptr;
mod transmute_ptr_to_ref;
mod transmute_ref_to_ref;
mod transmutes_expressible_as_ptr_casts;
mod unsound_collection_transmute;
mod useless_transmute;
mod utils;
mod wrong_transmute;

use crate::utils::{in_constant, match_def_path, paths};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes that can't ever be correct on any
    /// architecture.
    ///
    /// **Why is this bad?** It's basically guaranteed to be undefined behaviour.
    ///
    /// **Known problems:** When accessing C, users might want to store pointer
    /// sized objects in `extradata` arguments to save an allocation.
    ///
    /// **Example:**
    /// ```ignore
    /// let ptr: *const T = core::intrinsics::transmute('x')
    /// ```
    pub WRONG_TRANSMUTE,
    correctness,
    "transmutes that are confusing at best, undefined behaviour at worst and always useless"
}

// FIXME: Move this to `complexity` again, after #5343 is fixed
declare_clippy_lint! {
    /// **What it does:** Checks for transmutes to the original type of the object
    /// and transmutes that could be a cast.
    ///
    /// **Why is this bad?** Readability. The code tricks people into thinking that
    /// something complex is going on.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
    /// ```
    pub USELESS_TRANSMUTE,
    nursery,
    "transmutes that have the same to and from types or could be a cast/coercion"
}

// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
declare_clippy_lint! {
    /// **What it does:**Checks for transmutes that could be a pointer cast.
    ///
    /// **Why is this bad?** Readability. The code tricks people into thinking that
    /// something complex is going on.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// # let p: *const [i32] = &[];
    /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
    /// ```
    /// Use instead:
    /// ```rust
    /// # let p: *const [i32] = &[];
    /// p as *const [u16];
    /// ```
    pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
    complexity,
    "transmutes that could be a pointer cast"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes between a type `T` and `*T`.
    ///
    /// **Why is this bad?** It's easy to mistakenly transmute between a type and a
    /// pointer to that type.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// core::intrinsics::transmute(t) // where the result type is the same as
    ///                                // `*t` or `&t`'s
    /// ```
    pub CROSSPOINTER_TRANSMUTE,
    complexity,
    "transmutes that have to or from types that are a pointer to the other"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from a pointer to a reference.
    ///
    /// **Why is this bad?** This can always be rewritten with `&` and `*`.
    ///
    /// **Known problems:**
    /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0,
    /// while dereferencing raw pointer is not stable yet.
    /// If you need to do this in those places,
    /// you would have to use `transmute` instead.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// unsafe {
    ///     let _: &T = std::mem::transmute(p); // where p: *const T
    /// }
    ///
    /// // can be written:
    /// let _: &T = &*p;
    /// ```
    pub TRANSMUTE_PTR_TO_REF,
    complexity,
    "transmutes from a pointer to a reference type"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from an integer to a `char`.
    ///
    /// **Why is this bad?** Not every integer is a Unicode scalar value.
    ///
    /// **Known problems:**
    /// - [`from_u32`] which this lint suggests using is slower than `transmute`
    /// as it needs to validate the input.
    /// If you are certain that the input is always a valid Unicode scalar value,
    /// use [`from_u32_unchecked`] which is as fast as `transmute`
    /// but has a semantically meaningful name.
    /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
    ///
    /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
    /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
    ///
    /// **Example:**
    /// ```rust
    /// let x = 1_u32;
    /// unsafe {
    ///     let _: char = std::mem::transmute(x); // where x: u32
    /// }
    ///
    /// // should be:
    /// let _ = std::char::from_u32(x).unwrap();
    /// ```
    pub TRANSMUTE_INT_TO_CHAR,
    complexity,
    "transmutes from an integer to a `char`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.
    ///
    /// **Why is this bad?** Not every byte slice is a valid UTF-8 string.
    ///
    /// **Known problems:**
    /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
    /// as it needs to validate the input.
    /// If you are certain that the input is always a valid UTF-8,
    /// use [`from_utf8_unchecked`] which is as fast as `transmute`
    /// but has a semantically meaningful name.
    /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
    ///
    /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
    /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
    ///
    /// **Example:**
    /// ```rust
    /// let b: &[u8] = &[1_u8, 2_u8];
    /// unsafe {
    ///     let _: &str = std::mem::transmute(b); // where b: &[u8]
    /// }
    ///
    /// // should be:
    /// let _ = std::str::from_utf8(b).unwrap();
    /// ```
    pub TRANSMUTE_BYTES_TO_STR,
    complexity,
    "transmutes from a `&[u8]` to a `&str`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from an integer to a `bool`.
    ///
    /// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let x = 1_u8;
    /// unsafe {
    ///     let _: bool = std::mem::transmute(x); // where x: u8
    /// }
    ///
    /// // should be:
    /// let _: bool = x != 0;
    /// ```
    pub TRANSMUTE_INT_TO_BOOL,
    complexity,
    "transmutes from an integer to a `bool`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from an integer to a float.
    ///
    /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
    /// and safe.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// unsafe {
    ///     let _: f32 = std::mem::transmute(1_u32); // where x: u32
    /// }
    ///
    /// // should be:
    /// let _: f32 = f32::from_bits(1_u32);
    /// ```
    pub TRANSMUTE_INT_TO_FLOAT,
    complexity,
    "transmutes from an integer to a float"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from a float to an integer.
    ///
    /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
    /// and safe.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// unsafe {
    ///     let _: u32 = std::mem::transmute(1f32);
    /// }
    ///
    /// // should be:
    /// let _: u32 = 1f32.to_bits();
    /// ```
    pub TRANSMUTE_FLOAT_TO_INT,
    complexity,
    "transmutes from a float to an integer"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from a pointer to a pointer, or
    /// from a reference to a reference.
    ///
    /// **Why is this bad?** Transmutes are dangerous, and these can instead be
    /// written as casts.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let ptr = &1u32 as *const u32;
    /// unsafe {
    ///     // pointer-to-pointer transmute
    ///     let _: *const f32 = std::mem::transmute(ptr);
    ///     // ref-ref transmute
    ///     let _: &f32 = std::mem::transmute(&1u32);
    /// }
    /// // These can be respectively written:
    /// let _ = ptr as *const f32;
    /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
    /// ```
    pub TRANSMUTE_PTR_TO_PTR,
    complexity,
    "transmutes from a pointer to a pointer / a reference to a reference"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes between collections whose
    /// types have different ABI, size or alignment.
    ///
    /// **Why is this bad?** This is undefined behavior.
    ///
    /// **Known problems:** Currently, we cannot know whether a type is a
    /// collection, so we just lint the ones that come with `std`.
    ///
    /// **Example:**
    /// ```rust
    /// // different size, therefore likely out-of-bounds memory access
    /// // You absolutely do not want this in your code!
    /// unsafe {
    ///     std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
    /// };
    /// ```
    ///
    /// You must always iterate, map and collect the values:
    ///
    /// ```rust
    /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
    /// ```
    pub UNSOUND_COLLECTION_TRANSMUTE,
    correctness,
    "transmute between collections of layout-incompatible types"
}

declare_lint_pass!(Transmute => [
    CROSSPOINTER_TRANSMUTE,
    TRANSMUTE_PTR_TO_REF,
    TRANSMUTE_PTR_TO_PTR,
    USELESS_TRANSMUTE,
    WRONG_TRANSMUTE,
    TRANSMUTE_INT_TO_CHAR,
    TRANSMUTE_BYTES_TO_STR,
    TRANSMUTE_INT_TO_BOOL,
    TRANSMUTE_INT_TO_FLOAT,
    TRANSMUTE_FLOAT_TO_INT,
    UNSOUND_COLLECTION_TRANSMUTE,
    TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
]);

impl<'tcx> LateLintPass<'tcx> for Transmute {
    #[allow(clippy::similar_names, clippy::too_many_lines)]
    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
        if_chain! {
            if let ExprKind::Call(ref path_expr, ref args) = e.kind;
            if let ExprKind::Path(ref qpath) = path_expr.kind;
            if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
            if match_def_path(cx, def_id, &paths::TRANSMUTE);
            then {
                // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts.
                // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`.
                // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
                let const_context = in_constant(cx, e.hir_id);

                let from_ty = cx.typeck_results().expr_ty(&args[0]);
                let to_ty = cx.typeck_results().expr_ty(e);

                let triggered = useless_transmute::check(cx, e, from_ty, to_ty, args);
                if triggered {
                    return;
                }
                let triggered = wrong_transmute::check(cx, e, from_ty, to_ty);
                if triggered {
                    return;
                }
                let triggered = crosspointer_transmute::check(cx, e, from_ty, to_ty);
                if triggered {
                    return;
                }
                let triggered = transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, args, qpath);
                if triggered {
                    return;
                }
                let triggered = transmute_int_to_char::check(cx, e, from_ty, to_ty, args);
                if triggered {
                    return;
                }
                let triggered = transmute_ref_to_ref::check(cx, e, from_ty, to_ty, args, const_context);
                if triggered {
                    return;
                }
                let triggered = transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, args);
                if triggered {
                    return;
                }
                let triggered = transmute_int_to_bool::check(cx, e, from_ty, to_ty, args);
                if triggered {
                    return;
                }
                let triggered = transmute_int_to_float::check(cx, e, from_ty, to_ty, args, const_context);
                if triggered {
                    return;
                }
                let triggered = transmute_float_to_int::check(cx, e, from_ty, to_ty, args, const_context);
                if triggered {
                    return;
                }
                let triggered = unsound_collection_transmute::check(cx, e, from_ty, to_ty);
                if triggered {
                    return;
                }
                let triggered = transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, args);
                if triggered {
                    return;
                }
            }
        }
    }
}
<commit_msg>Refactor calls to lint check functions<commit_after>mod crosspointer_transmute;
mod transmute_float_to_int;
mod transmute_int_to_bool;
mod transmute_int_to_char;
mod transmute_int_to_float;
mod transmute_ptr_to_ptr;
mod transmute_ptr_to_ref;
mod transmute_ref_to_ref;
mod transmutes_expressible_as_ptr_casts;
mod unsound_collection_transmute;
mod useless_transmute;
mod utils;
mod wrong_transmute;

use crate::utils::{in_constant, match_def_path, paths};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes that can't ever be correct on any
    /// architecture.
    ///
    /// **Why is this bad?** It's basically guaranteed to be undefined behaviour.
    ///
    /// **Known problems:** When accessing C, users might want to store pointer
    /// sized objects in `extradata` arguments to save an allocation.
    ///
    /// **Example:**
    /// ```ignore
    /// let ptr: *const T = core::intrinsics::transmute('x')
    /// ```
    pub WRONG_TRANSMUTE,
    correctness,
    "transmutes that are confusing at best, undefined behaviour at worst and always useless"
}

// FIXME: Move this to `complexity` again, after #5343 is fixed
declare_clippy_lint! {
    /// **What it does:** Checks for transmutes to the original type of the object
    /// and transmutes that could be a cast.
    ///
    /// **Why is this bad?** Readability. The code tricks people into thinking that
    /// something complex is going on.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
    /// ```
    pub USELESS_TRANSMUTE,
    nursery,
    "transmutes that have the same to and from types or could be a cast/coercion"
}

// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
declare_clippy_lint! {
    /// **What it does:**Checks for transmutes that could be a pointer cast.
    ///
    /// **Why is this bad?** Readability. The code tricks people into thinking that
    /// something complex is going on.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// # let p: *const [i32] = &[];
    /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
    /// ```
    /// Use instead:
    /// ```rust
    /// # let p: *const [i32] = &[];
    /// p as *const [u16];
    /// ```
    pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
    complexity,
    "transmutes that could be a pointer cast"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes between a type `T` and `*T`.
    ///
    /// **Why is this bad?** It's easy to mistakenly transmute between a type and a
    /// pointer to that type.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// core::intrinsics::transmute(t) // where the result type is the same as
    ///                                // `*t` or `&t`'s
    /// ```
    pub CROSSPOINTER_TRANSMUTE,
    complexity,
    "transmutes that have to or from types that are a pointer to the other"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from a pointer to a reference.
    ///
    /// **Why is this bad?** This can always be rewritten with `&` and `*`.
    ///
    /// **Known problems:**
    /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0,
    /// while dereferencing raw pointer is not stable yet.
    /// If you need to do this in those places,
    /// you would have to use `transmute` instead.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// unsafe {
    ///     let _: &T = std::mem::transmute(p); // where p: *const T
    /// }
    ///
    /// // can be written:
    /// let _: &T = &*p;
    /// ```
    pub TRANSMUTE_PTR_TO_REF,
    complexity,
    "transmutes from a pointer to a reference type"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from an integer to a `char`.
    ///
    /// **Why is this bad?** Not every integer is a Unicode scalar value.
    ///
    /// **Known problems:**
    /// - [`from_u32`] which this lint suggests using is slower than `transmute`
    /// as it needs to validate the input.
    /// If you are certain that the input is always a valid Unicode scalar value,
    /// use [`from_u32_unchecked`] which is as fast as `transmute`
    /// but has a semantically meaningful name.
    /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
    ///
    /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
    /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
    ///
    /// **Example:**
    /// ```rust
    /// let x = 1_u32;
    /// unsafe {
    ///     let _: char = std::mem::transmute(x); // where x: u32
    /// }
    ///
    /// // should be:
    /// let _ = std::char::from_u32(x).unwrap();
    /// ```
    pub TRANSMUTE_INT_TO_CHAR,
    complexity,
    "transmutes from an integer to a `char`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.
    ///
    /// **Why is this bad?** Not every byte slice is a valid UTF-8 string.
    ///
    /// **Known problems:**
    /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
    /// as it needs to validate the input.
    /// If you are certain that the input is always a valid UTF-8,
    /// use [`from_utf8_unchecked`] which is as fast as `transmute`
    /// but has a semantically meaningful name.
    /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
    ///
    /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
    /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
    ///
    /// **Example:**
    /// ```rust
    /// let b: &[u8] = &[1_u8, 2_u8];
    /// unsafe {
    ///     let _: &str = std::mem::transmute(b); // where b: &[u8]
    /// }
    ///
    /// // should be:
    /// let _ = std::str::from_utf8(b).unwrap();
    /// ```
    pub TRANSMUTE_BYTES_TO_STR,
    complexity,
    "transmutes from a `&[u8]` to a `&str`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from an integer to a `bool`.
    ///
    /// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let x = 1_u8;
    /// unsafe {
    ///     let _: bool = std::mem::transmute(x); // where x: u8
    /// }
    ///
    /// // should be:
    /// let _: bool = x != 0;
    /// ```
    pub TRANSMUTE_INT_TO_BOOL,
    complexity,
    "transmutes from an integer to a `bool`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from an integer to a float.
    ///
    /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
    /// and safe.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// unsafe {
    ///     let _: f32 = std::mem::transmute(1_u32); // where x: u32
    /// }
    ///
    /// // should be:
    /// let _: f32 = f32::from_bits(1_u32);
    /// ```
    pub TRANSMUTE_INT_TO_FLOAT,
    complexity,
    "transmutes from an integer to a float"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from a float to an integer.
    ///
    /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
    /// and safe.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// unsafe {
    ///     let _: u32 = std::mem::transmute(1f32);
    /// }
    ///
    /// // should be:
    /// let _: u32 = 1f32.to_bits();
    /// ```
    pub TRANSMUTE_FLOAT_TO_INT,
    complexity,
    "transmutes from a float to an integer"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes from a pointer to a pointer, or
    /// from a reference to a reference.
    ///
    /// **Why is this bad?** Transmutes are dangerous, and these can instead be
    /// written as casts.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let ptr = &1u32 as *const u32;
    /// unsafe {
    ///     // pointer-to-pointer transmute
    ///     let _: *const f32 = std::mem::transmute(ptr);
    ///     // ref-ref transmute
    ///     let _: &f32 = std::mem::transmute(&1u32);
    /// }
    /// // These can be respectively written:
    /// let _ = ptr as *const f32;
    /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
    /// ```
    pub TRANSMUTE_PTR_TO_PTR,
    complexity,
    "transmutes from a pointer to a pointer / a reference to a reference"
}

declare_clippy_lint! {
    /// **What it does:** Checks for transmutes between collections whose
    /// types have different ABI, size or alignment.
    ///
    /// **Why is this bad?** This is undefined behavior.
    ///
    /// **Known problems:** Currently, we cannot know whether a type is a
    /// collection, so we just lint the ones that come with `std`.
    ///
    /// **Example:**
    /// ```rust
    /// // different size, therefore likely out-of-bounds memory access
    /// // You absolutely do not want this in your code!
    /// unsafe {
    ///     std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
    /// };
    /// ```
    ///
    /// You must always iterate, map and collect the values:
    ///
    /// ```rust
    /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
    /// ```
    pub UNSOUND_COLLECTION_TRANSMUTE,
    correctness,
    "transmute between collections of layout-incompatible types"
}

declare_lint_pass!(Transmute => [
    CROSSPOINTER_TRANSMUTE,
    TRANSMUTE_PTR_TO_REF,
    TRANSMUTE_PTR_TO_PTR,
    USELESS_TRANSMUTE,
    WRONG_TRANSMUTE,
    TRANSMUTE_INT_TO_CHAR,
    TRANSMUTE_BYTES_TO_STR,
    TRANSMUTE_INT_TO_BOOL,
    TRANSMUTE_INT_TO_FLOAT,
    TRANSMUTE_FLOAT_TO_INT,
    UNSOUND_COLLECTION_TRANSMUTE,
    TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
]);

impl<'tcx> LateLintPass<'tcx> for Transmute {
    #[allow(clippy::similar_names, clippy::too_many_lines)]
    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
        if_chain! {
            if let ExprKind::Call(ref path_expr, ref args) = e.kind;
            if let ExprKind::Path(ref qpath) = path_expr.kind;
            if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
            if match_def_path(cx, def_id, &paths::TRANSMUTE);
            then {
                // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts.
                // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`.
                // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
                let const_context = in_constant(cx, e.hir_id);

                let from_ty = cx.typeck_results().expr_ty(&args[0]);
                let to_ty = cx.typeck_results().expr_ty(e);

                // If useless_transmute is triggered, the other lints can be skipped.
                if useless_transmute::check(cx, e, from_ty, to_ty, args) {
                    return;
                }

                let mut linted = wrong_transmute::check(cx, e, from_ty, to_ty);
                linted |= crosspointer_transmute::check(cx, e, from_ty, to_ty);
                linted |= transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, args, qpath);
                linted |= transmute_int_to_char::check(cx, e, from_ty, to_ty, args);
                linted |= transmute_ref_to_ref::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, args);
                linted |= transmute_int_to_bool::check(cx, e, from_ty, to_ty, args);
                linted |= transmute_int_to_float::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= transmute_float_to_int::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= unsound_collection_transmute::check(cx, e, from_ty, to_ty);

                if !linted {
                    transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, args);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>pub fn arrnum_int_mult(arr_num: &[u8], basenum: u8, base_ten_int_fact: u8) -> Vec<u8> {
    let mut carry: u16 = 0;
    let mut rem: u16;
    let mut new_amount: u16;
    let fact: u16 = u16::from(base_ten_int_fact);
    let base: u16 = u16::from(basenum);

    let mut ret_rev: Vec<u8> = Vec::new();
    let mut it = arr_num.iter().rev();
    loop {
        let i = it.next();
        match i {
            Some(u) => {
                new_amount = (u16::from(*u) * fact) + carry;
                rem = new_amount % base;
                carry = (new_amount - rem) / base;
                ret_rev.push(rem as u8)
            }
            None => {
                while carry != 0 {
                    rem = carry % base;
                    carry = (carry - rem) / base;
                    ret_rev.push(rem as u8);
                }
                break;
            }
        }
    }
    #[allow(clippy::map_clone)]
    let ret: Vec<u8> = ret_rev.iter().rev().map(|x| *x).collect();
    ret
}

pub struct Remainder<'a> {
    pub position: usize,
    pub replace: Vec<u8>,
    pub arr_num: &'a Vec<u8>,
}

pub struct DivOut<'a> {
    pub quotient: u8,
    pub remainder: Remainder<'a>,
}

pub fn arrnum_int_div_step(
    rem_in: Remainder,
    radix_in: u8,
    base_ten_int_divisor: u8,
    after_decimal: bool,
) -> DivOut {
    let mut rem_out = Remainder {
        position: rem_in.position,
        replace: Vec::new(),
        arr_num: rem_in.arr_num,
    };

    let mut bufferval: u16 = 0;
    let base: u16 = u16::from(radix_in);
    let divisor: u16 = u16::from(base_ten_int_divisor);
    let mut traversed = 0;

    let mut quotient = 0;
    let refd_vals = &rem_in.arr_num[rem_in.position + rem_in.replace.len()..];
    let mut it_replace = rem_in.replace.iter();
    let mut it_f = refd_vals.iter();
    loop {
        let u = match it_replace.next() {
            Some(u_rep) => u16::from(*u_rep),
            None => match it_f.next() {
                Some(u_orig) => u16::from(*u_orig),
                None => {
                    if !after_decimal {
                        break;
                    }
                    0
                }
            },
        };
        traversed += 1;
        bufferval += u;
        if bufferval > divisor {
            while bufferval >= divisor {
                quotient += 1;
                bufferval -= divisor;
            }
            rem_out.replace = if bufferval == 0 {
                Vec::new()
            } else {
                let remainder_as_arrnum = unsigned_to_arrnum(bufferval);
                base_conv_vec(&remainder_as_arrnum, 10, radix_in)
            };
            rem_out.position += 1 + (traversed - rem_out.replace.len());
            break;
        } else {
            bufferval *= base;
        }
    }
    DivOut {
        quotient,
        remainder: rem_out,
    }
}
// pub struct ArrFloat {
// pub leading_zeros: u8,
// pub values: Vec<u8>,
// pub basenum: u8
// }
//
// pub struct ArrFloatDivOut {
// pub quotient: u8,
// pub remainder: ArrFloat
// }
//
// pub fn arrfloat_int_div(
// arrfloat_in : &ArrFloat,
// base_ten_int_divisor : u8,
// precision : u16
// ) -> DivOut  {
//
// let mut remainder = ArrFloat {
// basenum: arrfloat_in.basenum,
// leading_zeros: arrfloat_in.leading_zeroes,
// values: Vec<u8>::new()
// }
// let mut quotient = 0;
//
// let mut bufferval : u16 = 0;
// let base : u16 = arrfloat_in.basenum as u16;
// let divisor : u16 = base_ten_int_divisor as u16;
//
// let mut it_f = arrfloat_in.values.iter();
// let mut position = 0 + arrfloat_in.leading_zeroes as u16;
// let mut at_end = false;
// while position< precision {
// let next_digit = match it_f.next() {
// Some(c) => {}
// None => { 0 }
// }
// match u_cur {
// Some(u) => {
// bufferval += u.clone() as u16;
// if bufferval > divisor {
// while bufferval >= divisor {
// quotient+=1;
// bufferval -= divisor;
// }
// if bufferval == 0 {
// rem_out.position +=1;
// } else {
// rem_out.replace = Some(bufferval as u8);
// }
// break;
// } else {
// bufferval *= base;
// }
// },
// None => {
// break;
// }
// }
// u_cur = it_f.next().clone();
// rem_out.position+=1;
// }
// ArrFloatDivOut { quotient: quotient, remainder: remainder }
// }
//
pub fn arrnum_int_add(arrnum: &[u8], basenum: u8, base_ten_int_term: u8) -> Vec<u8> {
    let mut carry: u16 = u16::from(base_ten_int_term);
    let mut rem: u16;
    let mut new_amount: u16;
    let base: u16 = u16::from(basenum);

    let mut ret_rev: Vec<u8> = Vec::new();
    let mut it = arrnum.iter().rev();
    loop {
        let i = it.next();
        match i {
            Some(u) => {
                new_amount = u16::from(*u) + carry;
                rem = new_amount % base;
                carry = (new_amount - rem) / base;
                ret_rev.push(rem as u8)
            }
            None => {
                while carry != 0 {
                    rem = carry % base;
                    carry = (carry - rem) / base;
                    ret_rev.push(rem as u8);
                }
                break;
            }
        }
    }
    #[allow(clippy::map_clone)]
    let ret: Vec<u8> = ret_rev.iter().rev().map(|x| *x).collect();
    ret
}

pub fn base_conv_vec(src: &[u8], radix_src: u8, radix_dest: u8) -> Vec<u8> {
    let mut result: Vec<u8> = Vec::new();
    result.push(0);
    for i in src {
        result = arrnum_int_mult(&result, radix_dest, radix_src);
        result = arrnum_int_add(&result, radix_dest, *i);
    }
    result
}

pub fn unsigned_to_arrnum(src: u16) -> Vec<u8> {
    let mut result: Vec<u8> = Vec::new();
    let mut src_tmp: u16 = src;
    while src_tmp > 0 {
        result.push((src_tmp % 10) as u8);
        src_tmp /= 10;
    }
    result.reverse();
    result
}

// temporary needs-improvement-function
#[allow(unused_variables)]
pub fn base_conv_float(src: &[u8], radix_src: u8, radix_dest: u8) -> f64 {
    // it would require a lot of addl code
    // to implement this for arbitrary string input.
    // until then, the below operates as an outline
    // of how it would work.
    let mut result: Vec<u8> = Vec::new();
    result.push(0);
    let mut factor: f64 = 1_f64;
    let radix_src_float: f64 = f64::from(radix_src);
    let mut r: f64 = 0_f64;
    for (i, u) in src.iter().enumerate() {
        if i > 15 {
            break;
        }
        factor /= radix_src_float;
        r += factor * f64::from(*u)
    }
    r
}

pub fn str_to_arrnum(src: &str, radix_def_src: &dyn RadixDef) -> Vec<u8> {
    let mut intermed_in: Vec<u8> = Vec::new();
    for c in src.chars() {
        match radix_def_src.from_char(c) {
            Some(u) => {
                intermed_in.push(u);
            }
            None => {} //todo err msg on incorrect
        }
    }
    intermed_in
}

pub fn arrnum_to_str(src: &[u8], radix_def_dest: &dyn RadixDef) -> String {
    let mut str_out = String::new();
    for u in src.iter() {
        match radix_def_dest.from_u8(u.clone()) {
            Some(c) => {
                str_out.push(c);
            }
            None => {} //todo
        }
    }
    str_out
}

#[allow(unused_variables)]
pub fn base_conv_str(
    src: &str,
    radix_def_src: &dyn RadixDef,
    radix_def_dest: &dyn RadixDef,
) -> String {
    let intermed_in: Vec<u8> = str_to_arrnum(src, radix_def_src);
    let intermed_out = base_conv_vec(
        &intermed_in,
        radix_def_src.get_max(),
        radix_def_dest.get_max(),
    );
    arrnum_to_str(&intermed_out, radix_def_dest)
}

pub trait RadixDef {
    fn get_max(&self) -> u8;
    fn from_char(&self, x: char) -> Option<u8>;
    fn from_u8(&self, x: u8) -> Option<char>;
}
pub struct RadixTen;

const ZERO_ASC: u8 = b'0';
const UPPER_A_ASC: u8 = b'A';
const LOWER_A_ASC: u8 = b'a';

impl RadixDef for RadixTen {
    fn get_max(&self) -> u8 {
        10
    }
    fn from_char(&self, c: char) -> Option<u8> {
        match c {
            '0'..='9' => Some(c as u8 - ZERO_ASC),
            _ => None,
        }
    }
    fn from_u8(&self, u: u8) -> Option<char> {
        match u {
            0..=9 => Some((ZERO_ASC + u) as char),
            _ => None,
        }
    }
}
pub struct RadixHex;
impl RadixDef for RadixHex {
    fn get_max(&self) -> u8 {
        16
    }
    fn from_char(&self, c: char) -> Option<u8> {
        match c {
            '0'..='9' => Some(c as u8 - ZERO_ASC),
            'A'..='F' => Some(c as u8 + 10 - UPPER_A_ASC),
            'a'..='f' => Some(c as u8 + 10 - LOWER_A_ASC),
            _ => None,
        }
    }
    fn from_u8(&self, u: u8) -> Option<char> {
        match u {
            0..=9 => Some((ZERO_ASC + u) as char),
            10..=15 => Some((UPPER_A_ASC + (u - 10)) as char),
            _ => None,
        }
    }
}

mod tests;
<commit_msg>refactor/polish ~ fix `cargo clippy` complaints (allow single_match)<commit_after>pub fn arrnum_int_mult(arr_num: &[u8], basenum: u8, base_ten_int_fact: u8) -> Vec<u8> {
    let mut carry: u16 = 0;
    let mut rem: u16;
    let mut new_amount: u16;
    let fact: u16 = u16::from(base_ten_int_fact);
    let base: u16 = u16::from(basenum);

    let mut ret_rev: Vec<u8> = Vec::new();
    let mut it = arr_num.iter().rev();
    loop {
        let i = it.next();
        match i {
            Some(u) => {
                new_amount = (u16::from(*u) * fact) + carry;
                rem = new_amount % base;
                carry = (new_amount - rem) / base;
                ret_rev.push(rem as u8)
            }
            None => {
                while carry != 0 {
                    rem = carry % base;
                    carry = (carry - rem) / base;
                    ret_rev.push(rem as u8);
                }
                break;
            }
        }
    }
    #[allow(clippy::map_clone)]
    let ret: Vec<u8> = ret_rev.iter().rev().map(|x| *x).collect();
    ret
}

pub struct Remainder<'a> {
    pub position: usize,
    pub replace: Vec<u8>,
    pub arr_num: &'a Vec<u8>,
}

pub struct DivOut<'a> {
    pub quotient: u8,
    pub remainder: Remainder<'a>,
}

pub fn arrnum_int_div_step(
    rem_in: Remainder,
    radix_in: u8,
    base_ten_int_divisor: u8,
    after_decimal: bool,
) -> DivOut {
    let mut rem_out = Remainder {
        position: rem_in.position,
        replace: Vec::new(),
        arr_num: rem_in.arr_num,
    };

    let mut bufferval: u16 = 0;
    let base: u16 = u16::from(radix_in);
    let divisor: u16 = u16::from(base_ten_int_divisor);
    let mut traversed = 0;

    let mut quotient = 0;
    let refd_vals = &rem_in.arr_num[rem_in.position + rem_in.replace.len()..];
    let mut it_replace = rem_in.replace.iter();
    let mut it_f = refd_vals.iter();
    loop {
        let u = match it_replace.next() {
            Some(u_rep) => u16::from(*u_rep),
            None => match it_f.next() {
                Some(u_orig) => u16::from(*u_orig),
                None => {
                    if !after_decimal {
                        break;
                    }
                    0
                }
            },
        };
        traversed += 1;
        bufferval += u;
        if bufferval > divisor {
            while bufferval >= divisor {
                quotient += 1;
                bufferval -= divisor;
            }
            rem_out.replace = if bufferval == 0 {
                Vec::new()
            } else {
                let remainder_as_arrnum = unsigned_to_arrnum(bufferval);
                base_conv_vec(&remainder_as_arrnum, 10, radix_in)
            };
            rem_out.position += 1 + (traversed - rem_out.replace.len());
            break;
        } else {
            bufferval *= base;
        }
    }
    DivOut {
        quotient,
        remainder: rem_out,
    }
}
// pub struct ArrFloat {
// pub leading_zeros: u8,
// pub values: Vec<u8>,
// pub basenum: u8
// }
//
// pub struct ArrFloatDivOut {
// pub quotient: u8,
// pub remainder: ArrFloat
// }
//
// pub fn arrfloat_int_div(
// arrfloat_in : &ArrFloat,
// base_ten_int_divisor : u8,
// precision : u16
// ) -> DivOut  {
//
// let mut remainder = ArrFloat {
// basenum: arrfloat_in.basenum,
// leading_zeros: arrfloat_in.leading_zeroes,
// values: Vec<u8>::new()
// }
// let mut quotient = 0;
//
// let mut bufferval : u16 = 0;
// let base : u16 = arrfloat_in.basenum as u16;
// let divisor : u16 = base_ten_int_divisor as u16;
//
// let mut it_f = arrfloat_in.values.iter();
// let mut position = 0 + arrfloat_in.leading_zeroes as u16;
// let mut at_end = false;
// while position< precision {
// let next_digit = match it_f.next() {
// Some(c) => {}
// None => { 0 }
// }
// match u_cur {
// Some(u) => {
// bufferval += u.clone() as u16;
// if bufferval > divisor {
// while bufferval >= divisor {
// quotient+=1;
// bufferval -= divisor;
// }
// if bufferval == 0 {
// rem_out.position +=1;
// } else {
// rem_out.replace = Some(bufferval as u8);
// }
// break;
// } else {
// bufferval *= base;
// }
// },
// None => {
// break;
// }
// }
// u_cur = it_f.next().clone();
// rem_out.position+=1;
// }
// ArrFloatDivOut { quotient: quotient, remainder: remainder }
// }
//
pub fn arrnum_int_add(arrnum: &[u8], basenum: u8, base_ten_int_term: u8) -> Vec<u8> {
    let mut carry: u16 = u16::from(base_ten_int_term);
    let mut rem: u16;
    let mut new_amount: u16;
    let base: u16 = u16::from(basenum);

    let mut ret_rev: Vec<u8> = Vec::new();
    let mut it = arrnum.iter().rev();
    loop {
        let i = it.next();
        match i {
            Some(u) => {
                new_amount = u16::from(*u) + carry;
                rem = new_amount % base;
                carry = (new_amount - rem) / base;
                ret_rev.push(rem as u8)
            }
            None => {
                while carry != 0 {
                    rem = carry % base;
                    carry = (carry - rem) / base;
                    ret_rev.push(rem as u8);
                }
                break;
            }
        }
    }
    #[allow(clippy::map_clone)]
    let ret: Vec<u8> = ret_rev.iter().rev().map(|x| *x).collect();
    ret
}

pub fn base_conv_vec(src: &[u8], radix_src: u8, radix_dest: u8) -> Vec<u8> {
    let mut result: Vec<u8> = Vec::new();
    result.push(0);
    for i in src {
        result = arrnum_int_mult(&result, radix_dest, radix_src);
        result = arrnum_int_add(&result, radix_dest, *i);
    }
    result
}

pub fn unsigned_to_arrnum(src: u16) -> Vec<u8> {
    let mut result: Vec<u8> = Vec::new();
    let mut src_tmp: u16 = src;
    while src_tmp > 0 {
        result.push((src_tmp % 10) as u8);
        src_tmp /= 10;
    }
    result.reverse();
    result
}

// temporary needs-improvement-function
#[allow(unused_variables)]
pub fn base_conv_float(src: &[u8], radix_src: u8, radix_dest: u8) -> f64 {
    // it would require a lot of addl code
    // to implement this for arbitrary string input.
    // until then, the below operates as an outline
    // of how it would work.
    let mut result: Vec<u8> = Vec::new();
    result.push(0);
    let mut factor: f64 = 1_f64;
    let radix_src_float: f64 = f64::from(radix_src);
    let mut r: f64 = 0_f64;
    for (i, u) in src.iter().enumerate() {
        if i > 15 {
            break;
        }
        factor /= radix_src_float;
        r += factor * f64::from(*u)
    }
    r
}

pub fn str_to_arrnum(src: &str, radix_def_src: &dyn RadixDef) -> Vec<u8> {
    let mut intermed_in: Vec<u8> = Vec::new();
    for c in src.chars() {
        #[allow(clippy::single_match)]
        match radix_def_src.from_char(c) {
            Some(u) => {
                intermed_in.push(u);
            }
            None => {} //todo err msg on incorrect
        }
    }
    intermed_in
}

pub fn arrnum_to_str(src: &[u8], radix_def_dest: &dyn RadixDef) -> String {
    let mut str_out = String::new();
    for u in src.iter() {
        #[allow(clippy::single_match)]
        match radix_def_dest.from_u8(u.clone()) {
            Some(c) => {
                str_out.push(c);
            }
            None => {} //todo
        }
    }
    str_out
}

#[allow(unused_variables)]
pub fn base_conv_str(
    src: &str,
    radix_def_src: &dyn RadixDef,
    radix_def_dest: &dyn RadixDef,
) -> String {
    let intermed_in: Vec<u8> = str_to_arrnum(src, radix_def_src);
    let intermed_out = base_conv_vec(
        &intermed_in,
        radix_def_src.get_max(),
        radix_def_dest.get_max(),
    );
    arrnum_to_str(&intermed_out, radix_def_dest)
}

pub trait RadixDef {
    fn get_max(&self) -> u8;
    fn from_char(&self, x: char) -> Option<u8>;
    fn from_u8(&self, x: u8) -> Option<char>;
}
pub struct RadixTen;

const ZERO_ASC: u8 = b'0';
const UPPER_A_ASC: u8 = b'A';
const LOWER_A_ASC: u8 = b'a';

impl RadixDef for RadixTen {
    fn get_max(&self) -> u8 {
        10
    }
    fn from_char(&self, c: char) -> Option<u8> {
        match c {
            '0'..='9' => Some(c as u8 - ZERO_ASC),
            _ => None,
        }
    }
    fn from_u8(&self, u: u8) -> Option<char> {
        match u {
            0..=9 => Some((ZERO_ASC + u) as char),
            _ => None,
        }
    }
}
pub struct RadixHex;
impl RadixDef for RadixHex {
    fn get_max(&self) -> u8 {
        16
    }
    fn from_char(&self, c: char) -> Option<u8> {
        match c {
            '0'..='9' => Some(c as u8 - ZERO_ASC),
            'A'..='F' => Some(c as u8 + 10 - UPPER_A_ASC),
            'a'..='f' => Some(c as u8 + 10 - LOWER_A_ASC),
            _ => None,
        }
    }
    fn from_u8(&self, u: u8) -> Option<char> {
        match u {
            0..=9 => Some((ZERO_ASC + u) as char),
            10..=15 => Some((UPPER_A_ASC + (u - 10)) as char),
            _ => None,
        }
    }
}

mod tests;
<|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/. */

/// Liberally derived from the [Firefox JS implementation](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webbrowser.js).
/// Connection point for remote devtools that wish to investigate a particular tab's contents.
/// Supports dynamic attaching and detaching which control notifications of navigation, etc.

use actor::{Actor, ActorRegistry};
use actors::console::ConsoleActor;
use devtools_traits::WantsLiveNotifications;
use protocol::JsonPacketStream;

use serialize::json;
use std::io::TcpStream;

#[derive(RustcEncodable)]
struct TabTraits;

#[derive(RustcEncodable)]
struct TabAttachedReply {
    from: String,
    __type__: String,
    threadActor: String,
    cacheDisabled: bool,
    javascriptEnabled: bool,
    traits: TabTraits,
}

#[derive(RustcEncodable)]
struct TabDetachedReply {
    from: String,
    __type__: String,
}

#[derive(RustcEncodable)]
struct ReconfigureReply {
    from: String
}

#[derive(RustcEncodable)]
struct ListFramesReply {
    from: String,
    frames: Vec<FrameMsg>,
}

#[derive(RustcEncodable)]
struct FrameMsg {
    id: uint,
    url: String,
    title: String,
    parentID: uint,
}

#[derive(RustcEncodable)]
pub struct TabActorMsg {
    actor: String,
    title: String,
    url: String,
    outerWindowID: uint,
    consoleActor: String,
    inspectorActor: String,
}

pub struct TabActor {
    pub name: String,
    pub title: String,
    pub url: String,
    pub console: String,
    pub inspector: String,
}

impl Actor for TabActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &String,
                      _msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type.as_slice() {
            "reconfigure" => {
                stream.write_json_packet(&ReconfigureReply { from: self.name() });
                true
            }

            // https://wiki.mozilla.org/Remote_Debugging_Protocol#Listing_Browser_Tabs
            // (see "To attach to a _tabActor_")
            "attach" => {
                let msg = TabAttachedReply {
                    from: self.name(),
                    __type__: "tabAttached".to_string(),
                    threadActor: self.name(),
                    cacheDisabled: false,
                    javascriptEnabled: true,
                    traits: TabTraits,
                };
                let console_actor = registry.find::<ConsoleActor>(self.console.as_slice());
                console_actor.streams.borrow_mut().push(stream.clone());
                stream.write_json_packet(&msg);
                console_actor.script_chan.send(WantsLiveNotifications(console_actor.pipeline, true));
                true
            }

            //FIXME: The current implementation won't work for multiple connections. Need to ensure 105
            //       that the correct stream is removed.
            "detach" => {
                let msg = TabDetachedReply {
                    from: self.name(),
                    __type__: "detached".to_string(),
                };
                let console_actor = registry.find::<ConsoleActor>(self.console.as_slice());
                console_actor.streams.borrow_mut().pop();
                stream.write_json_packet(&msg);
                console_actor.script_chan.send(WantsLiveNotifications(console_actor.pipeline, false));
                true
            }

            "listFrames" => {
                let msg = ListFramesReply {
                    from: self.name(),
                    frames: vec!(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false
        })
    }
}

impl TabActor {
    pub fn encodable(&self) -> TabActorMsg {
        TabActorMsg {
            actor: self.name(),
            title: self.title.clone(),
            url: self.url.clone(),
            outerWindowID: 0, //FIXME: this should probably be the pipeline id
            consoleActor: self.console.clone(),
            inspectorActor: self.inspector.clone(),
        }
    }
}
<commit_msg>Fix warnings introduced in #4852.<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/. */

/// Liberally derived from the [Firefox JS implementation](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webbrowser.js).
/// Connection point for remote devtools that wish to investigate a particular tab's contents.
/// Supports dynamic attaching and detaching which control notifications of navigation, etc.

use actor::{Actor, ActorRegistry};
use actors::console::ConsoleActor;
use devtools_traits::WantsLiveNotifications;
use protocol::JsonPacketStream;

use serialize::json;
use std::io::TcpStream;

#[derive(RustcEncodable)]
struct TabTraits;

#[derive(RustcEncodable)]
struct TabAttachedReply {
    from: String,
    __type__: String,
    threadActor: String,
    cacheDisabled: bool,
    javascriptEnabled: bool,
    traits: TabTraits,
}

#[derive(RustcEncodable)]
struct TabDetachedReply {
    from: String,
    __type__: String,
}

#[derive(RustcEncodable)]
struct ReconfigureReply {
    from: String
}

#[derive(RustcEncodable)]
struct ListFramesReply {
    from: String,
    frames: Vec<FrameMsg>,
}

#[derive(RustcEncodable)]
struct FrameMsg {
    id: uint,
    url: String,
    title: String,
    parentID: uint,
}

#[derive(RustcEncodable)]
pub struct TabActorMsg {
    actor: String,
    title: String,
    url: String,
    outerWindowID: uint,
    consoleActor: String,
    inspectorActor: String,
}

pub struct TabActor {
    pub name: String,
    pub title: String,
    pub url: String,
    pub console: String,
    pub inspector: String,
}

impl Actor for TabActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(&self,
                      registry: &ActorRegistry,
                      msg_type: &String,
                      _msg: &json::Object,
                      stream: &mut TcpStream) -> Result<bool, ()> {
        Ok(match msg_type.as_slice() {
            "reconfigure" => {
                stream.write_json_packet(&ReconfigureReply { from: self.name() });
                true
            }

            // https://wiki.mozilla.org/Remote_Debugging_Protocol#Listing_Browser_Tabs
            // (see "To attach to a _tabActor_")
            "attach" => {
                let msg = TabAttachedReply {
                    from: self.name(),
                    __type__: "tabAttached".to_string(),
                    threadActor: self.name(),
                    cacheDisabled: false,
                    javascriptEnabled: true,
                    traits: TabTraits,
                };
                let console_actor = registry.find::<ConsoleActor>(self.console.as_slice());
                console_actor.streams.borrow_mut().push(stream.clone());
                stream.write_json_packet(&msg);
                console_actor.script_chan.send(WantsLiveNotifications(console_actor.pipeline, true)).unwrap();
                true
            }

            //FIXME: The current implementation won't work for multiple connections. Need to ensure 105
            //       that the correct stream is removed.
            "detach" => {
                let msg = TabDetachedReply {
                    from: self.name(),
                    __type__: "detached".to_string(),
                };
                let console_actor = registry.find::<ConsoleActor>(self.console.as_slice());
                console_actor.streams.borrow_mut().pop();
                stream.write_json_packet(&msg);
                console_actor.script_chan.send(WantsLiveNotifications(console_actor.pipeline, false)).unwrap();
                true
            }

            "listFrames" => {
                let msg = ListFramesReply {
                    from: self.name(),
                    frames: vec!(),
                };
                stream.write_json_packet(&msg);
                true
            }

            _ => false
        })
    }
}

impl TabActor {
    pub fn encodable(&self) -> TabActorMsg {
        TabActorMsg {
            actor: self.name(),
            title: self.title.clone(),
            url: self.url.clone(),
            outerWindowID: 0, //FIXME: this should probably be the pipeline id
            consoleActor: self.console.clone(),
            inspectorActor: self.inspector.clone(),
        }
    }
}
<|endoftext|>