<commit_before>//! Defines `Body`: a lowered representation of bodies of functions, statics and
//! consts.
mod lower;
pub mod scope;

use std::{mem, ops::Index, sync::Arc};

use drop_bomb::DropBomb;
use either::Either;
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
use ra_arena::{map::ArenaMap, Arena};
use ra_prof::profile;
use ra_syntax::{ast, AstNode, AstPtr};
use rustc_hash::FxHashMap;

use crate::{
    db::DefDatabase,
    expr::{Expr, ExprId, Pat, PatId},
    item_scope::BuiltinShadowMode,
    item_scope::ItemScope,
    nameres::CrateDefMap,
    path::{ModPath, Path},
    src::HasSource,
    AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
};

pub(crate) struct Expander {
    crate_def_map: Arc<CrateDefMap>,
    current_file_id: HirFileId,
    hygiene: Hygiene,
    ast_id_map: Arc<AstIdMap>,
    module: ModuleId,
}

impl Expander {
    pub(crate) fn new(
        db: &dyn DefDatabase,
        current_file_id: HirFileId,
        module: ModuleId,
    ) -> Expander {
        let crate_def_map = db.crate_def_map(module.krate);
        let hygiene = Hygiene::new(db.upcast(), current_file_id);
        let ast_id_map = db.ast_id_map(current_file_id);
        Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module }
    }

    pub(crate) fn enter_expand<T: ast::AstNode>(
        &mut self,
        db: &dyn DefDatabase,
        local_scope: Option<&ItemScope>,
        macro_call: ast::MacroCall,
    ) -> Option<(Mark, T)> {
        let macro_call = InFile::new(self.current_file_id, &macro_call);

        if let Some(call_id) = macro_call.as_call_id(db, |path| {
            if let Some(local_scope) = local_scope {
                if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
                    return Some(def);
                }
            }
            self.resolve_path_as_macro(db, &path)
        }) {
            let file_id = call_id.as_file();
            if let Some(node) = db.parse_or_expand(file_id) {
                if let Some(expr) = T::cast(node) {
                    log::debug!("macro expansion {:#?}", expr.syntax());

                    let mark = Mark {
                        file_id: self.current_file_id,
                        ast_id_map: mem::take(&mut self.ast_id_map),
                        bomb: DropBomb::new("expansion mark dropped"),
                    };
                    self.hygiene = Hygiene::new(db.upcast(), file_id);
                    self.current_file_id = file_id;
                    self.ast_id_map = db.ast_id_map(file_id);

                    return Some((mark, expr));
                }
            }
        }

        // FIXME: Instead of just dropping the error from expansion
        // report it
        None
    }

    pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
        self.hygiene = Hygiene::new(db.upcast(), mark.file_id);
        self.current_file_id = mark.file_id;
        self.ast_id_map = mem::take(&mut mark.ast_id_map);
        mark.bomb.defuse();
    }

    pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
        InFile { file_id: self.current_file_id, value }
    }

    fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
        Path::from_src(path, &self.hygiene)
    }

    fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> {
        self.crate_def_map
            .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other)
            .0
            .take_macros()
    }

    fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> {
        let file_local_id = self.ast_id_map.ast_id(item);
        AstId::new(self.current_file_id, file_local_id)
    }
}

pub(crate) struct Mark {
    file_id: HirFileId,
    ast_id_map: Arc<AstIdMap>,
    bomb: DropBomb,
}

/// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)]
pub struct Body {
    pub exprs: Arena<Expr>,
    pub pats: Arena<Pat>,
    /// The patterns for the function's parameters. While the parameter types are
    /// part of the function signature, the patterns are not (they don't change
    /// the external type of the function).
    ///
    /// If this `Body` is for the body of a constant, this will just be
    /// empty.
    pub params: Vec<PatId>,
    /// The `ExprId` of the actual body expression.
    pub body_expr: ExprId,
    pub item_scope: ItemScope,
}

pub type ExprPtr = Either<AstPtr<ast::Expr>, AstPtr<ast::RecordField>>;
pub type ExprSource = InFile<ExprPtr>;

pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
pub type PatSource = InFile<PatPtr>;

/// An item body together with the mapping from syntax nodes to HIR expression
/// IDs. This is needed to go from e.g. a position in a file to the HIR
/// expression containing it; but for type inference etc., we want to operate on
/// a structure that is agnostic to the actual positions of expressions in the
/// file, so that we don't recompute types whenever some whitespace is typed.
///
/// One complication here is that, due to macro expansion, a single `Body` might
/// be spread across several files. So, for each ExprId and PatId, we record
/// both the HirFileId and the position inside the file. However, we only store
/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
/// this properly for macros.
#[derive(Default, Debug, Eq, PartialEq)]
pub struct BodySourceMap {
    expr_map: FxHashMap<ExprSource, ExprId>,
    expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
    pat_map: FxHashMap<PatSource, PatId>,
    pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
    field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>,
    expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
}

#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
pub struct SyntheticSyntax;

impl Body {
    pub(crate) fn body_with_source_map_query(
        db: &dyn DefDatabase,
        def: DefWithBodyId,
    ) -> (Arc<Body>, Arc<BodySourceMap>) {
        let _p = profile("body_with_source_map_query");
        let mut params = None;

        let (file_id, module, body) = match def {
            DefWithBodyId::FunctionId(f) => {
                let f = f.lookup(db);
                let src = f.source(db);
                params = src.value.param_list();
                (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
            }
            DefWithBodyId::ConstId(c) => {
                let c = c.lookup(db);
                let src = c.source(db);
                (src.file_id, c.module(db), src.value.body())
            }
            DefWithBodyId::StaticId(s) => {
                let s = s.lookup(db);
                let src = s.source(db);
                (src.file_id, s.module(db), src.value.body())
            }
        };
        let expander = Expander::new(db, file_id, module);
        let (body, source_map) = Body::new(db, def, expander, params, body);
        (Arc::new(body), Arc::new(source_map))
    }

    pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
        db.body_with_source_map(def).0
    }

    fn new(
        db: &dyn DefDatabase,
        def: DefWithBodyId,
        expander: Expander,
        params: Option<ast::ParamList>,
        body: Option<ast::Expr>,
    ) -> (Body, BodySourceMap) {
        lower::lower(db, def, expander, params, body)
    }
}

impl Index<ExprId> for Body {
    type Output = Expr;

    fn index(&self, expr: ExprId) -> &Expr {
        &self.exprs[expr]
    }
}

impl Index<PatId> for Body {
    type Output = Pat;

    fn index(&self, pat: PatId) -> &Pat {
        &self.pats[pat]
    }
}

impl BodySourceMap {
    pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
        self.expr_map_back[expr]
    }

    pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
        let src = node.map(|it| Either::Left(AstPtr::new(it)));
        self.expr_map.get(&src).cloned()
    }

    pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> {
        let src = node.map(|it| AstPtr::new(it));
        self.expansions.get(&src).cloned()
    }

    pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> {
        let src = node.map(|it| Either::Right(AstPtr::new(it)));
        self.expr_map.get(&src).cloned()
    }

    pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
        self.pat_map_back[pat]
    }

    pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
        let src = node.map(|it| Either::Left(AstPtr::new(it)));
        self.pat_map.get(&src).cloned()
    }

    pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::RecordField> {
        self.field_map[&(expr, field)]
    }
}
<commit_msg>Add recursive limit in expression macro expansion<commit_after>//! Defines `Body`: a lowered representation of bodies of functions, statics and
//! consts.
mod lower;
pub mod scope;

use std::{mem, ops::Index, sync::Arc};

use drop_bomb::DropBomb;
use either::Either;
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
use ra_arena::{map::ArenaMap, Arena};
use ra_prof::profile;
use ra_syntax::{ast, AstNode, AstPtr};
use rustc_hash::FxHashMap;

use crate::{
    db::DefDatabase,
    expr::{Expr, ExprId, Pat, PatId},
    item_scope::BuiltinShadowMode,
    item_scope::ItemScope,
    nameres::CrateDefMap,
    path::{ModPath, Path},
    src::HasSource,
    AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
};

pub(crate) struct Expander {
    crate_def_map: Arc<CrateDefMap>,
    current_file_id: HirFileId,
    hygiene: Hygiene,
    ast_id_map: Arc<AstIdMap>,
    module: ModuleId,
    recursive_limit: usize,
}

