<commit_before>use analysis::c_type::{implements_c_type, rustify_pointers};
use analysis::rust_type::{Result, TypeError};
use env::Env;
use library::*;
use traits::*;

pub fn used_ffi_type(env: &Env, type_id: TypeId, c_type: &str) -> Option<String> {
    let (_ptr, inner) = rustify_pointers(c_type);
    let type_ = ffi_inner(env, type_id, &inner);
    type_
        .ok()
        .and_then(|type_name| if type_name.find(':').is_some() {
            Some(type_name)
        } else {
            None
        })
}

pub fn ffi_type(env: &Env, tid: TypeId, c_type: &str) -> Result {
    let (ptr, inner) = rustify_pointers(c_type);
    let res = if ptr.is_empty() {
        if let Some(c_tid) = env.library.find_type(0, c_type) {
            // Fast track plain fundamental types avoiding some checks
            if env.library
                .type_(c_tid)
                .maybe_ref_as::<Fundamental>()
                .is_some()
            {
                match *env.library.type_(tid) {
                    Type::FixedArray(_, size, _) => ffi_inner(env, c_tid, c_type)
                        .map_any(|s| format!("[{}; {}]", s, size)),
                    Type::Class(Class {
                        c_type: ref expected,
                        ..
                    }) |
                    Type::Interface(Interface {
                        c_type: ref expected,
                        ..
                    }) if c_type == "gpointer" =>
                    {
                        info!("[c:type `gpointer` instead of `*mut {}`, fixing]", expected);
                        ffi_inner(env, tid, expected).map_any(|s| format!("*mut {}", s))
                    }
                    _ => ffi_inner(env, c_tid, c_type),
                }
            } else {
                // c_type isn't fundamental
                ffi_inner(env, tid, &inner)
            }
        } else {
            // c_type doesn't match any type in the library by name
            ffi_inner(env, tid, &inner)
        }
    } else {
        // ptr not empty
        ffi_inner(env, tid, &inner).map_any(|s| format!("{} {}", ptr, s))
    };
    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
    res
}

fn ffi_inner(env: &Env, tid: TypeId, inner: &str) -> Result {
    let typ = env.library.type_(tid);
    match *typ {
        Type::Fundamental(fund) => {
            use library::Fundamental::*;
            let inner = match fund {
                None => "libc::c_void",
                Boolean => "glib_ffi::gboolean", //TODO:check main namespace
                Int8 => "i8",
                UInt8 => "u8",
                Int16 => "i16",
                UInt16 => "u16",
                Int32 => "i32",
                UInt32 => "u32",
                Int64 => "i64",
                UInt64 => "u64",
                Char => "libc::c_char",
                UChar => "libc::c_uchar",
                Short => "libc::c_short",
                UShort => "libc::c_ushort",
                Int => "libc::c_int",
                UInt => "libc::c_uint",
                Long => "libc::c_long",
                ULong => "libc::c_ulong",
                Size => "libc::size_t",
                SSize => "libc::ssize_t",
                Float => "libc::c_float",
                Double => "libc::c_double",
                UniChar => "u32",
                Utf8 => "libc::c_char",
                Filename => "libc::c_char",
                Type => "glib_ffi::GType",
                IntPtr => "libc::intptr_t",
                UIntPtr => "libc::uintptr_t",
                _ => return Err(TypeError::Unimplemented(inner.into())),
            };
            Ok(inner.into())
        }
        Type::Union(..) | Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
            if let Some(declared_c_type) = typ.get_glib_name() {
                if declared_c_type != inner {
                    let msg = format!(
                        "[c:type mismatch `{}` != `{}` of `{}`]",
                        inner,
                        declared_c_type,
                        typ.get_name()
                    );
                    warn_main!(tid, "{}", msg);
                    return Err(TypeError::Mismatch(msg));
                }
            } else {
                warn_main!(tid, "Type `{}` missing c_type", typ.get_name());
            }
            fix_name(env, tid, inner)
        }
        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
        Type::FixedArray(inner_tid, size, _) => {
            ffi_inner(env, inner_tid, inner).map_any(|s| format!("[{}; {}]", s, size))
        }
        Type::Array(..) |
        Type::PtrArray(..) |
        Type::List(..) |
        Type::SList(..) |
        Type::HashTable(..) => fix_name(env, tid, inner),
        _ => if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
            if inner != glib_name {
                if implements_c_type(env, tid, inner) {
                    info!(
                        "[c:type {} of {} <: {}, fixing]",
                        glib_name,
                        env.library.type_(tid).get_name(),
                        inner
                    );
                    fix_name(env, tid, glib_name)
                } else {
                    let msg = format!(
                        "[c:type mismatch {} != {} of {}]",
                        inner,
                        glib_name,
                        env.library.type_(tid).get_name()
                    );
                    warn_main!(tid, "{}", msg);
                    Err(TypeError::Mismatch(msg))
                }
            } else {
                fix_name(env, tid, inner)
            }
        } else {
            let msg = format!(
                "[Missing glib_name of {}, can't match != {}]",
                env.library.type_(tid).get_name(),
                inner
            );
            warn_main!(tid, "{}", msg);
            Err(TypeError::Mismatch(msg))
        },
    }
}

fn fix_name(env: &Env, type_id: TypeId, name: &str) -> Result {
    if type_id.ns_id == INTERNAL_NAMESPACE {
        match *env.library.type_(type_id) {
            Type::Array(..) |
            Type::PtrArray(..) |
            Type::List(..) |
            Type::SList(..) |
            Type::HashTable(..) => Ok(format!(
                "{}::{}",
                &env.namespaces[env.namespaces.glib_ns_id].ffi_crate_name,
                name
            )),
            _ => Ok(name.into()),
        }
    } else {
        let name_with_prefix = format!(
            "{}::{}",
            &env.namespaces[type_id.ns_id].ffi_crate_name,
            name
        );
        if env.type_status_sys(&type_id.full_name(&env.library)).ignored() {
            Err(TypeError::Ignored(name_with_prefix))
        } else {
            Ok(name_with_prefix)
        }
    }
}
<commit_msg>Fix invalid gboolean generation<commit_after>use analysis::c_type::{implements_c_type, rustify_pointers};
use analysis::rust_type::{Result, TypeError};
use env::Env;
use library::*;
use nameutil::get_crate_name;
use traits::*;

pub fn used_ffi_type(env: &Env, type_id: TypeId, c_type: &str) -> Option<String> {
    let (_ptr, inner) = rustify_pointers(c_type);
    let type_ = ffi_inner(env, type_id, &inner);
    type_
        .ok()
        .and_then(|type_name| if type_name.find(':').is_some() {
            Some(type_name)
        } else {
            None
        })
}

pub fn ffi_type(env: &Env, tid: TypeId, c_type: &str) -> Result {
    let (ptr, inner) = rustify_pointers(c_type);
    let res = if ptr.is_empty() {
        if let Some(c_tid) = env.library.find_type(0, c_type) {
            // Fast track plain fundamental types avoiding some checks
            if env.library
                .type_(c_tid)
                .maybe_ref_as::<Fundamental>()
                .is_some()
            {
                match *env.library.type_(tid) {
                    Type::FixedArray(_, size, _) => ffi_inner(env, c_tid, c_type)
                        .map_any(|s| format!("[{}; {}]", s, size)),
                    Type::Class(Class {
                        c_type: ref expected,
                        ..
                    }) |
                    Type::Interface(Interface {
                        c_type: ref expected,
                        ..
                    }) if c_type == "gpointer" =>
                    {
                        info!("[c:type `gpointer` instead of `*mut {}`, fixing]", expected);
                        ffi_inner(env, tid, expected).map_any(|s| format!("*mut {}", s))
                    }
                    _ => ffi_inner(env, c_tid, c_type),
                }
            } else {
                // c_type isn't fundamental
                ffi_inner(env, tid, &inner)
            }
        } else {
            // c_type doesn't match any type in the library by name
            ffi_inner(env, tid, &inner)
        }
    } else {
        // ptr not empty
        ffi_inner(env, tid, &inner).map_any(|s| format!("{} {}", ptr, s))
    };
    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
    res
}

fn ffi_inner(env: &Env, tid: TypeId, inner: &str) -> Result {
    let typ = env.library.type_(tid);
    match *typ {
        Type::Fundamental(fund) => {
            use library::Fundamental::*;
            let inner = match fund {
                None => "libc::c_void",
                Boolean => return Ok(format!("{}::gboolean", get_crate_name("GLib", env))),
                Int8 => "i8",
                UInt8 => "u8",
                Int16 => "i16",
                UInt16 => "u16",
                Int32 => "i32",
                UInt32 => "u32",
                Int64 => "i64",
                UInt64 => "u64",
                Char => "libc::c_char",
                UChar => "libc::c_uchar",
                Short => "libc::c_short",
                UShort => "libc::c_ushort",
                Int => "libc::c_int",
                UInt => "libc::c_uint",
                Long => "libc::c_long",
                ULong => "libc::c_ulong",
                Size => "libc::size_t",
                SSize => "libc::ssize_t",
                Float => "libc::c_float",
                Double => "libc::c_double",
                UniChar => "u32",
                Utf8 => "libc::c_char",
                Filename => "libc::c_char",
                Type => "glib_ffi::GType",
                IntPtr => "libc::intptr_t",
                UIntPtr => "libc::uintptr_t",
                _ => return Err(TypeError::Unimplemented(inner.into())),
            };
            Ok(inner.into())
        }
        Type::Union(..) | Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
            if let Some(declared_c_type) = typ.get_glib_name() {
                if declared_c_type != inner {
                    let msg = format!(
                        "[c:type mismatch `{}` != `{}` of `{}`]",
                        inner,
                        declared_c_type,
                        typ.get_name()
                    );
                    warn_main!(tid, "{}", msg);
                    return Err(TypeError::Mismatch(msg));
                }
            } else {
                warn_main!(tid, "Type `{}` missing c_type", typ.get_name());
            }
            fix_name(env, tid, inner)
        }
        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
        Type::FixedArray(inner_tid, size, _) => {
            ffi_inner(env, inner_tid, inner).map_any(|s| format!("[{}; {}]", s, size))
        }
        Type::Array(..) |
        Type::PtrArray(..) |
        Type::List(..) |
        Type::SList(..) |
        Type::HashTable(..) => fix_name(env, tid, inner),
        _ => if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
            if inner != glib_name {
                if implements_c_type(env, tid, inner) {
                    info!(
                        "[c:type {} of {} <: {}, fixing]",
                        glib_name,
                        env.library.type_(tid).get_name(),
                        inner
                    );
                    fix_name(env, tid, glib_name)
                } else {
                    let msg = format!(
                        "[c:type mismatch {} != {} of {}]",
                        inner,
                        glib_name,
                        env.library.type_(tid).get_name()
                    );
                    warn_main!(tid, "{}", msg);
                    Err(TypeError::Mismatch(msg))
                }
            } else {
                fix_name(env, tid, inner)
            }
        } else {
            let msg = format!(
                "[Missing glib_name of {}, can't match != {}]",
                env.library.type_(tid).get_name(),
                inner
            );
            warn_main!(tid, "{}", msg);
            Err(TypeError::Mismatch(msg))
        },
    }
}

fn fix_name(env: &Env, type_id: TypeId, name: &str) -> Result {
    if type_id.ns_id == INTERNAL_NAMESPACE {
        match *env.library.type_(type_id) {
            Type::Array(..) |
            Type::PtrArray(..) |
            Type::List(..) |
            Type::SList(..) |
            Type::HashTable(..) => Ok(format!(
                "{}::{}",
                &env.namespaces[env.namespaces.glib_ns_id].ffi_crate_name,
                name
            )),
            _ => Ok(name.into()),
        }
    } else {
        let name_with_prefix = format!(
            "{}::{}",
            &env.namespaces[type_id.ns_id].ffi_crate_name,
            name
        );
        if env.type_status_sys(&type_id.full_name(&env.library)).ignored() {
            Err(TypeError::Ignored(name_with_prefix))
        } else {
            Ok(name_with_prefix)
        }
    }
}
<|endoftext|>


<commit_before>//! `TypeFoldable` implementations for MIR types

use super::*;
use crate::ty;

CloneTypeFoldableAndLiftImpls! {
    BlockTailInfo,
    MirPhase,
    SourceInfo,
    FakeReadCause,
    RetagKind,
    SourceScope,
    SourceScopeLocalData,
    UserTypeAnnotationIndex,
}

impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::TerminatorKind::*;

        let kind = match self.kind {
            Goto { target } => Goto { target },
            SwitchInt { ref discr, switch_ty, ref targets } => SwitchInt {
                discr: discr.fold_with(folder),
                switch_ty: switch_ty.fold_with(folder),
                targets: targets.clone(),
            },
            Drop { ref place, target, unwind } => {
                Drop { place: place.fold_with(folder), target, unwind }
            }
            DropAndReplace { ref place, ref value, target, unwind } => DropAndReplace {
                place: place.fold_with(folder),
                value: value.fold_with(folder),
                target,
                unwind,
            },
            Yield { ref value, resume, ref resume_arg, drop } => Yield {
                value: value.fold_with(folder),
                resume,
                resume_arg: resume_arg.fold_with(folder),
                drop,
            },
            Call { ref func, ref args, ref destination, cleanup, from_hir_call, fn_span } => {
                let dest =
                    destination.as_ref().map(|&(ref loc, dest)| (loc.fold_with(folder), dest));

                Call {
                    func: func.fold_with(folder),
                    args: args.fold_with(folder),
                    destination: dest,
                    cleanup,
                    from_hir_call,
                    fn_span,
                }
            }
            Assert { ref cond, expected, ref msg, target, cleanup } => {
                use AssertKind::*;
                let msg = match msg {
                    BoundsCheck { len, index } => {
                        BoundsCheck { len: len.fold_with(folder), index: index.fold_with(folder) }
                    }
                    Overflow(op, l, r) => Overflow(*op, l.fold_with(folder), r.fold_with(folder)),
                    OverflowNeg(op) => OverflowNeg(op.fold_with(folder)),
                    DivisionByZero(op) => DivisionByZero(op.fold_with(folder)),
                    RemainderByZero(op) => RemainderByZero(op.fold_with(folder)),
                    ResumedAfterReturn(_) | ResumedAfterPanic(_) => msg.clone(),
                };
                Assert { cond: cond.fold_with(folder), expected, msg, target, cleanup }
            }
            GeneratorDrop => GeneratorDrop,
            Resume => Resume,
            Abort => Abort,
            Return => Return,
            Unreachable => Unreachable,
            FalseEdge { real_target, imaginary_target } => {
                FalseEdge { real_target, imaginary_target }
            }
            FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
            InlineAsm { template, ref operands, options, line_spans, destination } => InlineAsm {
                template,
                operands: operands.fold_with(folder),
                options,
                line_spans,
                destination,
            },
        };
        Terminator { source_info: self.source_info, kind }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        use crate::mir::TerminatorKind::*;

        match self.kind {
            SwitchInt { ref discr, switch_ty, .. } => {
                discr.visit_with(visitor)?;
                switch_ty.visit_with(visitor)
            }
            Drop { ref place, .. } => place.visit_with(visitor),
            DropAndReplace { ref place, ref value, .. } => {
                place.visit_with(visitor)?;
                value.visit_with(visitor)
            }
            Yield { ref value, .. } => value.visit_with(visitor),
            Call { ref func, ref args, ref destination, .. } => {
                if let Some((ref loc, _)) = *destination {
                    loc.visit_with(visitor)?;
                };
                func.visit_with(visitor)?;
                args.visit_with(visitor)
            }
            Assert { ref cond, ref msg, .. } => {
                if cond.visit_with(visitor).is_break() {
                    use AssertKind::*;
                    match msg {
                        BoundsCheck { ref len, ref index } => {
                            len.visit_with(visitor)?;
                            index.visit_with(visitor)
                        }
                        Overflow(_, l, r) => {
                            l.visit_with(visitor)?;
                            r.visit_with(visitor)
                        }
                        OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
                            op.visit_with(visitor)
                        }
                        ResumedAfterReturn(_) | ResumedAfterPanic(_) => ControlFlow::CONTINUE,
                    }
                } else {
                    ControlFlow::CONTINUE
                }
            }
            InlineAsm { ref operands, .. } => operands.visit_with(visitor),
            Goto { .. }
            | Resume
            | Abort
            | Return
            | GeneratorDrop
            | Unreachable
            | FalseEdge { .. }
            | FalseUnwind { .. } => ControlFlow::CONTINUE,
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for GeneratorKind {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        Place { local: self.local.fold_with(folder), projection: self.projection.fold_with(folder) }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.local.visit_with(visitor)?;
        self.projection.visit_with(visitor)
    }
}

impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<PlaceElem<'tcx>> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        let v = self.iter().map(|t| t.fold_with(folder)).collect::<Vec<_>>();
        folder.tcx().intern_place_elems(&v)
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.iter().try_for_each(|t| t.visit_with(visitor))
    }
}

impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::Rvalue::*;
        match *self {
            Use(ref op) => Use(op.fold_with(folder)),
            Repeat(ref op, len) => Repeat(op.fold_with(folder), len.fold_with(folder)),
            ThreadLocalRef(did) => ThreadLocalRef(did.fold_with(folder)),
            Ref(region, bk, ref place) => {
                Ref(region.fold_with(folder), bk, place.fold_with(folder))
            }
            AddressOf(mutability, ref place) => AddressOf(mutability, place.fold_with(folder)),
            Len(ref place) => Len(place.fold_with(folder)),
            Cast(kind, ref op, ty) => Cast(kind, op.fold_with(folder), ty.fold_with(folder)),
            BinaryOp(op, ref rhs, ref lhs) => {
                BinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder))
            }
            CheckedBinaryOp(op, ref rhs, ref lhs) => {
                CheckedBinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder))
            }
            UnaryOp(op, ref val) => UnaryOp(op, val.fold_with(folder)),
            Discriminant(ref place) => Discriminant(place.fold_with(folder)),
            NullaryOp(op, ty) => NullaryOp(op, ty.fold_with(folder)),
            Aggregate(ref kind, ref fields) => {
                let kind = box match **kind {
                    AggregateKind::Array(ty) => AggregateKind::Array(ty.fold_with(folder)),
                    AggregateKind::Tuple => AggregateKind::Tuple,
                    AggregateKind::Adt(def, v, substs, user_ty, n) => AggregateKind::Adt(
                        def,
                        v,
                        substs.fold_with(folder),
                        user_ty.fold_with(folder),
                        n,
                    ),
                    AggregateKind::Closure(id, substs) => {
                        AggregateKind::Closure(id, substs.fold_with(folder))
                    }
                    AggregateKind::Generator(id, substs, movablity) => {
                        AggregateKind::Generator(id, substs.fold_with(folder), movablity)
                    }
                };
                Aggregate(kind, fields.fold_with(folder))
            }
        }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        use crate::mir::Rvalue::*;
        match *self {
            Use(ref op) => op.visit_with(visitor),
            Repeat(ref op, _) => op.visit_with(visitor),
            ThreadLocalRef(did) => did.visit_with(visitor),
            Ref(region, _, ref place) => {
                region.visit_with(visitor)?;
                place.visit_with(visitor)
            }
            AddressOf(_, ref place) => place.visit_with(visitor),
            Len(ref place) => place.visit_with(visitor),
            Cast(_, ref op, ty) => {
                op.visit_with(visitor)?;
                ty.visit_with(visitor)
            }
            BinaryOp(_, ref rhs, ref lhs) | CheckedBinaryOp(_, ref rhs, ref lhs) => {
                rhs.visit_with(visitor)?;
                lhs.visit_with(visitor)
            }
            UnaryOp(_, ref val) => val.visit_with(visitor),
            Discriminant(ref place) => place.visit_with(visitor),
            NullaryOp(_, ty) => ty.visit_with(visitor),
            Aggregate(ref kind, ref fields) => {
                match **kind {
                    AggregateKind::Array(ty) => {
                        ty.visit_with(visitor)?;
                    }
                    AggregateKind::Tuple => {}
                    AggregateKind::Adt(_, _, substs, user_ty, _) => {
                        substs.visit_with(visitor)?;
                        user_ty.visit_with(visitor)?;
                    }
                    AggregateKind::Closure(_, substs) => {
                        substs.visit_with(visitor)?;
                    }
                    AggregateKind::Generator(_, substs, _) => {
                        substs.visit_with(visitor)?;
                    }
                }
                fields.visit_with(visitor)
            }
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        match *self {
            Operand::Copy(ref place) => Operand::Copy(place.fold_with(folder)),
            Operand::Move(ref place) => Operand::Move(place.fold_with(folder)),
            Operand::Constant(ref c) => Operand::Constant(c.fold_with(folder)),
        }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        match *self {
            Operand::Copy(ref place) | Operand::Move(ref place) => place.visit_with(visitor),
            Operand::Constant(ref c) => c.visit_with(visitor),
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::ProjectionElem::*;

        match *self {
            Deref => Deref,
            Field(f, ty) => Field(f, ty.fold_with(folder)),
            Index(v) => Index(v.fold_with(folder)),
            Downcast(symbol, variantidx) => Downcast(symbol, variantidx),
            ConstantIndex { offset, min_length, from_end } => {
                ConstantIndex { offset, min_length, from_end }
            }
            Subslice { from, to, from_end } => Subslice { from, to, from_end },
        }
    }

    fn super_visit_with<Vs: TypeVisitor<'tcx>>(&self, visitor: &mut Vs) -> ControlFlow<()> {
        use crate::mir::ProjectionElem::*;

        match self {
            Field(_, ty) => ty.visit_with(visitor),
            Index(v) => v.visit_with(visitor),
            _ => ControlFlow::CONTINUE,
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for Field {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx, R: Idx, C: Idx> TypeFoldable<'tcx> for BitMatrix<R, C> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        self.clone()
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        Constant {
            span: self.span,
            user_ty: self.user_ty.fold_with(folder),
            literal: self.literal.fold_with(folder),
        }
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.literal.visit_with(visitor)
    }
}
<commit_msg>fix `super_visit_with` for `Terminator`<commit_after>//! `TypeFoldable` implementations for MIR types

use super::*;
use crate::ty;

CloneTypeFoldableAndLiftImpls! {
    BlockTailInfo,
    MirPhase,
    SourceInfo,
    FakeReadCause,
    RetagKind,
    SourceScope,
    SourceScopeLocalData,
    UserTypeAnnotationIndex,
}

impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::TerminatorKind::*;

        let kind = match self.kind {
            Goto { target } => Goto { target },
            SwitchInt { ref discr, switch_ty, ref targets } => SwitchInt {
                discr: discr.fold_with(folder),
                switch_ty: switch_ty.fold_with(folder),
                targets: targets.clone(),
            },
            Drop { ref place, target, unwind } => {
                Drop { place: place.fold_with(folder), target, unwind }
            }
            DropAndReplace { ref place, ref value, target, unwind } => DropAndReplace {
                place: place.fold_with(folder),
                value: value.fold_with(folder),
                target,
                unwind,
            },
            Yield { ref value, resume, ref resume_arg, drop } => Yield {
                value: value.fold_with(folder),
                resume,
                resume_arg: resume_arg.fold_with(folder),
                drop,
            },
            Call { ref func, ref args, ref destination, cleanup, from_hir_call, fn_span } => {
                let dest =
                    destination.as_ref().map(|&(ref loc, dest)| (loc.fold_with(folder), dest));

                Call {
                    func: func.fold_with(folder),
                    args: args.fold_with(folder),
                    destination: dest,
                    cleanup,
                    from_hir_call,
                    fn_span,
                }
            }
            Assert { ref cond, expected, ref msg, target, cleanup } => {
                use AssertKind::*;
                let msg = match msg {
                    BoundsCheck { len, index } => {
                        BoundsCheck { len: len.fold_with(folder), index: index.fold_with(folder) }
                    }
                    Overflow(op, l, r) => Overflow(*op, l.fold_with(folder), r.fold_with(folder)),
                    OverflowNeg(op) => OverflowNeg(op.fold_with(folder)),
                    DivisionByZero(op) => DivisionByZero(op.fold_with(folder)),
                    RemainderByZero(op) => RemainderByZero(op.fold_with(folder)),
                    ResumedAfterReturn(_) | ResumedAfterPanic(_) => msg.clone(),
                };
                Assert { cond: cond.fold_with(folder), expected, msg, target, cleanup }
            }
            GeneratorDrop => GeneratorDrop,
            Resume => Resume,
            Abort => Abort,
            Return => Return,
            Unreachable => Unreachable,
            FalseEdge { real_target, imaginary_target } => {
                FalseEdge { real_target, imaginary_target }
            }
            FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
            InlineAsm { template, ref operands, options, line_spans, destination } => InlineAsm {
                template,
                operands: operands.fold_with(folder),
                options,
                line_spans,
                destination,
            },
        };
        Terminator { source_info: self.source_info, kind }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        use crate::mir::TerminatorKind::*;

        match self.kind {
            SwitchInt { ref discr, switch_ty, .. } => {
                discr.visit_with(visitor)?;
                switch_ty.visit_with(visitor)
            }
            Drop { ref place, .. } => place.visit_with(visitor),
            DropAndReplace { ref place, ref value, .. } => {
                place.visit_with(visitor)?;
                value.visit_with(visitor)
            }
            Yield { ref value, .. } => value.visit_with(visitor),
            Call { ref func, ref args, ref destination, .. } => {
                if let Some((ref loc, _)) = *destination {
                    loc.visit_with(visitor)?;
                };
                func.visit_with(visitor)?;
                args.visit_with(visitor)
            }
            Assert { ref cond, ref msg, .. } => {
                cond.visit_with(visitor)?;
                use AssertKind::*;
                match msg {
                    BoundsCheck { ref len, ref index } => {
                        len.visit_with(visitor)?;
                        index.visit_with(visitor)
                    }
                    Overflow(_, l, r) => {
                        l.visit_with(visitor)?;
                        r.visit_with(visitor)
                    }
                    OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
                        op.visit_with(visitor)
                    }
                    ResumedAfterReturn(_) | ResumedAfterPanic(_) => ControlFlow::CONTINUE,
                }
            }
            InlineAsm { ref operands, .. } => operands.visit_with(visitor),
            Goto { .. }
            | Resume
            | Abort
            | Return
            | GeneratorDrop
            | Unreachable
            | FalseEdge { .. }
            | FalseUnwind { .. } => ControlFlow::CONTINUE,
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for GeneratorKind {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        Place { local: self.local.fold_with(folder), projection: self.projection.fold_with(folder) }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.local.visit_with(visitor)?;
        self.projection.visit_with(visitor)
    }
}

impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<PlaceElem<'tcx>> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        let v = self.iter().map(|t| t.fold_with(folder)).collect::<Vec<_>>();
        folder.tcx().intern_place_elems(&v)
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.iter().try_for_each(|t| t.visit_with(visitor))
    }
}

impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::Rvalue::*;
        match *self {
            Use(ref op) => Use(op.fold_with(folder)),
            Repeat(ref op, len) => Repeat(op.fold_with(folder), len.fold_with(folder)),
            ThreadLocalRef(did) => ThreadLocalRef(did.fold_with(folder)),
            Ref(region, bk, ref place) => {
                Ref(region.fold_with(folder), bk, place.fold_with(folder))
            }
            AddressOf(mutability, ref place) => AddressOf(mutability, place.fold_with(folder)),
            Len(ref place) => Len(place.fold_with(folder)),
            Cast(kind, ref op, ty) => Cast(kind, op.fold_with(folder), ty.fold_with(folder)),
            BinaryOp(op, ref rhs, ref lhs) => {
                BinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder))
            }
            CheckedBinaryOp(op, ref rhs, ref lhs) => {
                CheckedBinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder))
            }
            UnaryOp(op, ref val) => UnaryOp(op, val.fold_with(folder)),
            Discriminant(ref place) => Discriminant(place.fold_with(folder)),
            NullaryOp(op, ty) => NullaryOp(op, ty.fold_with(folder)),
            Aggregate(ref kind, ref fields) => {
                let kind = box match **kind {
                    AggregateKind::Array(ty) => AggregateKind::Array(ty.fold_with(folder)),
                    AggregateKind::Tuple => AggregateKind::Tuple,
                    AggregateKind::Adt(def, v, substs, user_ty, n) => AggregateKind::Adt(
                        def,
                        v,
                        substs.fold_with(folder),
                        user_ty.fold_with(folder),
                        n,
                    ),
                    AggregateKind::Closure(id, substs) => {
                        AggregateKind::Closure(id, substs.fold_with(folder))
                    }
                    AggregateKind::Generator(id, substs, movablity) => {
                        AggregateKind::Generator(id, substs.fold_with(folder), movablity)
                    }
                };
                Aggregate(kind, fields.fold_with(folder))
            }
        }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        use crate::mir::Rvalue::*;
        match *self {
            Use(ref op) => op.visit_with(visitor),
            Repeat(ref op, _) => op.visit_with(visitor),
            ThreadLocalRef(did) => did.visit_with(visitor),
            Ref(region, _, ref place) => {
                region.visit_with(visitor)?;
                place.visit_with(visitor)
            }
            AddressOf(_, ref place) => place.visit_with(visitor),
            Len(ref place) => place.visit_with(visitor),
            Cast(_, ref op, ty) => {
                op.visit_with(visitor)?;
                ty.visit_with(visitor)
            }
            BinaryOp(_, ref rhs, ref lhs) | CheckedBinaryOp(_, ref rhs, ref lhs) => {
                rhs.visit_with(visitor)?;
                lhs.visit_with(visitor)
            }
            UnaryOp(_, ref val) => val.visit_with(visitor),
            Discriminant(ref place) => place.visit_with(visitor),
            NullaryOp(_, ty) => ty.visit_with(visitor),
            Aggregate(ref kind, ref fields) => {
                match **kind {
                    AggregateKind::Array(ty) => {
                        ty.visit_with(visitor)?;
                    }
                    AggregateKind::Tuple => {}
                    AggregateKind::Adt(_, _, substs, user_ty, _) => {
                        substs.visit_with(visitor)?;
                        user_ty.visit_with(visitor)?;
                    }
                    AggregateKind::Closure(_, substs) => {
                        substs.visit_with(visitor)?;
                    }
                    AggregateKind::Generator(_, substs, _) => {
                        substs.visit_with(visitor)?;
                    }
                }
                fields.visit_with(visitor)
            }
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        match *self {
            Operand::Copy(ref place) => Operand::Copy(place.fold_with(folder)),
            Operand::Move(ref place) => Operand::Move(place.fold_with(folder)),
            Operand::Constant(ref c) => Operand::Constant(c.fold_with(folder)),
        }
    }

    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        match *self {
            Operand::Copy(ref place) | Operand::Move(ref place) => place.visit_with(visitor),
            Operand::Constant(ref c) => c.visit_with(visitor),
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        use crate::mir::ProjectionElem::*;

        match *self {
            Deref => Deref,
            Field(f, ty) => Field(f, ty.fold_with(folder)),
            Index(v) => Index(v.fold_with(folder)),
            Downcast(symbol, variantidx) => Downcast(symbol, variantidx),
            ConstantIndex { offset, min_length, from_end } => {
                ConstantIndex { offset, min_length, from_end }
            }
            Subslice { from, to, from_end } => Subslice { from, to, from_end },
        }
    }

    fn super_visit_with<Vs: TypeVisitor<'tcx>>(&self, visitor: &mut Vs) -> ControlFlow<()> {
        use crate::mir::ProjectionElem::*;

        match self {
            Field(_, ty) => ty.visit_with(visitor),
            Index(v) => v.visit_with(visitor),
            _ => ControlFlow::CONTINUE,
        }
    }
}