impl Expander {
    pub(crate) fn new(
        db: &dyn DefDatabase,
        current_file_id: HirFileId,
        module: ModuleId,
    ) -> Expander {
        let crate_def_map = db.crate_def_map(module.krate);
        let hygiene = Hygiene::new(db.upcast(), current_file_id);
        let ast_id_map = db.ast_id_map(current_file_id);
        Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module, recursive_limit: 0 }
    }

    pub(crate) fn enter_expand<T: ast::AstNode>(
        &mut self,
        db: &dyn DefDatabase,
        local_scope: Option<&ItemScope>,
        macro_call: ast::MacroCall,
    ) -> Option<(Mark, T)> {
        if self.recursive_limit > 1024 {
            return None;
        }

        let macro_call = InFile::new(self.current_file_id, &macro_call);

        if let Some(call_id) = macro_call.as_call_id(db, |path| {
            if let Some(local_scope) = local_scope {
                if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
                    return Some(def);
                }
            }
            self.resolve_path_as_macro(db, &path)
        }) {
            let file_id = call_id.as_file();
            if let Some(node) = db.parse_or_expand(file_id) {
                if let Some(expr) = T::cast(node) {
                    log::debug!("macro expansion {:#?}", expr.syntax());

                    let mark = Mark {
                        file_id: self.current_file_id,
                        ast_id_map: mem::take(&mut self.ast_id_map),
                        bomb: DropBomb::new("expansion mark dropped"),
                    };
                    self.hygiene = Hygiene::new(db.upcast(), file_id);
                    self.current_file_id = file_id;
                    self.ast_id_map = db.ast_id_map(file_id);
                    self.recursive_limit += 1;

                    return Some((mark, expr));
                }
            }
        }

        // FIXME: Instead of just dropping the error from expansion
        // report it
        None
    }

    pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
        self.hygiene = Hygiene::new(db.upcast(), mark.file_id);
        self.current_file_id = mark.file_id;
        self.ast_id_map = mem::take(&mut mark.ast_id_map);
        self.recursive_limit -= 1;
        mark.bomb.defuse();
    }

    pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
        InFile { file_id: self.current_file_id, value }
    }

    fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
        Path::from_src(path, &self.hygiene)
    }

    fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> {
        self.crate_def_map
            .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other)
            .0
            .take_macros()
    }

    fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> {
        let file_local_id = self.ast_id_map.ast_id(item);
        AstId::new(self.current_file_id, file_local_id)
    }
}

pub(crate) struct Mark {
    file_id: HirFileId,
    ast_id_map: Arc<AstIdMap>,
    bomb: DropBomb,
}

/// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)]
pub struct Body {
    pub exprs: Arena<Expr>,
    pub pats: Arena<Pat>,
    /// The patterns for the function's parameters. While the parameter types are
    /// part of the function signature, the patterns are not (they don't change
    /// the external type of the function).
    ///
    /// If this `Body` is for the body of a constant, this will just be
    /// empty.
    pub params: Vec<PatId>,
    /// The `ExprId` of the actual body expression.
    pub body_expr: ExprId,
    pub item_scope: ItemScope,
}

pub type ExprPtr = Either<AstPtr<ast::Expr>, AstPtr<ast::RecordField>>;
pub type ExprSource = InFile<ExprPtr>;

pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
pub type PatSource = InFile<PatPtr>;

/// An item body together with the mapping from syntax nodes to HIR expression
/// IDs. This is needed to go from e.g. a position in a file to the HIR
/// expression containing it; but for type inference etc., we want to operate on
/// a structure that is agnostic to the actual positions of expressions in the
/// file, so that we don't recompute types whenever some whitespace is typed.
///
/// One complication here is that, due to macro expansion, a single `Body` might
/// be spread across several files. So, for each ExprId and PatId, we record
/// both the HirFileId and the position inside the file. However, we only store
/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
/// this properly for macros.
#[derive(Default, Debug, Eq, PartialEq)]
pub struct BodySourceMap {
    expr_map: FxHashMap<ExprSource, ExprId>,
    expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
    pat_map: FxHashMap<PatSource, PatId>,
    pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
    field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>,
    expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
}

#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
pub struct SyntheticSyntax;

impl Body {
    pub(crate) fn body_with_source_map_query(
        db: &dyn DefDatabase,
        def: DefWithBodyId,
    ) -> (Arc<Body>, Arc<BodySourceMap>) {
        let _p = profile("body_with_source_map_query");
        let mut params = None;

        let (file_id, module, body) = match def {
            DefWithBodyId::FunctionId(f) => {
                let f = f.lookup(db);
                let src = f.source(db);
                params = src.value.param_list();
                (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
            }
            DefWithBodyId::ConstId(c) => {
                let c = c.lookup(db);
                let src = c.source(db);
                (src.file_id, c.module(db), src.value.body())
            }
            DefWithBodyId::StaticId(s) => {
                let s = s.lookup(db);
                let src = s.source(db);
                (src.file_id, s.module(db), src.value.body())
            }
        };
        let expander = Expander::new(db, file_id, module);
        let (body, source_map) = Body::new(db, def, expander, params, body);
        (Arc::new(body), Arc::new(source_map))
    }

    pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
        db.body_with_source_map(def).0
    }

    fn new(
        db: &dyn DefDatabase,
        def: DefWithBodyId,
        expander: Expander,
        params: Option<ast::ParamList>,
        body: Option<ast::Expr>,
    ) -> (Body, BodySourceMap) {
        lower::lower(db, def, expander, params, body)
    }
}

impl Index<ExprId> for Body {
    type Output = Expr;

    fn index(&self, expr: ExprId) -> &Expr {
        &self.exprs[expr]
    }
}

impl Index<PatId> for Body {
    type Output = Pat;

    fn index(&self, pat: PatId) -> &Pat {
        &self.pats[pat]
    }
}

impl BodySourceMap {
    pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
        self.expr_map_back[expr]
    }

    pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
        let src = node.map(|it| Either::Left(AstPtr::new(it)));
        self.expr_map.get(&src).cloned()
    }

    pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> {
        let src = node.map(|it| AstPtr::new(it));
        self.expansions.get(&src).cloned()
    }

    pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> {
        let src = node.map(|it| Either::Right(AstPtr::new(it)));
        self.expr_map.get(&src).cloned()
    }

    pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
        self.pat_map_back[pat]
    }

    pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
        let src = node.map(|it| Either::Left(AstPtr::new(it)));
        self.pat_map.get(&src).cloned()
    }

    pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::RecordField> {
        self.field_map[&(expr, field)]
    }
}
<|endoftext|>


<commit_before>//! Traits and types for navigating order sequences of update tuples.
//!
//! The `Cursor` trait contains several methods for efficiently navigating ordered collections
//! of tuples of the form `(key, val, time, diff)`. The cursor is different from an iterator
//! both because it allows navigation on multiple levels (key and val), but also because it
//! supports efficient seeking (via the `seek_key` and `seek_val` methods).

// pub mod viewers;
pub mod cursor_list;
// pub mod cursor_pair;

/// A cursor for navigating ordered `(key, val, time, diff)` updates.
pub trait Cursor<K, V, T, R> {

	/// Type the cursor addresses data in.
	type Storage;

	/// Indicates if the current key is valid.
	///
	/// A value of `false` indicates that the cursor has exhausted all keys.
	fn key_valid(&self, storage: &Self::Storage) -> bool;
	/// Indicates if the current value is valid.
	///
	/// A value of `false` indicates that the cursor has exhausted all values for this key.
	fn val_valid(&self, storage: &Self::Storage) -> bool;

	/// A reference to the current key. Asserts if invalid.
	fn key<'a>(&self, storage: &'a Self::Storage) -> &'a K;
	/// A reference to the current value. Asserts if invalid.
	fn val<'a>(&self, storage: &'a Self::Storage) -> &'a V;
	/// Applies `logic` to each pair of time and difference. Intended for mutation of the
	/// closure's scope.
	fn map_times<L: FnMut(&T, R)>(&mut self, storage: &Self::Storage, logic: L);

	/// Advances the cursor to the next key. Indicates if the key is valid.
	fn step_key(&mut self, storage: &Self::Storage);
	/// Advances the cursor to the specified key. Indicates if the key is valid.
	fn seek_key(&mut self, storage: &Self::Storage, key: &K);

	/// Advances the cursor to the next value. Indicates if the value is valid.
	fn step_val(&mut self, storage: &Self::Storage);
	/// Advances the cursor to the specified value. Indicates if the value is valid.
	fn seek_val(&mut self, storage: &Self::Storage, val: &V);

	/// Rewinds the cursor to the first key.
	fn rewind_keys(&mut self, storage: &Self::Storage);
	/// Rewinds the cursor to the first value for current key.
	fn rewind_vals(&mut self, storage: &Self::Storage);
}

/// Debugging and testing utilities for Cursor.
pub trait CursorDebug<K: Clone, V: Clone, T: Clone, R: Clone> : Cursor<K, V, T, R> {
	/// Rewinds the cursor and outputs its contents to a Vec
	fn to_vec(&mut self, storage: &Self::Storage) -> Vec<((K, V), Vec<(T, R)>)> {
		let mut out = Vec::new();
		self.rewind_keys(storage);
		self.rewind_vals(storage);
		while self.key_valid(storage) {
			while self.val_valid(storage) {
				let mut kv_out = Vec::new();
				self.map_times(storage, |ts, r| {
					kv_out.push((ts.clone(), r));
				});
				out.push(((self.key(storage).clone(), self.val(storage).clone()), kv_out));
				self.step_val(storage);
			}
			self.step_key(storage);
		}
		out
	}
}

impl<C, K: Clone, V: Clone, T: Clone, R: Clone> CursorDebug<K, V, T, R> for C where C: Cursor<K, V, T, R> { }
<commit_msg>add get_key and get_val methods<commit_after>//! Traits and types for navigating order sequences of update tuples.
//!
//! The `Cursor` trait contains several methods for efficiently navigating ordered collections
//! of tuples of the form `(key, val, time, diff)`. The cursor is different from an iterator
//! both because it allows navigation on multiple levels (key and val), but also because it
//! supports efficient seeking (via the `seek_key` and `seek_val` methods).