impl<'tcx> TypeFoldable<'tcx> for Field {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        *self
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx, R: Idx, C: Idx> TypeFoldable<'tcx> for BitMatrix<R, C> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
        self.clone()
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<()> {
        ControlFlow::CONTINUE
    }
}

impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
    fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
        Constant {
            span: self.span,
            user_ty: self.user_ty.fold_with(folder),
            literal: self.literal.fold_with(folder),
        }
    }
    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<()> {
        self.literal.visit_with(visitor)
    }
}
<|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 rustc::front::map as ast_map;
use rustc::lint::{LateContext, LintPass, LintArray, LateLintPass, LintContext};
use rustc::middle::ty;
use rustc_front::hir;
use rustc_front::intravisit as visit;
use syntax::attr::AttrMetaMethods;
use syntax::{ast, codemap};
use utils::{match_def_path, in_derive_expn};

declare_lint!(UNROOTED_MUST_ROOT, Deny,
              "Warn and report usage of unrooted jsmanaged objects");

/// Lint for ensuring safe usage of unrooted pointers
///
/// This lint (disable with `-A unrooted-must-root`/`#[allow(unrooted_must_root)]`) ensures that `#[must_root]`
/// values are used correctly.
///
/// "Incorrect" usage includes:
///
///  - Not being used in a struct/enum field which is not `#[must_root]` itself
///  - Not being used as an argument to a function (Except onces named `new` and `new_inherited`)
///  - Not being bound locally in a `let` statement, assignment, `for` loop, or `match` statement.
///
/// This helps catch most situations where pointers like `JS<T>` are used in a way that they can be invalidated by a
/// GC pass.
///
/// Structs which have their own mechanism of rooting their unrooted contents (e.g. `ScriptTask`)
/// can be marked as `#[allow(unrooted_must_root)]`. Smart pointers which root their interior type
/// can be marked as `#[allow_unrooted_interior]`
pub struct UnrootedPass;

impl UnrootedPass {
    pub fn new() -> UnrootedPass {
        UnrootedPass
    }
}

/// Checks if a type is unrooted or contains any owned unrooted types
fn is_unrooted_ty(cx: &LateContext, ty: &ty::TyS, in_new_function: bool) -> bool {
    let mut ret = false;
    ty.maybe_walk(|t| {
        match t.sty {
            ty::TyStruct(did, _) |
            ty::TyEnum(did, _) => {
                if cx.tcx.has_attr(did.did, "must_root") {
                    ret = true;
                    false
                } else if cx.tcx.has_attr(did.did, "allow_unrooted_interior") {
                    false
                } else if match_def_path(cx, did.did, &["core", "cell", "Ref"])
                        || match_def_path(cx, did.did, &["core", "cell", "RefMut"])
                        || match_def_path(cx, did.did, &["core", "slice", "Iter"])
                        || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "OccupiedEntry"])
                        || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "VacantEntry"]) {
                    // Structures which are semantically similar to an &ptr.
                    false
                } else {
                    true
                }
            },
            ty::TyBox(..) if in_new_function => false, // box in new() is okay
            ty::TyRef(..) => false, // don't recurse down &ptrs
            ty::TyRawPtr(..) => false, // don't recurse down *ptrs
            _ => true
        }
    });
    ret
}

impl LintPass for UnrootedPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNROOTED_MUST_ROOT)
    }
}

impl LateLintPass for UnrootedPass {
    /// All structs containing #[must_root] types must be #[must_root] themselves
    fn check_struct_def(&mut self,
                        cx: &LateContext,
                        def: &hir::VariantData,
                        _n: ast::Name,
                        _gen: &hir::Generics,
                        id: ast::NodeId) {
        let item = match cx.tcx.map.get(id) {
            ast_map::Node::NodeItem(item) => item,
            _ => cx.tcx.map.expect_item(cx.tcx.map.get_parent(id)),
        };
        if item.attrs.iter().all(|a| !a.check_name("must_root")) {
            for ref field in def.fields() {
                if is_unrooted_ty(cx, cx.tcx.node_id_to_type(field.node.id), false) {
                    cx.span_lint(UNROOTED_MUST_ROOT, field.span,
                                 "Type must be rooted, use #[must_root] on the struct definition to propagate")
                }
            }
        }
    }

    /// All enums containing #[must_root] types must be #[must_root] themselves
    fn check_variant(&mut self, cx: &LateContext, var: &hir::Variant, _gen: &hir::Generics) {
        let ref map = cx.tcx.map;
        if map.expect_item(map.get_parent(var.node.data.id())).attrs.iter().all(|a| !a.check_name("must_root")) {
            match var.node.data {
                hir::VariantData::Tuple(ref vec, _) => {
                    for ty in vec {
                        cx.tcx.ast_ty_to_ty_cache.borrow().get(&ty.node.id).map(|t| {
                            if is_unrooted_ty(cx, t, false) {
                                cx.span_lint(UNROOTED_MUST_ROOT, ty.node.ty.span,
                                             "Type must be rooted, use #[must_root] on \
                                              the enum definition to propagate")
                            }
                        });
                    }
                }
                _ => () // Struct variants already caught by check_struct_def
            }
        }
    }
    /// Function arguments that are #[must_root] types are not allowed
    fn check_fn(&mut self, cx: &LateContext, kind: visit::FnKind, decl: &hir::FnDecl,
                block: &hir::Block, span: codemap::Span, _id: ast::NodeId) {
        let in_new_function = match kind {
            visit::FnKind::ItemFn(n, _, _, _, _, _) |
            visit::FnKind::Method(n, _, _) => {
                n.as_str() == "new" || n.as_str().starts_with("new_")
            }
            visit::FnKind::Closure => return,
        };

        for arg in &decl.inputs {
            cx.tcx.ast_ty_to_ty_cache.borrow().get(&arg.ty.id).map(|t| {
                if is_unrooted_ty(cx, t, false) {
                    if in_derive_expn(cx, span) {
                        return;
                    }
                    cx.span_lint(UNROOTED_MUST_ROOT, arg.ty.span, "Type must be rooted")
                }
            });
        }

        if !in_new_function {
            if let hir::Return(ref ty) = decl.output {
                cx.tcx.ast_ty_to_ty_cache.borrow().get(&ty.id).map(|t| {
                    if is_unrooted_ty(cx, t, false) {
                        if in_derive_expn(cx, span) {
                            return;
                        }
                        cx.span_lint(UNROOTED_MUST_ROOT, ty.span, "Type must be rooted")
                    }
                });
            }
        }

        let mut visitor = FnDefVisitor {
            cx: cx,
            in_new_function: in_new_function,
        };
        visit::walk_block(&mut visitor, block);
    }
}

struct FnDefVisitor<'a, 'b: 'a, 'tcx: 'a+'b> {
    cx: &'a LateContext<'b, 'tcx>,
    in_new_function: bool,
}

impl<'a, 'b: 'a, 'tcx: 'a+'b> visit::Visitor<'a> for FnDefVisitor<'a, 'b, 'tcx> {
    fn visit_expr(&mut self, expr: &'a hir::Expr) {
        let cx = self.cx;

        fn require_rooted(cx: &LateContext, in_new_function: bool, subexpr: &hir::Expr) {
            let ty = cx.tcx.expr_ty(&*subexpr);
            if is_unrooted_ty(cx, ty, in_new_function) {
                cx.span_lint(UNROOTED_MUST_ROOT,
                             subexpr.span,
                             &format!("Expression of type {:?} must be rooted", ty))
            }
        }

        match expr.node {
            /// Trait casts from #[must_root] types are not allowed
            hir::ExprCast(ref subexpr, _) => require_rooted(cx, self.in_new_function, &*subexpr),
            // This catches assignments... the main point of this would be to catch mutable
            // references to `JS<T>`.
            // FIXME: Enable this? Triggers on certain kinds of uses of DOMRefCell.
            // hir::ExprAssign(_, ref rhs) => require_rooted(cx, self.in_new_function, &*rhs),
            // This catches calls; basically, this enforces the constraint that only constructors
            // can call other constructors.
            // FIXME: Enable this? Currently triggers with constructs involving DOMRefCell, and
            // constructs like Vec<JS<T>> and RootedVec<JS<T>>.
            // hir::ExprCall(..) if !self.in_new_function => {
            //     require_rooted(cx, self.in_new_function, expr);
            // }
            _ => {
                // TODO(pcwalton): Check generics with a whitelist of allowed generics.
            }
        }

        visit::walk_expr(self, expr);
    }

    fn visit_pat(&mut self, pat: &'a hir::Pat) {
        let cx = self.cx;

        if let hir::PatIdent(hir::BindByValue(_), _, _) = pat.node {
            let ty = cx.tcx.pat_ty(pat);
            if is_unrooted_ty(cx, ty, self.in_new_function) {
                cx.span_lint(UNROOTED_MUST_ROOT,
                            pat.span,
                            &format!("Expression of type {:?} must be rooted", ty))
            }
        }

        visit::walk_pat(self, pat);
    }

    fn visit_fn(&mut self, kind: visit::FnKind<'a>, decl: &'a hir::FnDecl,
                block: &'a hir::Block, span: codemap::Span, _id: ast::NodeId) {
        if kind == visit::FnKind::Closure {
            visit::walk_fn(self, kind, decl, block, span);
        }
    }

    fn visit_foreign_item(&mut self, _: &'a hir::ForeignItem) {}
    fn visit_ty(&mut self, _: &'a hir::Ty) { }
}
<commit_msg>Auto merge of #8731 - eefriedman:root-lint-pattern, r=Manishearth<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 rustc::front::map as ast_map;
use rustc::lint::{LateContext, LintPass, LintArray, LateLintPass, LintContext};
use rustc::middle::pat_util::pat_is_binding;
use rustc::middle::ty;
use rustc_front::hir;
use rustc_front::intravisit as visit;
use syntax::attr::AttrMetaMethods;
use syntax::{ast, codemap};
use utils::{match_def_path, in_derive_expn};

declare_lint!(UNROOTED_MUST_ROOT, Deny,
              "Warn and report usage of unrooted jsmanaged objects");

/// Lint for ensuring safe usage of unrooted pointers
///
/// This lint (disable with `-A unrooted-must-root`/`#[allow(unrooted_must_root)]`) ensures that `#[must_root]`
/// values are used correctly.
///
/// "Incorrect" usage includes:
///
///  - Not being used in a struct/enum field which is not `#[must_root]` itself
///  - Not being used as an argument to a function (Except onces named `new` and `new_inherited`)
///  - Not being bound locally in a `let` statement, assignment, `for` loop, or `match` statement.
///
/// This helps catch most situations where pointers like `JS<T>` are used in a way that they can be invalidated by a
/// GC pass.
///
/// Structs which have their own mechanism of rooting their unrooted contents (e.g. `ScriptTask`)
/// can be marked as `#[allow(unrooted_must_root)]`. Smart pointers which root their interior type
/// can be marked as `#[allow_unrooted_interior]`
pub struct UnrootedPass;

impl UnrootedPass {
    pub fn new() -> UnrootedPass {
        UnrootedPass
    }
}

/// Checks if a type is unrooted or contains any owned unrooted types
fn is_unrooted_ty(cx: &LateContext, ty: &ty::TyS, in_new_function: bool) -> bool {
    let mut ret = false;
    ty.maybe_walk(|t| {
        match t.sty {
            ty::TyStruct(did, _) |
            ty::TyEnum(did, _) => {
                if cx.tcx.has_attr(did.did, "must_root") {
                    ret = true;
                    false
                } else if cx.tcx.has_attr(did.did, "allow_unrooted_interior") {
                    false
                } else if match_def_path(cx, did.did, &["core", "cell", "Ref"])
                        || match_def_path(cx, did.did, &["core", "cell", "RefMut"])
                        || match_def_path(cx, did.did, &["core", "slice", "Iter"])
                        || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "OccupiedEntry"])
                        || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "VacantEntry"]) {
                    // Structures which are semantically similar to an &ptr.
                    false
                } else {
                    true
                }
            },
            ty::TyBox(..) if in_new_function => false, // box in new() is okay
            ty::TyRef(..) => false, // don't recurse down &ptrs
            ty::TyRawPtr(..) => false, // don't recurse down *ptrs
            _ => true
        }
    });
    ret
}

impl LintPass for UnrootedPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNROOTED_MUST_ROOT)
    }
}

impl LateLintPass for UnrootedPass {
    /// All structs containing #[must_root] types must be #[must_root] themselves
    fn check_struct_def(&mut self,
                        cx: &LateContext,
                        def: &hir::VariantData,
                        _n: ast::Name,
                        _gen: &hir::Generics,
                        id: ast::NodeId) {
        let item = match cx.tcx.map.get(id) {
            ast_map::Node::NodeItem(item) => item,
            _ => cx.tcx.map.expect_item(cx.tcx.map.get_parent(id)),
        };
        if item.attrs.iter().all(|a| !a.check_name("must_root")) {
            for ref field in def.fields() {
                if is_unrooted_ty(cx, cx.tcx.node_id_to_type(field.node.id), false) {
                    cx.span_lint(UNROOTED_MUST_ROOT, field.span,
                                 "Type must be rooted, use #[must_root] on the struct definition to propagate")
                }
            }
        }
    }

    /// All enums containing #[must_root] types must be #[must_root] themselves
    fn check_variant(&mut self, cx: &LateContext, var: &hir::Variant, _gen: &hir::Generics) {
        let ref map = cx.tcx.map;
        if map.expect_item(map.get_parent(var.node.data.id())).attrs.iter().all(|a| !a.check_name("must_root")) {
            match var.node.data {
                hir::VariantData::Tuple(ref vec, _) => {
                    for ty in vec {
                        cx.tcx.ast_ty_to_ty_cache.borrow().get(&ty.node.id).map(|t| {
                            if is_unrooted_ty(cx, t, false) {
                                cx.span_lint(UNROOTED_MUST_ROOT, ty.node.ty.span,
                                             "Type must be rooted, use #[must_root] on \
                                              the enum definition to propagate")
                            }
                        });
                    }
                }
                _ => () // Struct variants already caught by check_struct_def
            }
        }
    }
    /// Function arguments that are #[must_root] types are not allowed
    fn check_fn(&mut self, cx: &LateContext, kind: visit::FnKind, decl: &hir::FnDecl,
                block: &hir::Block, span: codemap::Span, _id: ast::NodeId) {
        let in_new_function = match kind {
            visit::FnKind::ItemFn(n, _, _, _, _, _) |
            visit::FnKind::Method(n, _, _) => {
                n.as_str() == "new" || n.as_str().starts_with("new_")
            }
            visit::FnKind::Closure => return,
        };

        for arg in &decl.inputs {
            cx.tcx.ast_ty_to_ty_cache.borrow().get(&arg.ty.id).map(|t| {
                if is_unrooted_ty(cx, t, false) {
                    if in_derive_expn(cx, span) {
                        return;
                    }
                    cx.span_lint(UNROOTED_MUST_ROOT, arg.ty.span, "Type must be rooted")
                }
            });
        }

        if !in_new_function {
            if let hir::Return(ref ty) = decl.output {
                cx.tcx.ast_ty_to_ty_cache.borrow().get(&ty.id).map(|t| {
                    if is_unrooted_ty(cx, t, false) {
                        if in_derive_expn(cx, span) {
                            return;
                        }
                        cx.span_lint(UNROOTED_MUST_ROOT, ty.span, "Type must be rooted")
                    }
                });
            }
        }

        let mut visitor = FnDefVisitor {
            cx: cx,
            in_new_function: in_new_function,
        };
        visit::walk_block(&mut visitor, block);
    }
}

struct FnDefVisitor<'a, 'b: 'a, 'tcx: 'a+'b> {
    cx: &'a LateContext<'b, 'tcx>,
    in_new_function: bool,
}

impl<'a, 'b: 'a, 'tcx: 'a+'b> visit::Visitor<'a> for FnDefVisitor<'a, 'b, 'tcx> {
    fn visit_expr(&mut self, expr: &'a hir::Expr) {
        let cx = self.cx;

        fn require_rooted(cx: &LateContext, in_new_function: bool, subexpr: &hir::Expr) {
            let ty = cx.tcx.expr_ty(&*subexpr);
            if is_unrooted_ty(cx, ty, in_new_function) {
                cx.span_lint(UNROOTED_MUST_ROOT,
                             subexpr.span,
                             &format!("Expression of type {:?} must be rooted", ty))
            }
        }

        match expr.node {
            /// Trait casts from #[must_root] types are not allowed
            hir::ExprCast(ref subexpr, _) => require_rooted(cx, self.in_new_function, &*subexpr),
            // This catches assignments... the main point of this would be to catch mutable
            // references to `JS<T>`.
            // FIXME: Enable this? Triggers on certain kinds of uses of DOMRefCell.
            // hir::ExprAssign(_, ref rhs) => require_rooted(cx, self.in_new_function, &*rhs),
            // This catches calls; basically, this enforces the constraint that only constructors
            // can call other constructors.
            // FIXME: Enable this? Currently triggers with constructs involving DOMRefCell, and
            // constructs like Vec<JS<T>> and RootedVec<JS<T>>.
            // hir::ExprCall(..) if !self.in_new_function => {
            //     require_rooted(cx, self.in_new_function, expr);
            // }
            _ => {
                // TODO(pcwalton): Check generics with a whitelist of allowed generics.
            }
        }

        visit::walk_expr(self, expr);
    }

    fn visit_pat(&mut self, pat: &'a hir::Pat) {
        let cx = self.cx;

        if let hir::PatIdent(hir::BindByValue(_), _, _) = pat.node {
            if pat_is_binding(&cx.tcx.def_map.borrow(), pat) {
                let ty = cx.tcx.pat_ty(pat);
                if is_unrooted_ty(cx, ty, self.in_new_function) {
                    cx.span_lint(UNROOTED_MUST_ROOT,
                                pat.span,
                                &format!("Expression of type {:?} must be rooted", ty))
                }
            }
        }

        visit::walk_pat(self, pat);
    }

    fn visit_fn(&mut self, kind: visit::FnKind<'a>, decl: &'a hir::FnDecl,
                block: &'a hir::Block, span: codemap::Span, _id: ast::NodeId) {
        if kind == visit::FnKind::Closure {
            visit::walk_fn(self, kind, decl, block, span);
        }
    }