// pub mod viewers;
pub mod cursor_list;
// pub mod cursor_pair;

/// A cursor for navigating ordered `(key, val, time, diff)` updates.
pub trait Cursor<K, V, T, R> {

	/// Type the cursor addresses data in.
	type Storage;

	/// Indicates if the current key is valid.
	///
	/// A value of `false` indicates that the cursor has exhausted all keys.
	fn key_valid(&self, storage: &Self::Storage) -> bool;
	/// Indicates if the current value is valid.
	///
	/// A value of `false` indicates that the cursor has exhausted all values for this key.
	fn val_valid(&self, storage: &Self::Storage) -> bool;

	/// A reference to the current key. Asserts if invalid.
	fn key<'a>(&self, storage: &'a Self::Storage) -> &'a K;
	/// A reference to the current value. Asserts if invalid.
	fn val<'a>(&self, storage: &'a Self::Storage) -> &'a V;

	/// Returns a reference to the current key, if valid.
	fn get_key<'a>(&self, storage: &'a Self::Storage) -> Option<&'a K> {
		if self.key_valid(storage) { Some(self.key(storage)) } else { None }
	}
	/// Returns a reference to the current value, if valid.
	fn get_val<'a>(&self, storage: &'a Self::Storage) -> Option<&'a V> {
		if self.val_valid(storage) { Some(self.val(storage)) } else { None }
	}

	/// Applies `logic` to each pair of time and difference. Intended for mutation of the
	/// closure's scope.
	fn map_times<L: FnMut(&T, R)>(&mut self, storage: &Self::Storage, logic: L);

	/// Advances the cursor to the next key. Indicates if the key is valid.
	fn step_key(&mut self, storage: &Self::Storage);
	/// Advances the cursor to the specified key. Indicates if the key is valid.
	fn seek_key(&mut self, storage: &Self::Storage, key: &K);

	/// Advances the cursor to the next value. Indicates if the value is valid.
	fn step_val(&mut self, storage: &Self::Storage);
	/// Advances the cursor to the specified value. Indicates if the value is valid.
	fn seek_val(&mut self, storage: &Self::Storage, val: &V);

	/// Rewinds the cursor to the first key.
	fn rewind_keys(&mut self, storage: &Self::Storage);
	/// Rewinds the cursor to the first value for current key.
	fn rewind_vals(&mut self, storage: &Self::Storage);
}

/// Debugging and testing utilities for Cursor.
pub trait CursorDebug<K: Clone, V: Clone, T: Clone, R: Clone> : Cursor<K, V, T, R> {
	/// Rewinds the cursor and outputs its contents to a Vec
	fn to_vec(&mut self, storage: &Self::Storage) -> Vec<((K, V), Vec<(T, R)>)> {
		let mut out = Vec::new();
		self.rewind_keys(storage);
		self.rewind_vals(storage);
		while self.key_valid(storage) {
			while self.val_valid(storage) {
				let mut kv_out = Vec::new();
				self.map_times(storage, |ts, r| {
					kv_out.push((ts.clone(), r));
				});
				out.push(((self.key(storage).clone(), self.val(storage).clone()), kv_out));
				self.step_val(storage);
			}
			self.step_key(storage);
		}
		out
	}
}

impl<C, K: Clone, V: Clone, T: Clone, R: Clone> CursorDebug<K, V, T, R> for C where C: Cursor<K, V, T, R> { }
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Dynamically typed value.

use capability::FromClientHook;
use private::capability::{ClientHook, PipelineHook, PipelineOp};
use private::layout::{PointerReader, PointerBuilder};
use traits::{FromPointerReader, FromPointerBuilder, SetPointerBuilder};
use Result;

#[derive(Copy, Clone)]
pub struct Reader<'a> {
    reader : PointerReader<'a>
}

impl <'a> Reader<'a> {
    #[inline]
    pub fn new<'b>(reader : PointerReader<'b>) -> Reader<'b> {
        Reader { reader : reader }
    }

    #[inline]
    pub fn is_null(&self) -> bool {
        self.reader.is_null()
    }

    #[inline]
    pub fn get_as<T : FromPointerReader<'a>>(&self) -> Result<T> {
        FromPointerReader::get_from_pointer(&self.reader)
    }

    pub fn get_as_capability<T : FromClientHook>(&self) -> Result<T> {
        Ok(FromClientHook::new(try!(self.reader.get_capability())))
    }

    //# Used by RPC system to implement pipelining. Applications
    //# generally shouldn't use this directly.
    pub fn get_pipelined_cap(&self, ops : &[PipelineOp]) -> Result<Box<ClientHook+Send>> {
        let mut pointer = self.reader;

        for op in ops.iter() {
            match op {
                &PipelineOp::Noop =>  { }
                &PipelineOp::GetPointerField(idx) => {
                    pointer = try!(pointer.get_struct(::std::ptr::null())).get_pointer_field(idx as usize);
                }
            }
        }

        pointer.get_capability()
    }
}

impl <'a> FromPointerReader<'a> for Reader<'a> {
    fn get_from_pointer(reader: &PointerReader<'a>) -> Result<Reader<'a>> {
        Ok(Reader { reader: *reader })
    }
}

pub struct Builder<'a> {
    builder : PointerBuilder<'a>
}

impl <'a> Builder<'a> {
    #[inline]
    pub fn new<'b>(builder : PointerBuilder<'a>) -> Builder<'a> {
        Builder { builder : builder }
    }

    pub fn get_as<T : FromPointerBuilder<'a>>(self) -> Result<T> {
        FromPointerBuilder::get_from_pointer(self.builder)
    }

    pub fn init_as<T : FromPointerBuilder<'a>>(self) -> T {
        FromPointerBuilder::init_pointer(self.builder, 0)
    }

    pub fn init_as_sized<T : FromPointerBuilder<'a>>(self, size : u32) -> T {
        FromPointerBuilder::init_pointer(self.builder, size)
    }

    pub fn set_as<To, From : SetPointerBuilder<To>>(self, value : From) -> Result<()> {
        SetPointerBuilder::<To>::set_pointer_builder(self.builder, value)
    }

    // XXX value should be a user client.
    pub fn set_as_capability(&self, value : Box<ClientHook+Send>) {
        self.builder.set_capability(value);
    }

    #[inline]
    pub fn clear(&mut self) {
        self.builder.clear()
    }

    #[inline]
    pub fn as_reader(self) -> Reader<'a> {
        Reader { reader : self.builder.as_reader() }
    }
}

impl <'a> FromPointerBuilder<'a> for Builder<'a> {
    fn init_pointer(builder: PointerBuilder<'a>, _len: u32) -> Builder<'a> {
        Builder { builder: builder }
    }
    fn get_from_pointer(builder: PointerBuilder<'a>) -> Result<Builder<'a>> {
        Ok(Builder { builder: builder })
    }
}

pub struct Pipeline {
    hook : Box<PipelineHook+Send>,
    ops : Vec<PipelineOp>,
}

impl Pipeline {
    pub fn new(hook : Box<PipelineHook+Send>) -> Pipeline {
        Pipeline { hook : hook, ops : Vec::new() }
    }

    pub fn noop(&self) -> Pipeline {
        Pipeline { hook : self.hook.copy(), ops : self.ops.clone() }
    }

    pub fn get_pointer_field(&self, pointer_index : u16) -> Pipeline {
        let mut new_ops = Vec::with_capacity(self.ops.len() + 1);
        for &op in self.ops.iter() {
            new_ops.push(op)
        }
        new_ops.push(PipelineOp::GetPointerField(pointer_index));
        Pipeline { hook : self.hook.copy(), ops : new_ops }
    }

    pub fn as_cap(&self) -> Box<ClientHook+Send> {
        self.hook.get_pipelined_cap(self.ops.clone())
    }
}

<commit_msg>formatting<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Dynamically typed value.

use capability::FromClientHook;
use private::capability::{ClientHook, PipelineHook, PipelineOp};
use private::layout::{PointerReader, PointerBuilder};
use traits::{FromPointerReader, FromPointerBuilder, SetPointerBuilder};
use Result;

#[derive(Copy, Clone)]
pub struct Reader<'a> {
    reader : PointerReader<'a>
}

impl <'a> Reader<'a> {
    #[inline]
    pub fn new<'b>(reader : PointerReader<'b>) -> Reader<'b> {
        Reader { reader : reader }
    }

    #[inline]
    pub fn is_null(&self) -> bool {
        self.reader.is_null()
    }

    #[inline]
    pub fn get_as<T : FromPointerReader<'a>>(&self) -> Result<T> {
        FromPointerReader::get_from_pointer(&self.reader)
    }

    pub fn get_as_capability<T : FromClientHook>(&self) -> Result<T> {
        Ok(FromClientHook::new(try!(self.reader.get_capability())))
    }

    //# Used by RPC system to implement pipelining. Applications
    //# generally shouldn't use this directly.
    pub fn get_pipelined_cap(&self, ops : &[PipelineOp]) -> Result<Box<ClientHook+Send>> {
        let mut pointer = self.reader;

        for op in ops.iter() {
            match op {
                &PipelineOp::Noop =>  { }
                &PipelineOp::GetPointerField(idx) => {
                    pointer = try!(pointer.get_struct(::std::ptr::null())).get_pointer_field(idx as usize);
                }
            }
        }

        pointer.get_capability()
    }
}