    fn visit_foreign_item(&mut self, _: &'a hir::ForeignItem) {}
    fn visit_ty(&mut self, _: &'a hir::Ty) { }
}
<|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/. */

//! An iterator over a list of rules.

use context::QuirksMode;
use media_queries::Device;
use shared_lock::SharedRwLockReadGuard;
use smallvec::SmallVec;
use std::slice;
use stylesheets::{CssRule, CssRules, DocumentRule, ImportRule, MediaRule, SupportsRule};

/// An iterator over a list of rules.
pub struct RulesIterator<'a, 'b, C>
    where 'b: 'a,
          C: NestedRuleIterationCondition + 'static,
{
    device: &'a Device,
    quirks_mode: QuirksMode,
    guard: &'a SharedRwLockReadGuard<'b>,
    stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>,
    _phantom: ::std::marker::PhantomData<C>,
}

impl<'a, 'b, C> RulesIterator<'a, 'b, C>
    where 'b: 'a,
          C: NestedRuleIterationCondition + 'static,
{
    /// Creates a new `RulesIterator` to iterate over `rules`.
    pub fn new(
        device: &'a Device,
        quirks_mode: QuirksMode,
        guard: &'a SharedRwLockReadGuard<'b>,
        rules: &'a CssRules)
        -> Self
    {
        let mut stack = SmallVec::new();
        stack.push(rules.0.iter());
        Self {
            device: device,
            quirks_mode: quirks_mode,
            guard: guard,
            stack: stack,
            _phantom: ::std::marker::PhantomData,
        }
    }

    /// Skips all the remaining children of the last nested rule processed.
    pub fn skip_children(&mut self) {
        self.stack.pop();
    }
}

impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
    where 'b: 'a,
          C: NestedRuleIterationCondition + 'static,
{
    type Item = &'a CssRule;

    fn next(&mut self) -> Option<Self::Item> {
        let mut nested_iter_finished = false;
        while !self.stack.is_empty() {
            if nested_iter_finished {
                self.stack.pop();
                nested_iter_finished = false;
                continue;
            }

            let rule;
            let sub_iter;
            {
                let mut nested_iter = self.stack.last_mut().unwrap();
                rule = match nested_iter.next() {
                    Some(r) => r,
                    None => {
                        nested_iter_finished = true;
                        continue
                    }
                };

                sub_iter = match *rule {
                    CssRule::Import(ref import_rule) => {
                        let import_rule = import_rule.read_with(self.guard);
                        if !C::process_import(self.guard,
                                              self.device,
                                              self.quirks_mode,
                                              import_rule) {
                            continue;
                        }
                        Some(import_rule.stylesheet.rules.read_with(self.guard).0.iter())
                    }
                    CssRule::Document(ref doc_rule) => {
                        let doc_rule = doc_rule.read_with(self.guard);
                        if !C::process_document(self.guard,
                                                self.device,
                                                self.quirks_mode,
                                                doc_rule) {
                            continue;
                        }
                        Some(doc_rule.rules.read_with(self.guard).0.iter())
                    }
                    CssRule::Media(ref lock) => {
                        let media_rule = lock.read_with(self.guard);
                        if !C::process_media(self.guard,
                                             self.device,
                                             self.quirks_mode,
                                             media_rule) {
                            continue;
                        }
                        Some(media_rule.rules.read_with(self.guard).0.iter())
                    }
                    CssRule::Supports(ref lock) => {
                        let supports_rule = lock.read_with(self.guard);
                        if !C::process_supports(self.guard,
                                                self.device,
                                                self.quirks_mode,
                                                supports_rule) {
                            continue;
                        }
                        Some(supports_rule.rules.read_with(self.guard).0.iter())
                    }
                    CssRule::Namespace(_) |
                    CssRule::Style(_) |
                    CssRule::FontFace(_) |
                    CssRule::CounterStyle(_) |
                    CssRule::Viewport(_) |
                    CssRule::Keyframes(_) |
                    CssRule::Page(_) => None,
                };
            }

            if let Some(sub_iter) = sub_iter {
                self.stack.push(sub_iter);
            }

            return Some(rule);
        }

        None
    }
}

/// RulesIterator.
pub trait NestedRuleIterationCondition {
    /// Whether we should process the nested rules in a given `@import` rule.
    fn process_import(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &ImportRule)
        -> bool;

    /// Whether we should process the nested rules in a given `@media` rule.
    fn process_media(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &MediaRule)
        -> bool;

    /// Whether we should process the nested rules in a given `@-moz-document`
    /// rule.
    fn process_document(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &DocumentRule)
        -> bool;

    /// Whether we should process the nested rules in a given `@supports` rule.
    fn process_supports(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &SupportsRule)
        -> bool;
}

/// A struct that represents the condition that a rule applies to the document.
pub struct EffectiveRules;

impl NestedRuleIterationCondition for EffectiveRules {
    fn process_import(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &ImportRule)
        -> bool
    {
        rule.stylesheet.media.read_with(guard).evaluate(device, quirks_mode)
    }

    fn process_media(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &MediaRule)
        -> bool
    {
        rule.media_queries.read_with(guard).evaluate(device, quirks_mode)
    }

    fn process_document(
        _: &SharedRwLockReadGuard,
        device: &Device,
        _: QuirksMode,
        rule: &DocumentRule)
        -> bool
    {
        rule.condition.evaluate(device)
    }

    fn process_supports(
        _: &SharedRwLockReadGuard,
        _: &Device,
        _: QuirksMode,
        rule: &SupportsRule)
        -> bool
    {
        rule.enabled
    }
}

/// A filter that processes all the rules in a rule list.
pub struct AllRules;

impl NestedRuleIterationCondition for AllRules {
    fn process_import(
        _: &SharedRwLockReadGuard,
        _: &Device,
        _: QuirksMode,
        _: &ImportRule)
        -> bool
    {
        true
    }

    fn process_media(
        _: &SharedRwLockReadGuard,
        _: &Device,
        _: QuirksMode,
        _: &MediaRule)
        -> bool
    {
        true
    }

    fn process_document(
        _: &SharedRwLockReadGuard,
        _: &Device,
        _: QuirksMode,
        _: &DocumentRule)
        -> bool
    {
        true
    }

    fn process_supports(
        _: &SharedRwLockReadGuard,
        _: &Device,
        _: QuirksMode,
        _: &SupportsRule)
        -> bool
    {
        true
    }
}

/// An iterator over all the effective rules of a stylesheet.
///
/// NOTE: This iterator recurses into `@import` rules.
pub type EffectiveRulesIterator<'a, 'b> = RulesIterator<'a, 'b, EffectiveRules>;
<commit_msg>style: Avoid some branches in common RuleIterator::next cases.<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/. */

//! An iterator over a list of rules.

use context::QuirksMode;
use media_queries::Device;
use shared_lock::SharedRwLockReadGuard;
use smallvec::SmallVec;
use std::slice;
use stylesheets::{CssRule, CssRules, DocumentRule, ImportRule, MediaRule, SupportsRule};

/// An iterator over a list of rules.
pub struct RulesIterator<'a, 'b, C>
    where 'b: 'a,
          C: NestedRuleIterationCondition + 'static,
{
    device: &'a Device,
    quirks_mode: QuirksMode,
    guard: &'a SharedRwLockReadGuard<'b>,
    stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>,
    _phantom: ::std::marker::PhantomData<C>,
}

impl<'a, 'b, C> RulesIterator<'a, 'b, C>
    where 'b: 'a,
          C: NestedRuleIterationCondition + 'static,
{
    /// Creates a new `RulesIterator` to iterate over `rules`.
    pub fn new(
        device: &'a Device,
        quirks_mode: QuirksMode,
        guard: &'a SharedRwLockReadGuard<'b>,
        rules: &'a CssRules)
        -> Self
    {
        let mut stack = SmallVec::new();
        stack.push(rules.0.iter());
        Self {
            device: device,
            quirks_mode: quirks_mode,
            guard: guard,
            stack: stack,
            _phantom: ::std::marker::PhantomData,
        }
    }

    /// Skips all the remaining children of the last nested rule processed.
    pub fn skip_children(&mut self) {
        self.stack.pop();
    }
}

impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
    where 'b: 'a,
          C: NestedRuleIterationCondition + 'static,
{
    type Item = &'a CssRule;

    fn next(&mut self) -> Option<Self::Item> {
        let mut nested_iter_finished = false;
        while !self.stack.is_empty() {
            if nested_iter_finished {
                self.stack.pop();
                nested_iter_finished = false;
                continue;
            }

            let rule;
            let sub_iter = {
                let mut nested_iter = self.stack.last_mut().unwrap();
                rule = match nested_iter.next() {
                    Some(r) => r,
                    None => {
                        nested_iter_finished = true;
                        continue
                    }
                };

                match *rule {
                    CssRule::Namespace(_) |
                    CssRule::Style(_) |
                    CssRule::FontFace(_) |
                    CssRule::CounterStyle(_) |
                    CssRule::Viewport(_) |
                    CssRule::Keyframes(_) |
                    CssRule::Page(_) => {
                        return Some(rule)
                    },
                    CssRule::Import(ref import_rule) => {
                        let import_rule = import_rule.read_with(self.guard);
                        if !C::process_import(self.guard,
                                              self.device,
                                              self.quirks_mode,
                                              import_rule) {
                            continue;
                        }
                        import_rule.stylesheet.rules.read_with(self.guard).0.iter()
                    }
                    CssRule::Document(ref doc_rule) => {
                        let doc_rule = doc_rule.read_with(self.guard);
                        if !C::process_document(self.guard,
                                                self.device,
                                                self.quirks_mode,
                                                doc_rule) {
                            continue;
                        }
                        doc_rule.rules.read_with(self.guard).0.iter()
                    }
                    CssRule::Media(ref lock) => {
                        let media_rule = lock.read_with(self.guard);
                        if !C::process_media(self.guard,
                                             self.device,
                                             self.quirks_mode,
                                             media_rule) {
                            continue;
                        }
                        media_rule.rules.read_with(self.guard).0.iter()
                    }
                    CssRule::Supports(ref lock) => {
                        let supports_rule = lock.read_with(self.guard);
                        if !C::process_supports(self.guard,
                                                self.device,
                                                self.quirks_mode,
                                                supports_rule) {
                            continue;
                        }
                        supports_rule.rules.read_with(self.guard).0.iter()
                    }
                }
            };

            self.stack.push(sub_iter);
            return Some(rule);
        }

        None
    }
}

/// RulesIterator.
pub trait NestedRuleIterationCondition {
    /// Whether we should process the nested rules in a given `@import` rule.
    fn process_import(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &ImportRule)
        -> bool;

    /// Whether we should process the nested rules in a given `@media` rule.
    fn process_media(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &MediaRule)
        -> bool;

    /// Whether we should process the nested rules in a given `@-moz-document`
    /// rule.
    fn process_document(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &DocumentRule)
        -> bool;

    /// Whether we should process the nested rules in a given `@supports` rule.
    fn process_supports(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &SupportsRule)
        -> bool;
}

/// A struct that represents the condition that a rule applies to the document.
pub struct EffectiveRules;

impl NestedRuleIterationCondition for EffectiveRules {
    fn process_import(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &ImportRule)
        -> bool
    {
        rule.stylesheet.media.read_with(guard).evaluate(device, quirks_mode)
    }

    fn process_media(
        guard: &SharedRwLockReadGuard,
        device: &Device,
        quirks_mode: QuirksMode,
        rule: &MediaRule)
        -> bool
    {
        rule.media_queries.read_with(guard).evaluate(device, quirks_mode)
    }

    fn process_document(
        _: &SharedRwLockReadGuard,
        device: &Device,
        _: QuirksMode,
        rule: &DocumentRule)
        -> bool
    {
        rule.condition.evaluate(device)
    }

    fn process_supports(
        _: &SharedRwLockReadGuard,
        _: &Device,
        _: QuirksMode,
        rule: &SupportsRule)
        -> bool
    {
        rule.enabled
    }
}

/// A filter that processes all the rules in a rule list.
pub struct AllRules;

impl NestedRuleIterationCondition for AllRules {
    fn process_import(
        _: &SharedRwLockReadGuard,
        _: &Device,
        _: QuirksMode,
        _: &ImportRule)
        -> bool
    {
        true
    }

    fn process_media(
        _: &SharedRwLockReadGuard,
        _: &Device,
        _: QuirksMode,
        _: &MediaRule)
        -> bool
    {
        true
    }

    fn process_document(
        _: &SharedRwLockReadGuard,
        _: &Device,
        _: QuirksMode,
        _: &DocumentRule)
        -> bool
    {
        true
    }

    fn process_supports(
        _: &SharedRwLockReadGuard,
        _: &Device,
        _: QuirksMode,
        _: &SupportsRule)
        -> bool
    {
        true
    }
}

/// An iterator over all the effective rules of a stylesheet.
///
/// NOTE: This iterator recurses into `@import` rules.
pub type EffectiveRulesIterator<'a, 'b> = RulesIterator<'a, 'b, EffectiveRules>;
<|endoftext|>


<commit_before>#[macro_use]
extern crate asn1_cereal;

use std::io;

use asn1_cereal::serial::{Asn1Serialize, Asn1Deserialize};
use asn1_cereal::enc::{DER, BER};
use asn1_cereal::tag::Class;

fn main() {
  // let mut buffer: Vec<u8> = Vec::new();
  let mut buffer = io::BufWriter::new(io::stdout());
  let seq = IntSequence { a: 3, b: 4, c: SomeString("Hello".into()) };
  {
    let mut writer = io::BufWriter::new(&mut buffer);
    seq.serialize_enc(DER, &mut writer).unwrap();
  };
  // println!("{:?}", buffer);
  // {
  //   let mut reader = buffer.iter().map(|x| Ok(*x) as Result<u8, std::io::Error>);
  //   let seq = IntSequence::deserialize_enc(DER, &mut reader, None).unwrap();
  //   println!("{:?}", seq);
  // }
}

#[derive(Debug)]
struct SomeString(String);

asn1_info!(
  SomeString,
  Class::Private,
  1,
  true,
  "SOMESTRING"
);

asn1_newtype_serialize!(SomeString);
asn1_newtype_deserialize!(SomeString);

#[derive(Debug)]
struct IntSequence {
  a: u64,
  b: u64,
  c: SomeString,
}

asn1_sequence_info!(
  IntSequence,
  "INTSEQ"
);

asn1_sequence_serialize!(
  IntSequence,
  a,
  b,
  c
);

asn1_sequence_deserialize!(
  IntSequence,
  a,
  b,
  c
);
<commit_msg>Make simple.rs use DER<commit_after>#[macro_use]
extern crate asn1_cereal;

use std::io;

use asn1_cereal::serial::{Asn1Serialize, Asn1Deserialize};
use asn1_cereal::enc::{DER, BER};
use asn1_cereal::tag::Class;

fn main() {
  let mut buffer: Vec<u8> = Vec::new();
  // let mut buffer = io::BufWriter::new(io::stdout());
  let seq = IntSequence { a: 3, b: 4, c: SomeString("Hello".into()) };
  {
    let mut writer = io::BufWriter::new(&mut buffer);
    seq.serialize_enc(DER, &mut writer).unwrap();
  };
  println!("{:?}", buffer);
  {
    let mut reader = buffer.iter().map(|x| Ok(*x) as Result<u8, std::io::Error>);
    let seq = IntSequence::deserialize_enc(BER, &mut reader, None).unwrap();
    println!("{:?}", seq);
  }
}

#[derive(Debug)]
struct SomeString(String);

asn1_info!(
  SomeString,
  Class::Private,
  1,
  true,
  "SOMESTRING"
);

asn1_newtype_serialize!(SomeString);
asn1_newtype_deserialize!(SomeString);

#[derive(Debug)]
struct IntSequence {
  a: u64,
  b: u64,
  c: SomeString,
}

asn1_sequence_info!(
  IntSequence,
  "INTSEQ"
);

asn1_sequence_serialize!(
  IntSequence,
  a,
  b,
  c
);

asn1_sequence_deserialize!(
  IntSequence,
  a,
  b,
  c
);
<|endoftext|>


<commit_before>#![warn(clippy::panic_in_result_fn)]
#![allow(clippy::unnecessary_wraps)]

// debug_assert should never trigger the `panic_in_result_fn` lint

struct A;

impl A {
    fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String>
    {
        debug_assert!(x == 5, "wrong argument");
        Ok(true)
    }

    fn result_with_debug_assert_eq(x: i32) -> Result<bool, String>
    {
        debug_assert_eq!(x, 5);
        Ok(true)
    }

    fn result_with_debug_assert_ne(x: i32) -> Result<bool, String>
    {
        debug_assert_ne!(x, 1);
        Ok(true)
    }

    fn other_with_debug_assert_with_message(x: i32)
    {
        debug_assert!(x == 5, "wrong argument");
    }

    fn other_with_debug_assert_eq(x: i32)
    {
        debug_assert_eq!(x, 5);
    }

    fn other_with_debug_assert_ne(x: i32)
    {
        debug_assert_ne!(x, 1);
    }

    fn result_without_banned_functions() -> Result<bool, String>
    {
        let debug_assert = "debug_assert!";
        println!("No {}", debug_assert);
        Ok(true)
    }
}

fn main() {}
<commit_msg>Fix rustfmt<commit_after>#![warn(clippy::panic_in_result_fn)]
#![allow(clippy::unnecessary_wraps)]

// debug_assert should never trigger the `panic_in_result_fn` lint

struct A;

impl A {
    fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> {
        debug_assert!(x == 5, "wrong argument");
        Ok(true)
    }

    fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> {
        debug_assert_eq!(x, 5);
        Ok(true)
    }

    fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> {
        debug_assert_ne!(x, 1);
        Ok(true)
    }

    fn other_with_debug_assert_with_message(x: i32) {
        debug_assert!(x == 5, "wrong argument");
    }

    fn other_with_debug_assert_eq(x: i32) {
        debug_assert_eq!(x, 5);
    }

    fn other_with_debug_assert_ne(x: i32) {
        debug_assert_ne!(x, 1);
    }

    fn result_without_banned_functions() -> Result<bool, String> {
        let debug_assert = "debug_assert!";
        println!("No {}", debug_assert);
        Ok(true)
    }
}

fn main() {}
<|endoftext|>


<commit_before>extern crate cairo;
extern crate gio;
extern crate gtk;

use std::cell::RefCell;
use std::env::args;

use std::mem;
use std::rc::Rc;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

use cairo::prelude::*;
use cairo::{Context, Format, ImageSurface};
use gio::prelude::*;
use gtk::prelude::*;
use gtk::{ApplicationWindow, DrawingArea};

// make moving clones into closures more convenient
macro_rules! clone {
    (@param _) => ( _ );
    (@param $x:ident) => ( $x );
    ($($n:ident),+ => move || $body:expr) => (
        {
            $( let $n = $n.clone(); )+
            move || $body
        }
    );
    ($($n:ident),+ => move |$($p:tt),+| $body:expr) => (
        {
            $( let $n = $n.clone(); )+
            move |$(clone!(@param $p),)+| $body
        }
    );
}

// This example runs four worker threads rendering parts of the image
// independently at different paces in a sort of double buffered way.
//
// +---+---+
// | 0 | 1 |
// +---+---+
// | 2 | 3 |
// +---+---+
//
// Each worker thread waits for an image (cairo::ImageSurface) to render into, sleeps for a while,
// does the drawing, then sends the image back and waits for the next one.
//
// The GUI thread holds an ImageSurface per image part at all times, these
// surfaces are painted on a DrawingArea in its 'draw' signal handler. This
// thread periodically checks if any worker has sent a freshly rendered image.
// Upon receipt it's wrapped in ImageSurface and swapped with the previously
// held surface, whose image is sent to the worker thread again. Then the
// appropriate part of the DrawingArea is invalidated prompting a redraw.
//
// The two images per thread are allocated and initialized once and sent back
// and forth repeatedly.

fn build_ui(application: &gtk::Application) {
    let window = ApplicationWindow::new(application);
    let area = DrawingArea::new();
    window.add(&area);

    window.connect_delete_event(|win, _| {
        win.destroy();
        Inhibit(false)
    });

    let format = Format::Rgb24;
    let width = 200;
    let height = 200;
    area.set_size_request(width * 2, height * 2);

    let initial_image = draw_initial(format, width, height);
    let (ready_tx, ready_rx) = mpsc::channel();

    let mut images = Vec::new();
    let mut origins = Vec::new();
    let mut workers = Vec::new();

    for thread_num in 0..4 {
        let (tx, rx) = mpsc::channel();
        // copy the initial image in two images for the workers
        let image0 = duplicate_image(&initial_image);
        let image1 = duplicate_image(&initial_image);
        // store the first one in our vec
        images.push(image0);
        // send the second one immediately
        let _ = tx.send(image1);
        origins.push(match thread_num {
            0 => (0, 0),
            1 => (width, 0),
            2 => (0, height),
            _ => (width, height),
        });
        workers.push(tx);

        let x = (width - origins[thread_num].0) as f64;
        let y = (height - origins[thread_num].1) as f64;
        let delay = Duration::from_millis((100 << thread_num) - 5);

        // spawn the worker thread
        thread::spawn(clone!(ready_tx => move || {
            let mut n = 0;
            for image in rx.iter() {
                n = (n + 1) % 0x10000;
                let cr = Context::new(&image);
                // draw an arc with a weirdly calculated radius
                draw_slow(&cr, delay, x, y, 1.2_f64.powi(((n as i32) << thread_num) % 32));
                image.flush();
                let _ = ready_tx.send((thread_num, image));
            }
        }));
    }

    // our signal and timeout handler closures have to be 'static,
    // so they can't just borrow these
    let cell = Rc::new(RefCell::new((images, origins, workers)));

    area.connect_draw(clone!(cell => move |_, cr| {
        let (ref images, ref origins, _) = *cell.borrow();
        for (image, origin) in images.iter().zip(origins.iter()) {
            draw_image_if_dirty(&cr, image, *origin, (width, height));
        }
        Inhibit(false)
    }));

    gtk::timeout_add(100, move || {
        while let Ok((thread_num, mut image)) = ready_rx.try_recv() {
            let &mut (ref mut images, ref origins, ref workers) = &mut *cell.borrow_mut();
            let tx = workers[thread_num].clone();
            mem::swap(&mut images[thread_num], &mut image);
            let _ = tx.send(image);
            area.queue_draw_area(origins[thread_num].0, origins[thread_num].1, width, height);
        }
        Continue(true)
    });

    window.show_all();
}

fn main() {
    let application = gtk::Application::new("com.github.cairo_threads",
                                            gio::ApplicationFlags::empty())
                                       .expect("Initialization failed...");

    application.connect_startup(|app| {
        build_ui(app);
    });
    application.connect_activate(|_| {});

    application.run(&args().collect::<Vec<_>>());
}

fn draw_initial(format: Format, width: i32, height: i32) -> ImageSurface {
    let image = ImageSurface::create(format, width, height).expect("Can't create surface");
    {
        let cr = Context::new(&image);
        cr.set_source_rgb(0., 1., 0.);
        cr.paint();
        // Destroying the context releases its reference to `image`.
    }
    // We have a unique reference to `image` again and return it
    image
}

fn draw_slow(cr: &Context, delay: Duration, x: f64, y: f64, radius: f64) {
    thread::sleep(delay);
    cr.set_source_rgb(0., 0., 0.);
    cr.paint();
    cr.set_source_rgb(1., 1., 1.);
    cr.arc(x, y, radius, 0.0, 3.1416 * 2.);
    cr.stroke();
}

fn draw_image_if_dirty(cr: &Context, image: &ImageSurface, origin: (i32, i32),
        dimensions: (i32, i32)) {
    let x = origin.0 as f64;
    let y = origin.1 as f64;
    let w = dimensions.0 as f64;
    let h = dimensions.1 as f64;
    let (clip_x1, clip_y1, clip_x2, clip_y2) = cr.clip_extents();
    if clip_x1 >= x + w || clip_y1 >= y + h || clip_x2 <= x || clip_y2 <= y {
        return;
    }
    cr.set_source_surface(image, x, y);
    cr.paint();
}

fn duplicate_image(image: &ImageSurface) -> ImageSurface {
    let image_dup = ImageSurface::create(image.get_format(), image.get_width(), image.get_height()).expect("Can't create surface");
    {
        let cr = Context::new(&image_dup);
        cr.set_source_surface(image, 0.0, 0.0);
        cr.paint();
    }

    image_dup
}
<commit_msg>Remove unneeded clone()<commit_after>extern crate cairo;
extern crate gio;
extern crate gtk;

use std::cell::RefCell;
use std::env::args;

use std::mem;
use std::rc::Rc;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

use cairo::prelude::*;
use cairo::{Context, Format, ImageSurface};
use gio::prelude::*;
use gtk::prelude::*;
use gtk::{ApplicationWindow, DrawingArea};

// make moving clones into closures more convenient
macro_rules! clone {
    (@param _) => ( _ );
    (@param $x:ident) => ( $x );
    ($($n:ident),+ => move || $body:expr) => (
        {
            $( let $n = $n.clone(); )+
            move || $body
        }
    );
    ($($n:ident),+ => move |$($p:tt),+| $body:expr) => (
        {
            $( let $n = $n.clone(); )+
            move |$(clone!(@param $p),)+| $body
        }
    );
}

// This example runs four worker threads rendering parts of the image
// independently at different paces in a sort of double buffered way.
//
// +---+---+
// | 0 | 1 |
// +---+---+
// | 2 | 3 |
// +---+---+
//
// Each worker thread waits for an image (cairo::ImageSurface) to render into, sleeps for a while,
// does the drawing, then sends the image back and waits for the next one.
//
// The GUI thread holds an ImageSurface per image part at all times, these
// surfaces are painted on a DrawingArea in its 'draw' signal handler. This
// thread periodically checks if any worker has sent a freshly rendered image.
// Upon receipt it's wrapped in ImageSurface and swapped with the previously
// held surface, whose image is sent to the worker thread again. Then the
// appropriate part of the DrawingArea is invalidated prompting a redraw.
//
// The two images per thread are allocated and initialized once and sent back
// and forth repeatedly.

fn build_ui(application: &gtk::Application) {
    let window = ApplicationWindow::new(application);
    let area = DrawingArea::new();
    window.add(&area);

    window.connect_delete_event(|win, _| {
        win.destroy();
        Inhibit(false)
    });

    let format = Format::Rgb24;
    let width = 200;
    let height = 200;
    area.set_size_request(width * 2, height * 2);

    let initial_image = draw_initial(format, width, height);
    let (ready_tx, ready_rx) = mpsc::channel();

    let mut images = Vec::new();
    let mut origins = Vec::new();
    let mut workers = Vec::new();

    for thread_num in 0..4 {
        let (tx, rx) = mpsc::channel();
        // copy the initial image in two images for the workers
        let image0 = duplicate_image(&initial_image);
        let image1 = duplicate_image(&initial_image);
        // store the first one in our vec
        images.push(image0);
        // send the second one immediately
        let _ = tx.send(image1);
        origins.push(match thread_num {
            0 => (0, 0),
            1 => (width, 0),
            2 => (0, height),
            _ => (width, height),
        });
        workers.push(tx);

        let x = (width - origins[thread_num].0) as f64;
        let y = (height - origins[thread_num].1) as f64;
        let delay = Duration::from_millis((100 << thread_num) - 5);

        // spawn the worker thread
        thread::spawn(clone!(ready_tx => move || {
            let mut n = 0;
            for image in rx.iter() {
                n = (n + 1) % 0x10000;
                let cr = Context::new(&image);
                // draw an arc with a weirdly calculated radius
                draw_slow(&cr, delay, x, y, 1.2_f64.powi(((n as i32) << thread_num) % 32));
                image.flush();
                let _ = ready_tx.send((thread_num, image));
            }
        }));
    }

    // our signal and timeout handler closures have to be 'static,
    // so they can't just borrow these
    let cell = Rc::new(RefCell::new((images, origins, workers)));

    area.connect_draw(clone!(cell => move |_, cr| {
        let (ref images, ref origins, _) = *cell.borrow();
        for (image, origin) in images.iter().zip(origins.iter()) {
            draw_image_if_dirty(&cr, image, *origin, (width, height));
        }
        Inhibit(false)
    }));

    gtk::timeout_add(100, move || {
        while let Ok((thread_num, mut image)) = ready_rx.try_recv() {
            let &mut (ref mut images, ref origins, ref workers) = &mut *cell.borrow_mut();
            let tx = &workers[thread_num];
            mem::swap(&mut images[thread_num], &mut image);
            let _ = tx.send(image);
            area.queue_draw_area(origins[thread_num].0, origins[thread_num].1, width, height);
        }
        Continue(true)
    });

    window.show_all();
}

fn main() {
    let application = gtk::Application::new("com.github.cairo_threads",
                                            gio::ApplicationFlags::empty())
                                       .expect("Initialization failed...");

    application.connect_startup(|app| {
        build_ui(app);
    });
    application.connect_activate(|_| {});

    application.run(&args().collect::<Vec<_>>());
}

fn draw_initial(format: Format, width: i32, height: i32) -> ImageSurface {
    let image = ImageSurface::create(format, width, height).expect("Can't create surface");
    {
        let cr = Context::new(&image);
        cr.set_source_rgb(0., 1., 0.);
        cr.paint();
        // Destroying the context releases its reference to `image`.
    }
    // We have a unique reference to `image` again and return it
    image
}

fn draw_slow(cr: &Context, delay: Duration, x: f64, y: f64, radius: f64) {
    thread::sleep(delay);
    cr.set_source_rgb(0., 0., 0.);
    cr.paint();
    cr.set_source_rgb(1., 1., 1.);
    cr.arc(x, y, radius, 0.0, 3.1416 * 2.);
    cr.stroke();
}

fn draw_image_if_dirty(cr: &Context, image: &ImageSurface, origin: (i32, i32),
        dimensions: (i32, i32)) {
    let x = origin.0 as f64;
    let y = origin.1 as f64;
    let w = dimensions.0 as f64;
    let h = dimensions.1 as f64;
    let (clip_x1, clip_y1, clip_x2, clip_y2) = cr.clip_extents();
    if clip_x1 >= x + w || clip_y1 >= y + h || clip_x2 <= x || clip_y2 <= y {
        return;
    }
    cr.set_source_surface(image, x, y);
    cr.paint();
}

fn duplicate_image(image: &ImageSurface) -> ImageSurface {
    let image_dup = ImageSurface::create(image.get_format(), image.get_width(), image.get_height()).expect("Can't create surface");
    {
        let cr = Context::new(&image_dup);
        cr.set_source_surface(image, 0.0, 0.0);
        cr.paint();
    }

    image_dup
}
<|endoftext|>