impl <'a> FromPointerReader<'a> for Reader<'a> {
    fn get_from_pointer(reader: &PointerReader<'a>) -> Result<Reader<'a>> {
        Ok(Reader { reader: *reader })
    }
}

pub struct Builder<'a> {
    builder : PointerBuilder<'a>
}

impl <'a> Builder<'a> {
    #[inline]
    pub fn new<'b>(builder : PointerBuilder<'a>) -> Builder<'a> {
        Builder { builder : builder }
    }

    pub fn get_as<T : FromPointerBuilder<'a>>(self) -> Result<T> {
        FromPointerBuilder::get_from_pointer(self.builder)
    }

    pub fn init_as<T : FromPointerBuilder<'a>>(self) -> T {
        FromPointerBuilder::init_pointer(self.builder, 0)
    }

    pub fn init_as_sized<T : FromPointerBuilder<'a>>(self, size : u32) -> T {
        FromPointerBuilder::init_pointer(self.builder, size)
    }

    pub fn set_as<To, From : SetPointerBuilder<To>>(self, value : From) -> Result<()> {
        SetPointerBuilder::<To>::set_pointer_builder(self.builder, value)
    }

    // XXX value should be a user client.
    pub fn set_as_capability(&self, value : Box<ClientHook+Send>) {
        self.builder.set_capability(value);
    }

    #[inline]
    pub fn clear(&mut self) {
        self.builder.clear()
    }

    #[inline]
    pub fn as_reader(self) -> Reader<'a> {
        Reader { reader : self.builder.as_reader() }
    }
}

impl <'a> FromPointerBuilder<'a> for Builder<'a> {
    fn init_pointer(builder: PointerBuilder<'a>, _len: u32) -> Builder<'a> {
        Builder { builder: builder }
    }
    fn get_from_pointer(builder: PointerBuilder<'a>) -> Result<Builder<'a>> {
        Ok(Builder { builder: builder })
    }
}

pub struct Pipeline {
    hook : Box<PipelineHook+Send>,
    ops : Vec<PipelineOp>,
}

impl Pipeline {
    pub fn new(hook : Box<PipelineHook+Send>) -> Pipeline {
        Pipeline { hook : hook, ops : Vec::new() }
    }

    pub fn noop(&self) -> Pipeline {
        Pipeline { hook : self.hook.copy(), ops : self.ops.clone() }
    }

    pub fn get_pointer_field(&self, pointer_index : u16) -> Pipeline {
        let mut new_ops = Vec::with_capacity(self.ops.len() + 1);
        for &op in self.ops.iter() {
            new_ops.push(op)
        }
        new_ops.push(PipelineOp::GetPointerField(pointer_index));
        Pipeline { hook : self.hook.copy(), ops : new_ops }
    }

    pub fn as_cap(&self) -> Box<ClientHook+Send> {
        self.hook.get_pipelined_cap(self.ops.clone())
    }
}

<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! How to retreive data from an image within a shader.
//!
//! This module contains a struct named `Sampler` which describes how to get pixel data from
//! a texture.
//!
use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;

use device::Device;
use Error;
use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// Describes how to retreive data from an image within a shader.
pub struct Sampler {
    sampler: vk::Sampler,
    device: Arc<Device>,
}

// TODO: what's the story with VK_KHR_mirror_clamp_to_edge? Is it an extension or is it core?

impl Sampler {
    /// Creates a new `Sampler` with the given behavior.
    ///
    /// # Panic
    ///
    /// - Panicks if `max_anisotropy < 1.0`.
    /// - Panicks if `min_lod > max_lod`.
    ///
    // TODO: wrong error type returned
    pub fn new(device: &Arc<Device>, mag_filter: Filter, min_filter: Filter,
               mipmap_mode: MipmapMode, address_u: SamplerAddressMode,
               address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32,
               max_anisotropy: f32, min_lod: f32, max_lod: f32)
               -> Result<Arc<Sampler>, SamplerCreationError>
    {
        assert!(max_anisotropy >= 1.0);
        // TODO: check limits
        assert!(min_lod <= max_lod);

        let vk = device.pointers();

        let sampler = unsafe {
            let infos = vk::SamplerCreateInfo {
                sType: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                magFilter: mag_filter as u32,
                minFilter: min_filter as u32,
                mipmapMode: mipmap_mode as u32,
                addressModeU: address_u as u32,
                addressModeV: address_v as u32,
                addressModeW: address_w as u32,
                mipLodBias: mip_lod_bias,
                anisotropyEnable: if max_anisotropy > 1.0 { vk::TRUE } else { vk::FALSE },
                maxAnisotropy: max_anisotropy,
                compareEnable: 0,       // FIXME:
                compareOp: 0,       // FIXME:
                minLod: min_lod,
                maxLod: max_lod,
                borderColor: 0,     // FIXME:
                unnormalizedCoordinates: vk::FALSE,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSampler(device.internal_object(), &infos,
                                               ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Sampler {
            sampler: sampler,
            device: device.clone(),
        }))
    }

    /// Creates a sampler with unnormalized coordinates. This means that texture coordinates won't
    /// range between `0.0` and `1.0` but use plain pixel offsets.
    ///
    /// Using an unnormalized sampler adds a few restrictions:
    ///
    /// - It can only be used with non-array 1D or 2D images.
    /// - It can only be used with images with a single mipmap.
    /// - Projection and offsets can't be used by shaders. Only the first mipmap can be accessed.
    ///
    // TODO: wrong error type returned
    pub fn unnormalized(device: &Arc<Device>, filter: Filter,
                        address_u: UnnormalizedSamplerAddressMode,
                        address_v: UnnormalizedSamplerAddressMode)
                        -> Result<Arc<Sampler>, SamplerCreationError>
    {
        let vk = device.pointers();

        let sampler = unsafe {
            let infos = vk::SamplerCreateInfo {
                sType: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                magFilter: filter as u32,
                minFilter: filter as u32,
                mipmapMode: vk::SAMPLER_MIPMAP_MODE_NEAREST,
                addressModeU: address_u as u32,
                addressModeV: address_v as u32,
                addressModeW: vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,       // unused by the impl
                mipLodBias: 0.0,
                anisotropyEnable: vk::FALSE,
                maxAnisotropy: 0.0,
                compareEnable: vk::FALSE,
                compareOp: vk::COMPARE_OP_NEVER,
                minLod: 0.0,
                maxLod: 0.0,
                borderColor: 0,     // FIXME:
                unnormalizedCoordinates: vk::TRUE,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSampler(device.internal_object(), &infos,
                                               ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Sampler {
            sampler: sampler,
            device: device.clone(),
        }))
    }
}

unsafe impl VulkanObject for Sampler {
    type Object = vk::Sampler;

    #[inline]
    fn internal_object(&self) -> vk::Sampler {
        self.sampler
    }
}

impl Drop for Sampler {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroySampler(self.device.internal_object(), self.sampler, ptr::null());
        }
    }
}

/// Describes how the color of each pixel should be determined.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum Filter {
    /// The four pixels whose center surround the requested coordinates are taken, then their
    /// values are interpolated.
    Linear = vk::FILTER_LINEAR,

    /// The pixel whose center is nearest to the requested coordinates is taken from the source
    /// and its value is returned as-is.
    Nearest = vk::FILTER_NEAREST,
}

/// Describes which mipmap from the source to use.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum MipmapMode {
    /// Use the mipmap whose dimensions are the nearest to the dimensions of the destination.
    Nearest = vk::SAMPLER_MIPMAP_MODE_NEAREST,

    /// Take the two mipmaps whose dimensions are immediately inferior and superior to the
    /// dimensions of the destination, calculate the value for both, and interpolate them.
    Linear = vk::SAMPLER_MIPMAP_MODE_LINEAR,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum SamplerAddressMode {
    Repeat = vk::SAMPLER_ADDRESS_MODE_REPEAT,
    MirroredRepeat = vk::SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
    ClampToEdge = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    ClampToBorder = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
    MirrorClampToEdge = vk::SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum UnnormalizedSamplerAddressMode {
    ClampToEdge = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    ClampToBorder = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
}

/// Error that can happen when creating an instance.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SamplerCreationError {
    /// Not enough memory.
    OomError(OomError),
    /// Too many sampler objects have been created. You must destroy some before creating new ones.
    /// Note the specs guarantee that at least 4000 samplers can exist simultaneously.
    TooManyObjects,
}

impl error::Error for SamplerCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            SamplerCreationError::OomError(_) => "not enough memory available",
            SamplerCreationError::TooManyObjects => "too many simultaneous sampler objects",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SamplerCreationError::OomError(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for SamplerCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<OomError> for SamplerCreationError {
    #[inline]
    fn from(err: OomError) -> SamplerCreationError {
        SamplerCreationError::OomError(err)
    }
}

impl From<Error> for SamplerCreationError {
    #[inline]
    fn from(err: Error) -> SamplerCreationError {
        match err {
            err @ Error::OutOfHostMemory => SamplerCreationError::OomError(OomError::from(err)),
            err @ Error::OutOfDeviceMemory => SamplerCreationError::OomError(OomError::from(err)),
            Error::TooManyObjects => SamplerCreationError::TooManyObjects,
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

#[cfg(test)]
mod tests {
    use sampler;

    #[test]
    fn create() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 1.0,
                                      0.0, 2.0).unwrap();
    }

    #[test]
    #[should_panic]
    fn min_lod_inferior() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 1.0, 5.0, 2.0);
    }

    #[test]
    #[should_panic]
    fn max_anisotropy() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 0.5, 0.0, 2.0);
    }
}
<commit_msg>Check the limits when creating a sampler<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! How to retreive data from an image within a shader.
//!
//! This module contains a struct named `Sampler` which describes how to get pixel data from
//! a texture.
//!
use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;

use device::Device;
use Error;
use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// Describes how to retreive data from an image within a shader.
pub struct Sampler {
    sampler: vk::Sampler,
    device: Arc<Device>,
}

// TODO: what's the story with VK_KHR_mirror_clamp_to_edge? Is it an extension or is it core?

impl Sampler {
    /// Creates a new `Sampler` with the given behavior.
    ///
    /// # Panic
    ///
    /// - Panicks if `max_anisotropy < 1.0`.
    /// - Panicks if `min_lod > max_lod`.
    ///
    // TODO: wrong error type returned
    pub fn new(device: &Arc<Device>, mag_filter: Filter, min_filter: Filter,
               mipmap_mode: MipmapMode, address_u: SamplerAddressMode,
               address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32,
               max_anisotropy: f32, min_lod: f32, max_lod: f32)
               -> Result<Arc<Sampler>, SamplerCreationError>
    {
        assert!(max_anisotropy >= 1.0);
        assert!(min_lod <= max_lod);

        if max_anisotropy > 1.0 {
            if !device.enabled_features().sampler_anisotropy {
                return Err(SamplerCreationError::SamplerAnisotropyFeatureNotEnabled);
            }

            let limit = device.physical_device().limits().max_sampler_anisotropy();
            if max_anisotropy > limit {
                return Err(SamplerCreationError::AnisotropyLimitExceeded {
                    requested: max_anisotropy,
                    maximum: limit,
                });
            }
        }

        {
            let limit = device.physical_device().limits().max_sampler_lod_bias();
            if mip_lod_bias > limit {
                return Err(SamplerCreationError::MipLodBiasLimitExceeded {
                    requested: mip_lod_bias,
                    maximum: limit,
                });
            }
        }

        let vk = device.pointers();

        let sampler = unsafe {
            let infos = vk::SamplerCreateInfo {
                sType: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                magFilter: mag_filter as u32,
                minFilter: min_filter as u32,
                mipmapMode: mipmap_mode as u32,
                addressModeU: address_u as u32,
                addressModeV: address_v as u32,
                addressModeW: address_w as u32,
                mipLodBias: mip_lod_bias,
                anisotropyEnable: if max_anisotropy > 1.0 { vk::TRUE } else { vk::FALSE },
                maxAnisotropy: max_anisotropy,
                compareEnable: 0,       // FIXME:
                compareOp: 0,       // FIXME:
                minLod: min_lod,
                maxLod: max_lod,
                borderColor: 0,     // FIXME:
                unnormalizedCoordinates: vk::FALSE,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSampler(device.internal_object(), &infos,
                                               ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Sampler {
            sampler: sampler,
            device: device.clone(),
        }))
    }

    /// Creates a sampler with unnormalized coordinates. This means that texture coordinates won't
    /// range between `0.0` and `1.0` but use plain pixel offsets.
    ///
    /// Using an unnormalized sampler adds a few restrictions:
    ///
    /// - It can only be used with non-array 1D or 2D images.
    /// - It can only be used with images with a single mipmap.
    /// - Projection and offsets can't be used by shaders. Only the first mipmap can be accessed.
    ///
    // TODO: wrong error type returned
    pub fn unnormalized(device: &Arc<Device>, filter: Filter,
                        address_u: UnnormalizedSamplerAddressMode,
                        address_v: UnnormalizedSamplerAddressMode)
                        -> Result<Arc<Sampler>, SamplerCreationError>
    {
        let vk = device.pointers();

        let sampler = unsafe {
            let infos = vk::SamplerCreateInfo {
                sType: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                magFilter: filter as u32,
                minFilter: filter as u32,
                mipmapMode: vk::SAMPLER_MIPMAP_MODE_NEAREST,
                addressModeU: address_u as u32,
                addressModeV: address_v as u32,
                addressModeW: vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,       // unused by the impl
                mipLodBias: 0.0,
                anisotropyEnable: vk::FALSE,
                maxAnisotropy: 0.0,
                compareEnable: vk::FALSE,
                compareOp: vk::COMPARE_OP_NEVER,
                minLod: 0.0,
                maxLod: 0.0,
                borderColor: 0,     // FIXME:
                unnormalizedCoordinates: vk::TRUE,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSampler(device.internal_object(), &infos,
                                               ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Sampler {
            sampler: sampler,
            device: device.clone(),
        }))
    }
}

unsafe impl VulkanObject for Sampler {
    type Object = vk::Sampler;

    #[inline]
    fn internal_object(&self) -> vk::Sampler {
        self.sampler
    }
}

impl Drop for Sampler {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroySampler(self.device.internal_object(), self.sampler, ptr::null());
        }
    }
}

/// Describes how the color of each pixel should be determined.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum Filter {
    /// The four pixels whose center surround the requested coordinates are taken, then their
    /// values are interpolated.
    Linear = vk::FILTER_LINEAR,

    /// The pixel whose center is nearest to the requested coordinates is taken from the source
    /// and its value is returned as-is.
    Nearest = vk::FILTER_NEAREST,
}

/// Describes which mipmap from the source to use.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum MipmapMode {
    /// Use the mipmap whose dimensions are the nearest to the dimensions of the destination.
    Nearest = vk::SAMPLER_MIPMAP_MODE_NEAREST,

    /// Take the two mipmaps whose dimensions are immediately inferior and superior to the
    /// dimensions of the destination, calculate the value for both, and interpolate them.
    Linear = vk::SAMPLER_MIPMAP_MODE_LINEAR,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum SamplerAddressMode {
    Repeat = vk::SAMPLER_ADDRESS_MODE_REPEAT,
    MirroredRepeat = vk::SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
    ClampToEdge = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    ClampToBorder = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
    MirrorClampToEdge = vk::SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum UnnormalizedSamplerAddressMode {
    ClampToEdge = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    ClampToBorder = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
}

/// Error that can happen when creating an instance.
#[derive(Clone, Debug, PartialEq)]
pub enum SamplerCreationError {
    /// Not enough memory.
    OomError(OomError),

    /// Too many sampler objects have been created. You must destroy some before creating new ones.
    /// Note the specs guarantee that at least 4000 samplers can exist simultaneously.
    TooManyObjects,

    /// Using an anisotropy superior to 1.0 requires enabling the `sampler_anisotropy` feature when
    /// creating the device.
    SamplerAnisotropyFeatureNotEnabled,

    /// The requested anisotropy level exceeds the device's limits.
    AnisotropyLimitExceeded { requested: f32, maximum: f32 },

    /// The requested mip lod bias exceeds the device's limits.
    MipLodBiasLimitExceeded { requested: f32, maximum: f32 },
}

impl error::Error for SamplerCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            SamplerCreationError::OomError(_) => "not enough memory available",
            SamplerCreationError::TooManyObjects => "too many simultaneous sampler objects",
            SamplerCreationError::SamplerAnisotropyFeatureNotEnabled => "the `sampler_anisotropy` \
                                                                         feature is not enabled",
            SamplerCreationError::AnisotropyLimitExceeded { .. } => "anisotropy limit exceeded",
            SamplerCreationError::MipLodBiasLimitExceeded { .. } => "mip lod bias limit exceeded",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SamplerCreationError::OomError(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for SamplerCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<OomError> for SamplerCreationError {
    #[inline]
    fn from(err: OomError) -> SamplerCreationError {
        SamplerCreationError::OomError(err)
    }
}

impl From<Error> for SamplerCreationError {
    #[inline]
    fn from(err: Error) -> SamplerCreationError {
        match err {
            err @ Error::OutOfHostMemory => SamplerCreationError::OomError(OomError::from(err)),
            err @ Error::OutOfDeviceMemory => SamplerCreationError::OomError(OomError::from(err)),
            Error::TooManyObjects => SamplerCreationError::TooManyObjects,
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

#[cfg(test)]
mod tests {
    use sampler;

    #[test]
    fn create_regular() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 1.0,
                                      0.0, 2.0).unwrap();
    }

    #[test]
    fn create_unnormalized() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::unnormalized(&device, sampler::Filter::Linear,
                                               sampler::UnnormalizedSamplerAddressMode::ClampToEdge,
                                               sampler::UnnormalizedSamplerAddressMode::ClampToEdge)
                                               .unwrap();
    }

    #[test]
    #[should_panic]
    fn min_lod_inferior() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 1.0, 5.0, 2.0);
    }

    #[test]
    #[should_panic]
    fn max_anisotropy() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 0.5, 0.0, 2.0);
    }

    #[test]
    fn anisotropy_feature() {
        let (device, queue) = gfx_dev_and_queue!();

        let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 2.0, 0.0, 2.0);

        match r {
            Err(sampler::SamplerCreationError::SamplerAnisotropyFeatureNotEnabled) => (),
            _ => panic!()
        }
    }

    #[test]
    fn anisotropy_limit() {
        let (device, queue) = gfx_dev_and_queue!(sampler_anisotropy);

        let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 100000000.0, 0.0,
                                      2.0);

        match r {
            Err(sampler::SamplerCreationError::AnisotropyLimitExceeded { .. }) => (),
            _ => panic!()
        }
    }

    #[test]
    fn mip_lod_bias_limit() {
        let (device, queue) = gfx_dev_and_queue!();

        let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 100000000.0, 1.0, 0.0,
                                      2.0);

        match r {
            Err(sampler::SamplerCreationError::MipLodBiasLimitExceeded { .. }) => (),
            _ => panic!()
        }
    }
}
<|endoftext|>


<commit_before>// AsRef and AsMut allow for cheap reference-to-reference conversions.
// Read more about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html
// and https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively.

// I AM NOT DONE
// Obtain the number of bytes (not characters) in the given argument
// Add the AsRef trait appropriately as a trait bound
fn byte_counter<T>(arg: T) -> usize {
    arg.as_ref().as_bytes().len()
}

// I AM NOT DONE
// Obtain the number of characters (not bytes) in the given argument
// Add the AsRef trait appropriately as a trait bound
fn char_counter<T>(arg: T) -> usize {
    arg.as_ref().chars().collect::<Vec<_>>().len()
}

fn main() {
    let s = "Café au lait";
    println!("{}", char_counter(s));
    println!("{}", byte_counter(s));
}

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

    #[test]
    fn different_counts() {
        let s = "Café au lait";
        assert_ne!(char_counter(s), byte_counter(s));
    }
    fn same_counts() {
        let s = "Cafe au lait";
        assert_eq!(char_counter(s), byte_counter(s));
    }
}<commit_msg>Enable a test and improve per clippy's suggestion.<commit_after>// AsRef and AsMut allow for cheap reference-to-reference conversions.
// Read more about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html
// and https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively.

// I AM NOT DONE
// Obtain the number of bytes (not characters) in the given argument
// Add the AsRef trait appropriately as a trait bound
fn byte_counter<T>(arg: T) -> usize {
    arg.as_ref().as_bytes().len()
}

// I AM NOT DONE
// Obtain the number of characters (not bytes) in the given argument
// Add the AsRef trait appropriately as a trait bound
fn char_counter<T>(arg: T) -> usize {
    arg.as_ref().chars().count()
}

fn main() {
    let s = "Café au lait";
    println!("{}", char_counter(s));
    println!("{}", byte_counter(s));
}

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

    #[test]
    fn different_counts() {
        let s = "Café au lait";
        assert_ne!(char_counter(s), byte_counter(s));
    }

    #[test]
    fn same_counts() {
        let s = "Cafe au lait";
        assert_eq!(char_counter(s), byte_counter(s));
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(herbie_lint)]

#![deny(herbie)]

fn get_f64() -> f64 {
    4.2
}

fn get_tup() -> (f64, f64) {
    (0., 0.)
}

fn integers() {
    1;
    (1/2 + 3) * 2;
}

fn main() {
    integers();

    let (a, b, c) = (0., 0., 0.);
    (a/a + a) * a;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (a/b + a) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (a/b + c) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/b + c) * a;

    (0./1. + 2.) * 1.;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (1./1. + 2.) * 1.;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (1./1. + 1.) * 1.;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (0./1. + a) * 1.;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (0./a + 2.) * a;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/b + get_f64()) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (a/b + (4.5f64).sqrt()) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (a/b + (42 as f64)) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (a/b + { 42. }) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/get_f64() + c) * get_f64();
    (a/{ 42. } + c) * { 42. };

    (4.5f64).abs();

    //(a*a + b*b).sqrt();
    //**^ ERROR
    //**| HELP Try this
    //**| SUGGESTION (hypot $0 $1)

    //(a*a + (-4. * -4.)).sqrt();
    //**^ ERROR
    //**| HELP Try this
    //**| SUGGESTION (hypot $0 $1)

    ((a-b) * (a-b)).sqrt();
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (- $0 $1)

    a.floor();

    (a/b + c.floor()) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    let d = (0., 0.);

    (a/b + d.0) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/b + get_tup().0) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/d.0 + c) * d.0;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/d.0 + c) * d.1;
    (a/get_tup().0 + c) * get_tup().0;

    struct Foo { a: f64, b: f64 };
    let e = Foo { a: 0., b: 0. };

    (a/b + e.a) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/e.a + c) * e.a;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/e.a + c) * e.b;
}
<commit_msg>Improve structs tests<commit_after>#![feature(plugin)]
#![plugin(herbie_lint)]

#![deny(herbie)]

struct Foo { a: f64, b: f64 }

fn get_f64() -> f64 {
    4.2
}

fn get_tup() -> (f64, f64) {
    (0., 0.)
}

fn get_struct() -> Foo {
    Foo { a: 0., b: 0. }
}

fn integers() {
    1;
    (1/2 + 3) * 2;
}

fn main() {
    integers();

    let (a, b, c) = (0., 0., 0.);
    (a/a + a) * a;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (a/b + a) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (a/b + c) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/b + c) * a;

    (0./1. + 2.) * 1.;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (1./1. + 2.) * 1.;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (1./1. + 1.) * 1.;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (0./1. + a) * 1.;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (0./a + 2.) * a;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/b + get_f64()) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (a/b + (4.5f64).sqrt()) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (a/b + (42 as f64)) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)
    (a/b + { 42. }) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/get_f64() + c) * get_f64();
    (a/{ 42. } + c) * { 42. };

    (4.5f64).abs();

    //(a*a + b*b).sqrt();
    //**^ ERROR
    //**| HELP Try this
    //**| SUGGESTION (hypot $0 $1)

    //(a*a + (-4. * -4.)).sqrt();
    //**^ ERROR
    //**| HELP Try this
    //**| SUGGESTION (hypot $0 $1)

    ((a-b) * (a-b)).sqrt();
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (- $0 $1)

    a.floor();

    (a/b + c.floor()) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    let d = (0., 0.);

    (a/b + d.0) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/b + get_tup().0) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/d.0 + c) * d.0;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/d.0 + c) * d.1;
    (a/get_tup().0 + c) * get_tup().0;

    let e = get_struct();

    (a/b + e.a) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/b + get_struct().a) * b;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/e.a + c) * e.a;
    //~^ ERROR
    //~| HELP Try this
    //~| SUGGESTION (+ (* $2 $1) $0)

    (a/get_struct().a + c) * get_struct().a;
    (a/e.a + c) * e.b;
}
<|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/.

use dbus_tree::{Access, EmitsChangedSignal, Factory, MTSync, Method, Property};

use crate::{
    dbus_api::{
        consts,
        filesystem::filesystem_3_0::{
            methods::rename_filesystem,
            props::{get_filesystem_created, get_filesystem_devnode, get_filesystem_name},
        },
        types::TData,
        util::{get_parent, get_uuid},
    },
    engine::Engine,
};

pub fn rename_method<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Method<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.method("SetName", (), rename_filesystem)
        .in_arg(("name", "s"))
        // b: true if UUID of changed resource has been returned
        // s: UUID of changed resource
        //
        // Rust representation: (bool, String)
        .out_arg(("result", "(bs)"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"))
}

pub fn devnode_property<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.property::<&str, _>(consts::FILESYSTEM_DEVNODE_PROP, ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::Invalidates)
        .on_get(get_filesystem_devnode)
}

pub fn name_property<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.property::<&str, _>(consts::FILESYSTEM_NAME_PROP, ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::True)
        .on_get(get_filesystem_name)
}

pub fn pool_property<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.property::<&dbus::Path, _>(consts::FILESYSTEM_POOL_PROP, ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::Const)
        .on_get(get_parent)
}

pub fn uuid_property<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.property::<&str, _>(consts::FILESYSTEM_UUID_PROP, ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::Const)
        .on_get(get_uuid)
}

pub fn created_property<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.property::<&str, _>("Created", ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::Const)
        .on_get(get_filesystem_created)
}
<commit_msg>Use defined constant instead of literal<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/.

use dbus_tree::{Access, EmitsChangedSignal, Factory, MTSync, Method, Property};

use crate::{
    dbus_api::{
        consts,
        filesystem::filesystem_3_0::{
            methods::rename_filesystem,
            props::{get_filesystem_created, get_filesystem_devnode, get_filesystem_name},
        },
        types::TData,
        util::{get_parent, get_uuid},
    },
    engine::Engine,
};

pub fn rename_method<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Method<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.method("SetName", (), rename_filesystem)
        .in_arg(("name", "s"))
        // b: true if UUID of changed resource has been returned
        // s: UUID of changed resource
        //
        // Rust representation: (bool, String)
        .out_arg(("result", "(bs)"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"))
}

pub fn devnode_property<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.property::<&str, _>(consts::FILESYSTEM_DEVNODE_PROP, ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::Invalidates)
        .on_get(get_filesystem_devnode)
}

pub fn name_property<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.property::<&str, _>(consts::FILESYSTEM_NAME_PROP, ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::True)
        .on_get(get_filesystem_name)
}

pub fn pool_property<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.property::<&dbus::Path, _>(consts::FILESYSTEM_POOL_PROP, ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::Const)
        .on_get(get_parent)
}

pub fn uuid_property<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.property::<&str, _>(consts::FILESYSTEM_UUID_PROP, ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::Const)
        .on_get(get_uuid)
}

pub fn created_property<E>(
    f: &Factory<MTSync<TData<E>>, TData<E>>,
) -> Property<MTSync<TData<E>>, TData<E>>
where
    E: 'static + Engine,
{
    f.property::<&str, _>(consts::FILESYSTEM_CREATED_PROP, ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::Const)
        .on_get(get_filesystem_created)
}
<|endoftext|>


<commit_before>use crate::utils::{
    has_drop, in_macro_or_desugar, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_hir,
    span_lint_hir_and_then, walk_ptrs_ty_depth,
};
use if_chain::if_chain;
use matches::matches;
use rustc::hir::intravisit::FnKind;
use rustc::hir::{def_id, Body, FnDecl, HirId};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::mir::{
    self, traversal,
    visit::{MutatingUseContext, PlaceContext, Visitor},
    TerminatorKind,
};
use rustc::ty::{self, Ty};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use std::convert::TryFrom;
use syntax::source_map::{BytePos, Span};

macro_rules! unwrap_or_continue {
    ($x:expr) => {
        match $x {
            Some(x) => x,
            None => continue,
        }
    };
}

declare_clippy_lint! {
    /// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
    /// value that is going to be dropped without further use.
    ///
    /// **Why is this bad?** It is not always possible for the compiler to eliminate useless
    /// allocations and deallocations generated by redundant `clone()`s.
    ///
    /// **Known problems:**
    ///
    /// * Suggestions made by this lint could require NLL to be enabled.
    /// * False-positive if there is a borrow preventing the value from moving out.
    ///
    /// ```rust
    /// let x = String::new();
    ///
    /// let y = &x;
    ///
    /// foo(x.clone()); // This lint suggests to remove this `clone()`
    /// ```
    ///
    /// **Example:**
    /// ```rust
    /// {
    ///     let x = Foo::new();
    ///     call(x.clone());
    ///     call(x.clone()); // this can just pass `x`
    /// }
    ///
    /// ["lorem", "ipsum"].join(" ").to_string()
    ///
    /// Path::new("/a/b").join("c").to_path_buf()
    /// ```
    pub REDUNDANT_CLONE,
    nursery,
    "`clone()` of an owned value that is going to be dropped immediately"
}

declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
    #[allow(clippy::too_many_lines)]
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: FnKind<'tcx>,
        _: &'tcx FnDecl,
        body: &'tcx Body,
        _: Span,
        _: HirId,
    ) {
        let def_id = cx.tcx.hir().body_owner_def_id(body.id());
        let mir = cx.tcx.optimized_mir(def_id);

        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
            let terminator = bbdata.terminator();

            if in_macro_or_desugar(terminator.source_info.span) {
                continue;
            }

            // Give up on loops
            if terminator.successors().any(|s| *s == bb) {
                continue;
            }

            let (fn_def_id, arg, arg_ty, _) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));

            let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
                || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
                || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD) && match_type(cx, arg_ty, &paths::STRING));

            let from_deref = !from_borrow
                && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
                    || match_def_path(cx, fn_def_id, &paths::OS_STR_TO_OS_STRING));

            if !from_borrow && !from_deref {
                continue;
            }

            // _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
            // In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
            // block.
            let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(
                cx,
                mir,
                arg,
                from_borrow,
                bbdata.statements.iter()
            ));

            if from_borrow && cannot_move_out {
                continue;
            }

            // _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
            let referent = if from_deref {
                let ps = mir.predecessors_for(bb);
                if ps.len() != 1 {
                    continue;
                }
                let pred_terminator = mir[ps[0]].terminator();

                let pred_arg = if_chain! {
                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) =
                        is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
                    if res.base == mir::PlaceBase::Local(cloned);
                    if match_def_path(cx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
                    if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
                        || match_type(cx, pred_arg_ty, &paths::OS_STRING);
                    then {
                        pred_arg
                    } else {
                        continue;
                    }
                };

                let (local, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(
                    cx,
                    mir,
                    pred_arg,
                    true,
                    mir[ps[0]].statements.iter()
                ));
                if cannot_move_out {
                    continue;
                }
                local
            } else {
                cloned
            };

            let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| {
                // Give up on loops
                if tdata.terminator().successors().any(|s| *s == bb) {
                    return true;
                }

                let mut vis = LocalUseVisitor {
                    local: referent,
                    used_other_than_drop: false,
                };
                vis.visit_basic_block_data(tbb, tdata);
                vis.used_other_than_drop
            });

            if !used_later {
                let span = terminator.source_info.span;
                let node = if let mir::ClearCrossCrate::Set(scope_local_data) = &mir.source_scope_local_data {
                    scope_local_data[terminator.source_info.scope].lint_root
                } else {
                    unreachable!()
                };

                if_chain! {
                    if let Some(snip) = snippet_opt(cx, span);
                    if let Some(dot) = snip.rfind('.');
                    then {
                        let sugg_span = span.with_lo(
                            span.lo() + BytePos(u32::try_from(dot).unwrap())
                        );

                        span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |db| {
                            db.span_suggestion(
                                sugg_span,
                                "remove this",
                                String::new(),
                                Applicability::MaybeIncorrect,
                            );
                            db.span_note(
                                span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
                                "this value is dropped without further use",
                            );
                        });
                    } else {
                        span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone");
                    }
                }
            }
        }
    }
}

/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
fn is_call_with_ref_arg<'tcx>(
    cx: &LateContext<'_, 'tcx>,
    mir: &'tcx mir::Body<'tcx>,
    kind: &'tcx mir::TerminatorKind<'tcx>,
) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, Option<&'tcx mir::Place<'tcx>>)> {
    if_chain! {
        if let TerminatorKind::Call { func, args, destination, .. } = kind;
        if args.len() == 1;
        if let mir::Operand::Move(mir::Place { base: mir::PlaceBase::Local(local), .. }) = &args[0];
        if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
        if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
        if !is_copy(cx, inner_ty);
        then {
            Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)))
        } else {
            None
        }
    }
}

type CannotMoveOut = bool;

/// Finds the first `to = (&)from`, and returns
/// ``Some((from, [`true` if `from` cannot be moved out]))``.
fn find_stmt_assigns_to<'a, 'tcx: 'a>(
    cx: &LateContext<'_, 'tcx>,
    mir: &mir::Body<'tcx>,
    to: mir::Local,
    by_ref: bool,
    stmts: impl DoubleEndedIterator<Item = &'a mir::Statement<'tcx>>,
) -> Option<(mir::Local, CannotMoveOut)> {
    stmts
        .rev()
        .find_map(|stmt| {
            if let mir::StatementKind::Assign(
                mir::Place {
                    base: mir::PlaceBase::Local(local),
                    ..
                },
                v,
            ) = &stmt.kind
            {
                if *local == to {
                    return Some(v);
                }
            }

            None
        })
        .and_then(|v| {
            if by_ref {
                if let mir::Rvalue::Ref(_, _, ref place) = **v {
                    return base_local_and_movability(cx, mir, place);
                }
            } else if let mir::Rvalue::Use(mir::Operand::Copy(ref place)) = **v {
                return base_local_and_movability(cx, mir, place);
            }
            None
        })
}

/// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself
/// if it is already a `Local`.
///
/// Also reports whether given `place` cannot be moved out.
fn base_local_and_movability<'tcx>(
    cx: &LateContext<'_, 'tcx>,
    mir: &mir::Body<'tcx>,
    place: &mir::Place<'tcx>,
) -> Option<(mir::Local, CannotMoveOut)> {
    use rustc::mir::Place;
    use rustc::mir::PlaceBase;
    use rustc::mir::PlaceRef;
    use rustc::mir::Projection;

    // Dereference. You cannot move things out from a borrowed value.
    let mut deref = false;
    // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
    let mut field = false;

    let PlaceRef {
        base: place_base,
        mut projection,
    } = place.as_place_ref();
    if let PlaceBase::Local(local) = place_base {
        while let Some(box Projection { base, elem }) = projection {
            projection = base;
            deref = matches!(elem, mir::ProjectionElem::Deref);
            field = !field
                && matches!(elem, mir::ProjectionElem::Field(..))
                && has_drop(cx, Place::ty_from(place_base, projection, &mir.local_decls, cx.tcx).ty);
        }

        Some((*local, deref || field))
    } else {
        None
    }
}

struct LocalUseVisitor {
    local: mir::Local,
    used_other_than_drop: bool,
}

impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
    fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
        let statements = &data.statements;
        for (statement_index, statement) in statements.iter().enumerate() {
            self.visit_statement(statement, mir::Location { block, statement_index });

            // Once flagged, skip remaining statements
            if self.used_other_than_drop {
                return;
            }
        }

        self.visit_terminator(
            data.terminator(),
            mir::Location {
                block,
                statement_index: statements.len(),
            },
        );
    }

    fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext, _: mir::Location) {
        match ctx {
            PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) => return,
            _ => {},
        }

        if *local == self.local {
            self.used_other_than_drop = true;
        }
    }
}
<commit_msg>rustup https://github.com/rust-lang/rust/pull/62859<commit_after>use crate::utils::{
    has_drop, in_macro_or_desugar, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_hir,
    span_lint_hir_and_then, walk_ptrs_ty_depth,
};
use if_chain::if_chain;
use matches::matches;
use rustc::hir::intravisit::FnKind;
use rustc::hir::{def_id, Body, FnDecl, HirId};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::mir::{
    self, traversal,
    visit::{MutatingUseContext, PlaceContext, Visitor},
    TerminatorKind,
};
use rustc::ty::{self, Ty};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use std::convert::TryFrom;
use syntax::source_map::{BytePos, Span};

macro_rules! unwrap_or_continue {
    ($x:expr) => {
        match $x {
            Some(x) => x,
            None => continue,
        }
    };
}

declare_clippy_lint! {
    /// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
    /// value that is going to be dropped without further use.
    ///
    /// **Why is this bad?** It is not always possible for the compiler to eliminate useless
    /// allocations and deallocations generated by redundant `clone()`s.
    ///
    /// **Known problems:**
    ///
    /// * Suggestions made by this lint could require NLL to be enabled.
    /// * False-positive if there is a borrow preventing the value from moving out.
    ///
    /// ```rust
    /// let x = String::new();
    ///
    /// let y = &x;
    ///
    /// foo(x.clone()); // This lint suggests to remove this `clone()`
    /// ```
    ///
    /// **Example:**
    /// ```rust
    /// {
    ///     let x = Foo::new();
    ///     call(x.clone());
    ///     call(x.clone()); // this can just pass `x`
    /// }
    ///
    /// ["lorem", "ipsum"].join(" ").to_string()
    ///
    /// Path::new("/a/b").join("c").to_path_buf()
    /// ```
    pub REDUNDANT_CLONE,
    nursery,
    "`clone()` of an owned value that is going to be dropped immediately"
}

declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
    #[allow(clippy::too_many_lines)]
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        _: FnKind<'tcx>,
        _: &'tcx FnDecl,
        body: &'tcx Body,
        _: Span,
        _: HirId,
    ) {
        let def_id = cx.tcx.hir().body_owner_def_id(body.id());
        let mir = cx.tcx.optimized_mir(def_id);

        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
            let terminator = bbdata.terminator();

            if in_macro_or_desugar(terminator.source_info.span) {
                continue;
            }

            // Give up on loops
            if terminator.successors().any(|s| *s == bb) {
                continue;
            }

            let (fn_def_id, arg, arg_ty, _) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));

            let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
                || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
                || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD) && match_type(cx, arg_ty, &paths::STRING));

            let from_deref = !from_borrow
                && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
                    || match_def_path(cx, fn_def_id, &paths::OS_STR_TO_OS_STRING));

            if !from_borrow && !from_deref {
                continue;
            }

            // _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
            // In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
            // block.
            let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(
                cx,
                mir,
                arg,
                from_borrow,
                bbdata.statements.iter()
            ));

            if from_borrow && cannot_move_out {
                continue;
            }

            // _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
            let referent = if from_deref {
                let ps = mir.predecessors_for(bb);
                if ps.len() != 1 {
                    continue;
                }
                let pred_terminator = mir[ps[0]].terminator();

                let pred_arg = if_chain! {
                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) =
                        is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
                    if res.base == mir::PlaceBase::Local(cloned);
                    if match_def_path(cx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
                    if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
                        || match_type(cx, pred_arg_ty, &paths::OS_STRING);
                    then {
                        pred_arg
                    } else {
                        continue;
                    }
                };

                let (local, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(
                    cx,
                    mir,
                    pred_arg,
                    true,
                    mir[ps[0]].statements.iter()
                ));
                if cannot_move_out {
                    continue;
                }
                local
            } else {
                cloned
            };

            let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| {
                // Give up on loops
                if tdata.terminator().successors().any(|s| *s == bb) {
                    return true;
                }

                let mut vis = LocalUseVisitor {
                    local: referent,
                    used_other_than_drop: false,
                };
                vis.visit_basic_block_data(tbb, tdata);
                vis.used_other_than_drop
            });

            if !used_later {
                let span = terminator.source_info.span;
                let node = if let mir::ClearCrossCrate::Set(scope_local_data) = &mir.source_scope_local_data {
                    scope_local_data[terminator.source_info.scope].lint_root
                } else {
                    unreachable!()
                };

                if_chain! {
                    if let Some(snip) = snippet_opt(cx, span);
                    if let Some(dot) = snip.rfind('.');
                    then {
                        let sugg_span = span.with_lo(
                            span.lo() + BytePos(u32::try_from(dot).unwrap())
                        );

                        span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |db| {
                            db.span_suggestion(
                                sugg_span,
                                "remove this",
                                String::new(),
                                Applicability::MaybeIncorrect,
                            );
                            db.span_note(
                                span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
                                "this value is dropped without further use",
                            );
                        });
                    } else {
                        span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone");
                    }
                }
            }
        }
    }
}

/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
fn is_call_with_ref_arg<'tcx>(
    cx: &LateContext<'_, 'tcx>,
    mir: &'tcx mir::Body<'tcx>,
    kind: &'tcx mir::TerminatorKind<'tcx>,
) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, Option<&'tcx mir::Place<'tcx>>)> {
    if_chain! {
        if let TerminatorKind::Call { func, args, destination, .. } = kind;
        if args.len() == 1;
        if let mir::Operand::Move(mir::Place { base: mir::PlaceBase::Local(local), .. }) = &args[0];
        if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
        if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
        if !is_copy(cx, inner_ty);
        then {
            Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)))
        } else {
            None
        }
    }
}

type CannotMoveOut = bool;

/// Finds the first `to = (&)from`, and returns
/// ``Some((from, [`true` if `from` cannot be moved out]))``.
fn find_stmt_assigns_to<'a, 'tcx: 'a>(
    cx: &LateContext<'_, 'tcx>,
    mir: &mir::Body<'tcx>,
    to: mir::Local,
    by_ref: bool,
    stmts: impl DoubleEndedIterator<Item = &'a mir::Statement<'tcx>>,
) -> Option<(mir::Local, CannotMoveOut)> {
    stmts
        .rev()
        .find_map(|stmt| {
            if let mir::StatementKind::Assign(
                mir::Place {
                    base: mir::PlaceBase::Local(local),
                    ..
                },
                v,
            ) = &stmt.kind
            {
                if *local == to {
                    return Some(v);
                }
            }

            None
        })
        .and_then(|v| {
            if by_ref {
                if let mir::Rvalue::Ref(_, _, ref place) = **v {
                    return base_local_and_movability(cx, mir, place);
                }
            } else if let mir::Rvalue::Use(mir::Operand::Copy(ref place)) = **v {
                return base_local_and_movability(cx, mir, place);
            }
            None
        })
}

/// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself
/// if it is already a `Local`.
///
/// Also reports whether given `place` cannot be moved out.
fn base_local_and_movability<'tcx>(
    cx: &LateContext<'_, 'tcx>,
    mir: &mir::Body<'tcx>,
    place: &mir::Place<'tcx>,
) -> Option<(mir::Local, CannotMoveOut)> {
    use rustc::mir::Place;
    use rustc::mir::PlaceBase;
    use rustc::mir::PlaceRef;
    use rustc::mir::Projection;

    // Dereference. You cannot move things out from a borrowed value.
    let mut deref = false;
    // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
    let mut field = false;

    let PlaceRef {
        base: place_base,
        mut projection,
    } = place.as_ref();
    if let PlaceBase::Local(local) = place_base {
        while let Some(box Projection { base, elem }) = projection {
            projection = base;
            deref = matches!(elem, mir::ProjectionElem::Deref);
            field = !field
                && matches!(elem, mir::ProjectionElem::Field(..))
                && has_drop(cx, Place::ty_from(place_base, projection, &mir.local_decls, cx.tcx).ty);
        }

        Some((*local, deref || field))
    } else {
        None
    }
}

struct LocalUseVisitor {
    local: mir::Local,
    used_other_than_drop: bool,
}

impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
    fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
        let statements = &data.statements;
        for (statement_index, statement) in statements.iter().enumerate() {
            self.visit_statement(statement, mir::Location { block, statement_index });

            // Once flagged, skip remaining statements
            if self.used_other_than_drop {
                return;
            }
        }

        self.visit_terminator(
            data.terminator(),
            mir::Location {
                block,
                statement_index: statements.len(),
            },
        );
    }

    fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext, _: mir::Location) {
        match ctx {
            PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) => return,
            _ => {},
        }

        if *local == self.local {
            self.used_other_than_drop = true;
        }
    }
}
<|endoftext|>