<commit_before>use crate::thir::visit::{self, Visitor};

use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_middle::thir::*;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
use rustc_session::lint::Level;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::symbol::Symbol;
use rustc_span::Span;

use std::ops::Bound;

struct UnsafetyVisitor<'a, 'tcx> {
    tcx: TyCtxt<'tcx>,
    thir: &'a Thir<'tcx>,
    /// The `HirId` of the current scope, which would be the `HirId`
    /// of the current HIR node, modulo adjustments. Used for lint levels.
    hir_context: hir::HirId,
    /// The current "safety context". This notably tracks whether we are in an
    /// `unsafe` block, and whether it has been used.
    safety_context: SafetyContext,
    body_unsafety: BodyUnsafety,
    /// The `#[target_feature]` attributes of the body. Used for checking
    /// calls to functions with `#[target_feature]` (RFC 2396).
    body_target_features: &'tcx Vec<Symbol>,
    is_const: bool,
}

impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
    fn in_safety_context<R>(
        &mut self,
        safety_context: SafetyContext,
        f: impl FnOnce(&mut Self) -> R,
    ) {
        if let (
            SafetyContext::UnsafeBlock { span: enclosing_span, .. },
            SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
        ) = (self.safety_context, safety_context)
        {
            self.warn_unused_unsafe(
                hir_id,
                block_span,
                Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")),
            );
            f(self);
        } else {
            let prev_context = self.safety_context;
            self.safety_context = safety_context;

            f(self);

            if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
                self.warn_unused_unsafe(
                    hir_id,
                    span,
                    if self.unsafe_op_in_unsafe_fn_allowed() {
                        self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn"))
                    } else {
                        None
                    },
                );
            }
            self.safety_context = prev_context;
            return;
        }
    }

    fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
        let (description, note) = kind.description_and_note();
        let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
        match self.safety_context {
            SafetyContext::UnsafeBlock { ref mut used, .. } => {
                if !self.body_unsafety.is_unsafe() || !unsafe_op_in_unsafe_fn_allowed {
                    // Mark this block as useful
                    *used = true;
                }
            }
            SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
            SafetyContext::UnsafeFn => {
                // unsafe_op_in_unsafe_fn is disallowed
                self.tcx.struct_span_lint_hir(
                    UNSAFE_OP_IN_UNSAFE_FN,
                    self.hir_context,
                    span,
                    |lint| {
                        lint.build(&format!(
                            "{} is unsafe and requires unsafe block (error E0133)",
                            description,
                        ))
                        .span_label(span, description)
                        .note(note)
                        .emit();
                    },
                )
            }
            SafetyContext::Safe => {
                let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
                struct_span_err!(
                    self.tcx.sess,
                    span,
                    E0133,
                    "{} is unsafe and requires unsafe{} block",
                    description,
                    fn_sugg,
                )
                .span_label(span, description)
                .note(note)
                .emit();
            }
        }
    }

    fn warn_unused_unsafe(
        &self,
        hir_id: hir::HirId,
        block_span: Span,
        enclosing_unsafe: Option<(Span, &'static str)>,
    ) {
        let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
        self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, |lint| {
            let msg = "unnecessary `unsafe` block";
            let mut db = lint.build(msg);
            db.span_label(block_span, msg);
            if let Some((span, kind)) = enclosing_unsafe {
                db.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
            }
            db.emit();
        });
    }

    /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
    fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
    }
}

impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
    fn thir(&self) -> &'a Thir<'tcx> {
        &self.thir
    }

    fn visit_block(&mut self, block: &Block) {
        if let BlockSafety::ExplicitUnsafe(hir_id) = block.safety_mode {
            self.in_safety_context(
                SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
                |this| visit::walk_block(this, block),
            );
        } else {
            visit::walk_block(self, block);
        }
    }

    fn visit_expr(&mut self, expr: &Expr<'tcx>) {
        match expr.kind {
            ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
                let prev_id = self.hir_context;
                self.hir_context = hir_id;
                self.visit_expr(&self.thir[value]);
                self.hir_context = prev_id;
                return;
            }
            ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
                if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
                    self.requires_unsafe(expr.span, CallToUnsafeFunction);
                } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
                    // If the called function has target features the calling function hasn't,
                    // the call requires `unsafe`.
                    if !self
                        .tcx
                        .codegen_fn_attrs(func_did)
                        .target_features
                        .iter()
                        .all(|feature| self.body_target_features.contains(feature))
                    {
                        self.requires_unsafe(expr.span, CallToFunctionWith);
                    }
                }
            }
            ExprKind::Deref { arg } => {
                if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
                    if self.tcx.is_mutable_static(def_id) {
                        self.requires_unsafe(expr.span, UseOfMutableStatic);
                    } else if self.tcx.is_foreign_item(def_id) {
                        self.requires_unsafe(expr.span, UseOfExternStatic);
                    }
                } else if self.thir[arg].ty.is_unsafe_ptr() {
                    self.requires_unsafe(expr.span, DerefOfRawPointer);
                }
            }
            ExprKind::InlineAsm { .. } | ExprKind::LlvmInlineAsm { .. } => {
                self.requires_unsafe(expr.span, UseOfInlineAssembly);
            }
            ExprKind::Adt {
                adt_def,
                variant_index: _,
                substs: _,
                user_ty: _,
                fields: _,
                base: _,
            } => match self.tcx.layout_scalar_valid_range(adt_def.did) {
                (Bound::Unbounded, Bound::Unbounded) => {}
                _ => self.requires_unsafe(expr.span, InitializingTypeWith),
            },
            ExprKind::Cast { source } => {
                let source = &self.thir[source];
                if self.tcx.features().const_raw_ptr_to_usize_cast
                    && self.is_const
                    && (source.ty.is_unsafe_ptr() || source.ty.is_fn_ptr())
                    && expr.ty.is_integral()
                {
                    self.requires_unsafe(expr.span, CastOfPointerToInt);
                }
            }
            _ => {}
        }

        visit::walk_expr(self, expr);
    }
}

#[derive(Clone, Copy)]
enum SafetyContext {
    Safe,
    UnsafeFn,
    UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
}

#[derive(Clone, Copy)]
enum BodyUnsafety {
    /// The body is not unsafe.
    Safe,
    /// The body is an unsafe function. The span points to
    /// the signature of the function.
    Unsafe(Span),
}

impl BodyUnsafety {
    /// Returns whether the body is unsafe.
    fn is_unsafe(&self) -> bool {
        matches!(self, BodyUnsafety::Unsafe(_))
    }

    /// If the body is unsafe, returns the `Span` of its signature.
    fn unsafe_fn_sig_span(self) -> Option<Span> {
        match self {
            BodyUnsafety::Unsafe(span) => Some(span),
            BodyUnsafety::Safe => None,
        }
    }
}

#[derive(Clone, Copy, PartialEq)]
enum UnsafeOpKind {
    CallToUnsafeFunction,
    UseOfInlineAssembly,
    InitializingTypeWith,
    CastOfPointerToInt,
    UseOfMutableStatic,
    UseOfExternStatic,
    DerefOfRawPointer,
    #[allow(dead_code)] // FIXME
    AssignToDroppingUnionField,
    #[allow(dead_code)] // FIXME
    AccessToUnionField,
    #[allow(dead_code)] // FIXME
    MutationOfLayoutConstrainedField,
    #[allow(dead_code)] // FIXME
    BorrowOfLayoutConstrainedField,
    CallToFunctionWith,
}

use UnsafeOpKind::*;

impl UnsafeOpKind {
    pub fn description_and_note(&self) -> (&'static str, &'static str) {
        match self {
            CallToUnsafeFunction => (
                "call to unsafe function",
                "consult the function's documentation for information on how to avoid undefined \
                 behavior",
            ),
            UseOfInlineAssembly => (
                "use of inline assembly",
                "inline assembly is entirely unchecked and can cause undefined behavior",
            ),
            InitializingTypeWith => (
                "initializing type with `rustc_layout_scalar_valid_range` attr",
                "initializing a layout restricted type's field with a value outside the valid \
                 range is undefined behavior",
            ),
            CastOfPointerToInt => {
                ("cast of pointer to int", "casting pointers to integers in constants")
            }
            UseOfMutableStatic => (
                "use of mutable static",
                "mutable statics can be mutated by multiple threads: aliasing violations or data \
                 races will cause undefined behavior",
            ),
            UseOfExternStatic => (
                "use of extern static",
                "extern statics are not controlled by the Rust type system: invalid data, \
                 aliasing violations or data races will cause undefined behavior",
            ),
            DerefOfRawPointer => (
                "dereference of raw pointer",
                "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
                 and cause data races: all of these are undefined behavior",
            ),
            AssignToDroppingUnionField => (
                "assignment to union field that might need dropping",
                "the previous content of the field will be dropped, which causes undefined \
                 behavior if the field was not properly initialized",
            ),
            AccessToUnionField => (
                "access to union field",
                "the field may not be properly initialized: using uninitialized data will cause \
                 undefined behavior",
            ),
            MutationOfLayoutConstrainedField => (
                "mutation of layout constrained field",
                "mutating layout constrained fields cannot statically be checked for valid values",
            ),
            BorrowOfLayoutConstrainedField => (
                "borrow of layout constrained field with interior mutability",
                "references to fields of layout constrained fields lose the constraints. Coupled \
                 with interior mutability, the field can be changed to invalid values",
            ),
            CallToFunctionWith => (
                "call to function with `#[target_feature]`",
                "can only be called if the required target features are available",
            ),
        }
    }
}

// FIXME: checking unsafety for closures should be handled by their parent body,
// as they inherit their "safety context" from their declaration site.
pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
    // THIR unsafeck is gated under `-Z thir-unsafeck`
    if !tcx.sess.opts.debugging_opts.thir_unsafeck {
        return;
    }

    let (thir, expr) = tcx.thir_body(def);
    let thir = &thir.borrow();
    // If `thir` is empty, a type error occured, skip this body.
    if thir.exprs.is_empty() {
        return;
    }

    let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
    let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
        if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
            BodyUnsafety::Unsafe(fn_sig.span)
        } else {
            BodyUnsafety::Safe
        }
    });
    let body_target_features = &tcx.codegen_fn_attrs(def.did).target_features;
    let safety_context =
        if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
    let is_const = match tcx.hir().body_owner_kind(hir_id) {
        hir::BodyOwnerKind::Closure => false,
        hir::BodyOwnerKind::Fn => tcx.is_const_fn_raw(def.did.to_def_id()),
        hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => true,
    };
    let mut visitor = UnsafetyVisitor {
        tcx,
        thir,
        safety_context,
        hir_context: hir_id,
        body_unsafety,
        body_target_features,
        is_const,
    };
    visitor.visit_expr(&thir[expr]);
}

crate fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
    if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
        tcx.thir_check_unsafety_for_const_arg(def)
    } else {
        check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
    }
}

crate fn thir_check_unsafety_for_const_arg<'tcx>(
    tcx: TyCtxt<'tcx>,
    (did, param_did): (LocalDefId, DefId),
) {
    check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
}
<commit_msg>Make closures inherit their parent's "safety context"<commit_after>use crate::thir::visit::{self, Visitor};

use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_middle::thir::*;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
use rustc_session::lint::Level;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::symbol::Symbol;
use rustc_span::Span;

use std::ops::Bound;

struct UnsafetyVisitor<'a, 'tcx> {
    tcx: TyCtxt<'tcx>,
    thir: &'a Thir<'tcx>,
    /// The `HirId` of the current scope, which would be the `HirId`
    /// of the current HIR node, modulo adjustments. Used for lint levels.
    hir_context: hir::HirId,
    /// The current "safety context". This notably tracks whether we are in an
    /// `unsafe` block, and whether it has been used.
    safety_context: SafetyContext,
    body_unsafety: BodyUnsafety,
    /// The `#[target_feature]` attributes of the body. Used for checking
    /// calls to functions with `#[target_feature]` (RFC 2396).
    body_target_features: &'tcx Vec<Symbol>,
    is_const: bool,
}

impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
    fn in_safety_context<R>(
        &mut self,
        safety_context: SafetyContext,
        f: impl FnOnce(&mut Self) -> R,
    ) {
        if let (
            SafetyContext::UnsafeBlock { span: enclosing_span, .. },
            SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
        ) = (self.safety_context, safety_context)
        {
            self.warn_unused_unsafe(
                hir_id,
                block_span,
                Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")),
            );
            f(self);
        } else {
            let prev_context = self.safety_context;
            self.safety_context = safety_context;

            f(self);

            if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
                self.warn_unused_unsafe(
                    hir_id,
                    span,
                    if self.unsafe_op_in_unsafe_fn_allowed() {
                        self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn"))
                    } else {
                        None
                    },
                );
            }
            self.safety_context = prev_context;
            return;
        }
    }

    fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
        let (description, note) = kind.description_and_note();
        let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
        match self.safety_context {
            SafetyContext::UnsafeBlock { ref mut used, .. } => {
                if !self.body_unsafety.is_unsafe() || !unsafe_op_in_unsafe_fn_allowed {
                    // Mark this block as useful
                    *used = true;
                }
            }
            SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
            SafetyContext::UnsafeFn => {
                // unsafe_op_in_unsafe_fn is disallowed
                self.tcx.struct_span_lint_hir(
                    UNSAFE_OP_IN_UNSAFE_FN,
                    self.hir_context,
                    span,
                    |lint| {
                        lint.build(&format!(
                            "{} is unsafe and requires unsafe block (error E0133)",
                            description,
                        ))
                        .span_label(span, description)
                        .note(note)
                        .emit();
                    },
                )
            }
            SafetyContext::Safe => {
                let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
                struct_span_err!(
                    self.tcx.sess,
                    span,
                    E0133,
                    "{} is unsafe and requires unsafe{} block",
                    description,
                    fn_sugg,
                )
                .span_label(span, description)
                .note(note)
                .emit();
            }
        }
    }

    fn warn_unused_unsafe(
        &self,
        hir_id: hir::HirId,
        block_span: Span,
        enclosing_unsafe: Option<(Span, &'static str)>,
    ) {
        let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
        self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, |lint| {
            let msg = "unnecessary `unsafe` block";
            let mut db = lint.build(msg);
            db.span_label(block_span, msg);
            if let Some((span, kind)) = enclosing_unsafe {
                db.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
            }
            db.emit();
        });
    }

    /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
    fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
    }
}

impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
    fn thir(&self) -> &'a Thir<'tcx> {
        &self.thir
    }

    fn visit_block(&mut self, block: &Block) {
        if let BlockSafety::ExplicitUnsafe(hir_id) = block.safety_mode {
            self.in_safety_context(
                SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
                |this| visit::walk_block(this, block),
            );
        } else {
            visit::walk_block(self, block);
        }
    }

    fn visit_expr(&mut self, expr: &Expr<'tcx>) {
        match expr.kind {
            ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
                let prev_id = self.hir_context;
                self.hir_context = hir_id;
                self.visit_expr(&self.thir[value]);
                self.hir_context = prev_id;
                return;
            }
            ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
                if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
                    self.requires_unsafe(expr.span, CallToUnsafeFunction);
                } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
                    // If the called function has target features the calling function hasn't,
                    // the call requires `unsafe`.
                    if !self
                        .tcx
                        .codegen_fn_attrs(func_did)
                        .target_features
                        .iter()
                        .all(|feature| self.body_target_features.contains(feature))
                    {
                        self.requires_unsafe(expr.span, CallToFunctionWith);
                    }
                }
            }
            ExprKind::Deref { arg } => {
                if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
                    if self.tcx.is_mutable_static(def_id) {
                        self.requires_unsafe(expr.span, UseOfMutableStatic);
                    } else if self.tcx.is_foreign_item(def_id) {
                        self.requires_unsafe(expr.span, UseOfExternStatic);
                    }
                } else if self.thir[arg].ty.is_unsafe_ptr() {
                    self.requires_unsafe(expr.span, DerefOfRawPointer);
                }
            }
            ExprKind::InlineAsm { .. } | ExprKind::LlvmInlineAsm { .. } => {
                self.requires_unsafe(expr.span, UseOfInlineAssembly);
            }
            ExprKind::Adt {
                adt_def,
                variant_index: _,
                substs: _,
                user_ty: _,
                fields: _,
                base: _,
            } => match self.tcx.layout_scalar_valid_range(adt_def.did) {
                (Bound::Unbounded, Bound::Unbounded) => {}
                _ => self.requires_unsafe(expr.span, InitializingTypeWith),
            },
            ExprKind::Cast { source } => {
                let source = &self.thir[source];
                if self.tcx.features().const_raw_ptr_to_usize_cast
                    && self.is_const
                    && (source.ty.is_unsafe_ptr() || source.ty.is_fn_ptr())
                    && expr.ty.is_integral()
                {
                    self.requires_unsafe(expr.span, CastOfPointerToInt);
                }
            }
            ExprKind::Closure {
                closure_id,
                substs: _,
                upvars: _,
                movability: _,
                fake_reads: _,
            } => {
                let closure_id = closure_id.expect_local();
                let closure_def = if let Some((did, const_param_id)) =
                    ty::WithOptConstParam::try_lookup(closure_id, self.tcx)
                {
                    ty::WithOptConstParam { did, const_param_did: Some(const_param_id) }
                } else {
                    ty::WithOptConstParam::unknown(closure_id)
                };
                let (closure_thir, expr) = self.tcx.thir_body(closure_def);
                let closure_thir = &closure_thir.borrow();
                let hir_context = self.tcx.hir().local_def_id_to_hir_id(closure_id);
                let mut closure_visitor =
                    UnsafetyVisitor { thir: closure_thir, hir_context, ..*self };
                closure_visitor.visit_expr(&closure_thir[expr]);
                // Unsafe blocks can be used in closures, make sure to take it into account
                self.safety_context = closure_visitor.safety_context;
            }
            _ => {}
        }

        visit::walk_expr(self, expr);
    }
}

#[derive(Clone, Copy)]
enum SafetyContext {
    Safe,
    UnsafeFn,
    UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
}

#[derive(Clone, Copy)]
enum BodyUnsafety {
    /// The body is not unsafe.
    Safe,
    /// The body is an unsafe function. The span points to
    /// the signature of the function.
    Unsafe(Span),
}

impl BodyUnsafety {
    /// Returns whether the body is unsafe.
    fn is_unsafe(&self) -> bool {
        matches!(self, BodyUnsafety::Unsafe(_))
    }

    /// If the body is unsafe, returns the `Span` of its signature.
    fn unsafe_fn_sig_span(self) -> Option<Span> {
        match self {
            BodyUnsafety::Unsafe(span) => Some(span),
            BodyUnsafety::Safe => None,
        }
    }
}

#[derive(Clone, Copy, PartialEq)]
enum UnsafeOpKind {
    CallToUnsafeFunction,
    UseOfInlineAssembly,
    InitializingTypeWith,
    CastOfPointerToInt,
    UseOfMutableStatic,
    UseOfExternStatic,
    DerefOfRawPointer,
    #[allow(dead_code)] // FIXME
    AssignToDroppingUnionField,
    #[allow(dead_code)] // FIXME
    AccessToUnionField,
    #[allow(dead_code)] // FIXME
    MutationOfLayoutConstrainedField,
    #[allow(dead_code)] // FIXME
    BorrowOfLayoutConstrainedField,
    CallToFunctionWith,
}

use UnsafeOpKind::*;

impl UnsafeOpKind {
    pub fn description_and_note(&self) -> (&'static str, &'static str) {
        match self {
            CallToUnsafeFunction => (
                "call to unsafe function",
                "consult the function's documentation for information on how to avoid undefined \
                 behavior",
            ),
            UseOfInlineAssembly => (
                "use of inline assembly",
                "inline assembly is entirely unchecked and can cause undefined behavior",
            ),
            InitializingTypeWith => (
                "initializing type with `rustc_layout_scalar_valid_range` attr",
                "initializing a layout restricted type's field with a value outside the valid \
                 range is undefined behavior",
            ),
            CastOfPointerToInt => {
                ("cast of pointer to int", "casting pointers to integers in constants")
            }
            UseOfMutableStatic => (
                "use of mutable static",
                "mutable statics can be mutated by multiple threads: aliasing violations or data \
                 races will cause undefined behavior",
            ),
            UseOfExternStatic => (
                "use of extern static",
                "extern statics are not controlled by the Rust type system: invalid data, \
                 aliasing violations or data races will cause undefined behavior",
            ),
            DerefOfRawPointer => (
                "dereference of raw pointer",
                "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
                 and cause data races: all of these are undefined behavior",
            ),
            AssignToDroppingUnionField => (
                "assignment to union field that might need dropping",
                "the previous content of the field will be dropped, which causes undefined \
                 behavior if the field was not properly initialized",
            ),
            AccessToUnionField => (
                "access to union field",
                "the field may not be properly initialized: using uninitialized data will cause \
                 undefined behavior",
            ),
            MutationOfLayoutConstrainedField => (
                "mutation of layout constrained field",
                "mutating layout constrained fields cannot statically be checked for valid values",
            ),
            BorrowOfLayoutConstrainedField => (
                "borrow of layout constrained field with interior mutability",
                "references to fields of layout constrained fields lose the constraints. Coupled \
                 with interior mutability, the field can be changed to invalid values",
            ),
            CallToFunctionWith => (
                "call to function with `#[target_feature]`",
                "can only be called if the required target features are available",
            ),
        }
    }
}

pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
    // THIR unsafeck is gated under `-Z thir-unsafeck`
    if !tcx.sess.opts.debugging_opts.thir_unsafeck {
        return;
    }

    // Closures are handled by their parent function
    if tcx.is_closure(def.did.to_def_id()) {
        tcx.ensure().thir_check_unsafety(tcx.hir().local_def_id_to_hir_id(def.did).owner);
        return;
    }

    let (thir, expr) = tcx.thir_body(def);
    let thir = &thir.borrow();
    // If `thir` is empty, a type error occured, skip this body.
    if thir.exprs.is_empty() {
        return;
    }

    let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
    let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
        if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
            BodyUnsafety::Unsafe(fn_sig.span)
        } else {
            BodyUnsafety::Safe
        }
    });
    let body_target_features = &tcx.codegen_fn_attrs(def.did).target_features;
    let safety_context =
        if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
    let is_const = match tcx.hir().body_owner_kind(hir_id) {
        hir::BodyOwnerKind::Closure => false,
        hir::BodyOwnerKind::Fn => tcx.is_const_fn_raw(def.did.to_def_id()),
        hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => true,
    };
    let mut visitor = UnsafetyVisitor {
        tcx,
        thir,
        safety_context,
        hir_context: hir_id,
        body_unsafety,
        body_target_features,
        is_const,
    };
    visitor.visit_expr(&thir[expr]);
}

crate fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
    if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
        tcx.thir_check_unsafety_for_const_arg(def)
    } else {
        check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
    }
}

crate fn thir_check_unsafety_for_const_arg<'tcx>(
    tcx: TyCtxt<'tcx>,
    (did, param_did): (LocalDefId, DefId),
) {
    check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
}
<|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 https://mozilla.org/MPL/2.0/. */

use app_units::Au;
use core_foundation::array::CFArray;
use core_foundation::base::{CFType, TCFType};
use core_foundation::dictionary::CFDictionary;
use core_foundation::number::CFNumber;
use core_foundation::string::CFString;
use core_graphics::data_provider::CGDataProvider;
use core_graphics::font::CGFont;
use core_text::font::CTFont;
use core_text::font_collection;
use core_text::font_descriptor;
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use servo_atoms::Atom;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::fmt;
use std::fs::{self, File};
use std::io::{Error as IoError, Read};
use std::ops::Deref;
use std::path::Path;
use std::sync::{Arc, Mutex};
use webrender_api::NativeFontHandle;

/// Platform specific font representation for mac.
/// The identifier is a PostScript font name. The
/// CTFont object is cached here for use by the
/// paint functions that create CGFont references.
#[derive(Deserialize, Serialize)]
pub struct FontTemplateData {
    // If you add members here, review the Debug impl below
    /// The `CTFont` object, if present. This is cached here so that we don't have to keep creating
    /// `CTFont` instances over and over. It can always be recreated from the `identifier` and/or
    /// `font_data` fields.
    ///
    /// When sending a `FontTemplateData` instance across processes, this will be cleared out on
    /// the other side, because `CTFont` instances cannot be sent across processes. This is
    /// harmless, however, because it can always be recreated.
    ctfont: CachedCTFont,

    pub identifier: Atom,
    pub font_data: Option<Arc<Vec<u8>>>,
}

impl fmt::Debug for FontTemplateData {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("FontTemplateData")
            .field("ctfont", &self.ctfont)
            .field("identifier", &self.identifier)
            .field(
                "font_data",
                &self
                    .font_data
                    .as_ref()
                    .map(|bytes| format!("[{} bytes]", bytes.len())),
            )
            .finish()
    }
}

unsafe impl Send for FontTemplateData {}
unsafe impl Sync for FontTemplateData {}

impl FontTemplateData {
    pub fn new(identifier: Atom, font_data: Option<Vec<u8>>) -> Result<FontTemplateData, IoError> {
        Ok(FontTemplateData {
            ctfont: CachedCTFont(Mutex::new(HashMap::new())),
            identifier: identifier.to_owned(),
            font_data: font_data.map(Arc::new),
        })
    }

    /// Retrieves the Core Text font instance, instantiating it if necessary.
    pub fn ctfont(&self, pt_size: f64) -> Option<CTFont> {
        let mut ctfonts = self.ctfont.lock().unwrap();
        let pt_size_key = Au::from_f64_px(pt_size);
        if !ctfonts.contains_key(&pt_size_key) {
            // If you pass a zero font size to one of the Core Text APIs, it'll replace it with
            // 12.0. We don't want that! (Issue #10492.)
            let clamped_pt_size = pt_size.max(0.01);
            let ctfont = match self.font_data {
                Some(ref bytes) => {
                    let fontprov = CGDataProvider::from_buffer(bytes.clone());
                    let cgfont_result = CGFont::from_data_provider(fontprov);
                    match cgfont_result {
                        Ok(cgfont) => {
                            Some(core_text::font::new_from_CGFont(&cgfont, clamped_pt_size))
                        },
                        Err(_) => None,
                    }
                },
                None => {
                    // We can't rely on Core Text to load a font for us by postscript
                    // name here, due to https://github.com/servo/servo/issues/23290.
                    // The APIs will randomly load the wrong font, forcing us to use
                    // the roundabout route of creating a Core Graphics font from a
                    // a set of descriptors and then creating a Core Text font from
                    // that one.

                    let attributes: CFDictionary<CFString, CFType> =
                        CFDictionary::from_CFType_pairs(&[
                            (
                                CFString::new("NSFontNameAttribute"),
                                CFString::new(&*self.identifier).as_CFType(),
                            ),
                        ]);

                    let descriptor = font_descriptor::new_from_attributes(&attributes);
                    let descriptors = CFArray::from_CFTypes(&[descriptor]);
                    let collection = font_collection::new_from_descriptors(&descriptors);
                    collection.get_descriptors().and_then(|descriptors| {
                        let descriptor = descriptors.get(0).unwrap();
                        let font_path = Path::new(&descriptor.font_path().unwrap()).to_owned();
                        fs::read(&font_path).ok().and_then(|bytes| {
                            let fontprov = CGDataProvider::from_buffer(Arc::new(bytes));
                            CGFont::from_data_provider(fontprov).ok().map(|cgfont| {
                                core_text::font::new_from_CGFont(&cgfont, clamped_pt_size)
                            })
                        })
                    })
                },
            };
            if let Some(ctfont) = ctfont {
                ctfonts.insert(pt_size_key, ctfont);
            }
        }
        ctfonts.get(&pt_size_key).map(|ctfont| (*ctfont).clone())
    }

    /// Returns a clone of the data in this font. This may be a hugely expensive
    /// operation (depending on the platform) which performs synchronous disk I/O
    /// and should never be done lightly.
    pub fn bytes(&self) -> Vec<u8> {
        if let Some(font_data) = self.bytes_if_in_memory() {
            return font_data;
        }

        let path = ServoUrl::parse(
            &*self
                .ctfont(0.0)
                .expect("No Core Text font available!")
                .url()
                .expect("No URL for Core Text font!")
                .get_string()
                .to_string(),
        )
        .expect("Couldn't parse Core Text font URL!")
        .as_url()
        .to_file_path()
        .expect("Core Text font didn't name a path!");
        let mut bytes = Vec::new();
        File::open(path)
            .expect("Couldn't open font file!")
            .read_to_end(&mut bytes)
            .unwrap();
        bytes
    }

    /// Returns a clone of the bytes in this font if they are in memory. This function never
    /// performs disk I/O.
    pub fn bytes_if_in_memory(&self) -> Option<Vec<u8>> {
        self.font_data.as_ref().map(|bytes| (**bytes).clone())
    }

    /// Returns the native font that underlies this font template, if applicable.
    pub fn native_font(&self) -> Option<NativeFontHandle> {
        self.ctfont(0.0)
            .map(|ctfont| NativeFontHandle(ctfont.copy_to_CGFont()))
    }
}

#[derive(Debug)]
pub struct CachedCTFont(Mutex<HashMap<Au, CTFont>>);

impl Deref for CachedCTFont {
    type Target = Mutex<HashMap<Au, CTFont>>;
    fn deref(&self) -> &Mutex<HashMap<Au, CTFont>> {
        &self.0
    }
}

impl Serialize for CachedCTFont {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_none()
    }
}

impl<'de> Deserialize<'de> for CachedCTFont {
    fn deserialize<D>(deserializer: D) -> Result<CachedCTFont, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct NoneOptionVisitor;

        impl<'de> Visitor<'de> for NoneOptionVisitor {
            type Value = CachedCTFont;

            fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                write!(fmt, "none")
            }

            #[inline]
            fn visit_none<E>(self) -> Result<CachedCTFont, E>
            where
                E: Error,
            {
                Ok(CachedCTFont(Mutex::new(HashMap::new())))
            }
        }

        deserializer.deserialize_option(NoneOptionVisitor)
    }
}
<commit_msg>rustfmt<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 https://mozilla.org/MPL/2.0/. */

use app_units::Au;
use core_foundation::array::CFArray;
use core_foundation::base::{CFType, TCFType};
use core_foundation::dictionary::CFDictionary;
use core_foundation::string::CFString;
use core_graphics::data_provider::CGDataProvider;
use core_graphics::font::CGFont;
use core_text::font::CTFont;
use core_text::font_collection;
use core_text::font_descriptor;
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use servo_atoms::Atom;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::fmt;
use std::fs::{self, File};
use std::io::{Error as IoError, Read};
use std::ops::Deref;
use std::path::Path;
use std::sync::{Arc, Mutex};
use webrender_api::NativeFontHandle;

/// Platform specific font representation for mac.
/// The identifier is a PostScript font name. The
/// CTFont object is cached here for use by the
/// paint functions that create CGFont references.
#[derive(Deserialize, Serialize)]
pub struct FontTemplateData {
    // If you add members here, review the Debug impl below
    /// The `CTFont` object, if present. This is cached here so that we don't have to keep creating
    /// `CTFont` instances over and over. It can always be recreated from the `identifier` and/or
    /// `font_data` fields.
    ///
    /// When sending a `FontTemplateData` instance across processes, this will be cleared out on
    /// the other side, because `CTFont` instances cannot be sent across processes. This is
    /// harmless, however, because it can always be recreated.
    ctfont: CachedCTFont,

    pub identifier: Atom,
    pub font_data: Option<Arc<Vec<u8>>>,
}

impl fmt::Debug for FontTemplateData {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("FontTemplateData")
            .field("ctfont", &self.ctfont)
            .field("identifier", &self.identifier)
            .field(
                "font_data",
                &self
                    .font_data
                    .as_ref()
                    .map(|bytes| format!("[{} bytes]", bytes.len())),
            )
            .finish()
    }
}

unsafe impl Send for FontTemplateData {}
unsafe impl Sync for FontTemplateData {}

impl FontTemplateData {
    pub fn new(identifier: Atom, font_data: Option<Vec<u8>>) -> Result<FontTemplateData, IoError> {
        Ok(FontTemplateData {
            ctfont: CachedCTFont(Mutex::new(HashMap::new())),
            identifier: identifier.to_owned(),
            font_data: font_data.map(Arc::new),
        })
    }

    /// Retrieves the Core Text font instance, instantiating it if necessary.
    pub fn ctfont(&self, pt_size: f64) -> Option<CTFont> {
        let mut ctfonts = self.ctfont.lock().unwrap();
        let pt_size_key = Au::from_f64_px(pt_size);
        if !ctfonts.contains_key(&pt_size_key) {
            // If you pass a zero font size to one of the Core Text APIs, it'll replace it with
            // 12.0. We don't want that! (Issue #10492.)
            let clamped_pt_size = pt_size.max(0.01);
            let ctfont = match self.font_data {
                Some(ref bytes) => {
                    let fontprov = CGDataProvider::from_buffer(bytes.clone());
                    let cgfont_result = CGFont::from_data_provider(fontprov);
                    match cgfont_result {
                        Ok(cgfont) => {
                            Some(core_text::font::new_from_CGFont(&cgfont, clamped_pt_size))
                        },
                        Err(_) => None,
                    }
                },
                None => {
                    // We can't rely on Core Text to load a font for us by postscript
                    // name here, due to https://github.com/servo/servo/issues/23290.
                    // The APIs will randomly load the wrong font, forcing us to use
                    // the roundabout route of creating a Core Graphics font from a
                    // a set of descriptors and then creating a Core Text font from
                    // that one.

                    let attributes: CFDictionary<CFString, CFType> =
                        CFDictionary::from_CFType_pairs(&[(
                            CFString::new("NSFontNameAttribute"),
                            CFString::new(&*self.identifier).as_CFType(),
                        )]);

                    let descriptor = font_descriptor::new_from_attributes(&attributes);
                    let descriptors = CFArray::from_CFTypes(&[descriptor]);
                    let collection = font_collection::new_from_descriptors(&descriptors);
                    collection.get_descriptors().and_then(|descriptors| {
                        let descriptor = descriptors.get(0).unwrap();
                        let font_path = Path::new(&descriptor.font_path().unwrap()).to_owned();
                        fs::read(&font_path).ok().and_then(|bytes| {
                            let fontprov = CGDataProvider::from_buffer(Arc::new(bytes));
                            CGFont::from_data_provider(fontprov).ok().map(|cgfont| {
                                core_text::font::new_from_CGFont(&cgfont, clamped_pt_size)
                            })
                        })
                    })
                },
            };
            if let Some(ctfont) = ctfont {
                ctfonts.insert(pt_size_key, ctfont);
            }
        }
        ctfonts.get(&pt_size_key).map(|ctfont| (*ctfont).clone())
    }

    /// Returns a clone of the data in this font. This may be a hugely expensive
    /// operation (depending on the platform) which performs synchronous disk I/O
    /// and should never be done lightly.
    pub fn bytes(&self) -> Vec<u8> {
        if let Some(font_data) = self.bytes_if_in_memory() {
            return font_data;
        }

        let path = ServoUrl::parse(
            &*self
                .ctfont(0.0)
                .expect("No Core Text font available!")
                .url()
                .expect("No URL for Core Text font!")
                .get_string()
                .to_string(),
        )
        .expect("Couldn't parse Core Text font URL!")
        .as_url()
        .to_file_path()
        .expect("Core Text font didn't name a path!");
        let mut bytes = Vec::new();
        File::open(path)
            .expect("Couldn't open font file!")
            .read_to_end(&mut bytes)
            .unwrap();
        bytes
    }

    /// Returns a clone of the bytes in this font if they are in memory. This function never
    /// performs disk I/O.
    pub fn bytes_if_in_memory(&self) -> Option<Vec<u8>> {
        self.font_data.as_ref().map(|bytes| (**bytes).clone())
    }

    /// Returns the native font that underlies this font template, if applicable.
    pub fn native_font(&self) -> Option<NativeFontHandle> {
        self.ctfont(0.0)
            .map(|ctfont| NativeFontHandle(ctfont.copy_to_CGFont()))
    }
}

#[derive(Debug)]
pub struct CachedCTFont(Mutex<HashMap<Au, CTFont>>);

impl Deref for CachedCTFont {
    type Target = Mutex<HashMap<Au, CTFont>>;
    fn deref(&self) -> &Mutex<HashMap<Au, CTFont>> {
        &self.0
    }
}

impl Serialize for CachedCTFont {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_none()
    }
}

impl<'de> Deserialize<'de> for CachedCTFont {
    fn deserialize<D>(deserializer: D) -> Result<CachedCTFont, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct NoneOptionVisitor;

        impl<'de> Visitor<'de> for NoneOptionVisitor {
            type Value = CachedCTFont;

            fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                write!(fmt, "none")
            }

            #[inline]
            fn visit_none<E>(self) -> Result<CachedCTFont, E>
            where
                E: Error,
            {
                Ok(CachedCTFont(Mutex::new(HashMap::new())))
            }
        }

        deserializer.deserialize_option(NoneOptionVisitor)
    }
}
<|endoftext|>


<commit_before>/* kernel::sgash.rs */

use core::*;
use core::str::*;
use core::option::{Some, Option, None}; // Match statement
use core::iter::Iterator;
use core::vec::Vec;
use core::mem::Allocator;
use kernel::*;
use super::super::platform::*;

pub static mut buffer: cstr = cstr {
				p: 0 as *mut u8,
				p_cstr_i: 0,
				max: 0
			      };
pub fn putchar(key: char) {
    unsafe {
	/*
	 * We need to include a blank asm call to prevent rustc
	 * from optimizing this part out
	 */
	asm!("");
	io::write_char(key, io::UART0);
    }
}

#[lang = "exchange_free"]
fn putstr(msg: &str) {
    for c in slice::iter(as_bytes(msg)) {
	putchar(*c as char);
    }
}

pub unsafe fn putcstr(s: cstr)
{
    let mut p = s.p as uint;
    while *(p as *char) != '\0'
    {
	putchar(*(p as *char));
	p += 1;
    }
}

pub unsafe fn parsekey(x: char) {
    let x = x as u8;
    // Set this to false to learn the keycodes of various keys!
    // Key codes are printed backwards because life is hard
    if (true) {
	match x {
	    13		=>	{ prompt(); }
	    127		=>	{
		if (buffer.delete_char()) {
		    putchar('');
		    putchar(' ');
		    putchar('');
		}
	    }
	    _		=>	{
		if (buffer.add_char(x)) { putchar(x as char); }
	    }
	}
    }
    else {
	keycode(x);
    }
}

fn keycode(x: u8) {
    let mut x = x;
    while ( x != 0 ) {
	putchar((x%10+ ('0' as u8) ) as char);
	x = x/10;
    }
    putchar(' ');
}
fn screen() {

	putstr(&"\n                                                               ");
	putstr(&"\n                                                               ");
	putstr(&"\n                       7=..~$=..:7                             ");
	putstr(&"\n                  +$: =$$$+$$$?$$$+ ,7?                        ");
	putstr(&"\n                  $$$$$$$$$$$$$$$$$$Z$$                        ");
	putstr(&"\n              7$$$$$$$$$$$$. .Z$$$$$Z$$$$$$                    ");
	putstr(&"\n           ~..7$$Z$$$$$7+7$+.?Z7=7$$Z$$Z$$$..:                 ");
	putstr(&"\n          ~$$$$$$$$7:     :ZZZ,     :7ZZZZ$$$$=                ");
	putstr(&"\n           Z$$$$$?                    .+ZZZZ$$                 ");
	putstr(&"\n       +$ZZ$$$Z7                         7ZZZ$Z$$I.            ");
	putstr(&"\n        $$$$ZZZZZZZZZZZZZZZZZZZZZZZZI,    ,ZZZ$$Z              ");
	putstr(&"\n      :+$$$$ZZZZZZZZZZZZZZZZZZZZZZZZZZZ=    $ZZ$$+~,           ");
	putstr(&" _                     _     _                         _  ");
	putstr(&"\n     ?$Z$$$$ZZZZZZZZZZZZZZZZZZZZZZZZZZZZI   7ZZZ$ZZI           ");
	putstr(&"| |                   (_)   | |                       | | ");
	putstr(&"\n      =Z$$+7Z$$7ZZZZZZZZ$$$$$$$ZZZZZZZZZZ  ~Z$?$ZZ?            ");
	putstr(&"| | ____ ___  ____     _____| |_____  ____ ____  _____| | ");
	putstr(&"\n    :$Z$Z...$Z  $ZZZZZZZ~       ~ZZZZZZZZ,.ZZ...Z$Z$~          ");
	putstr(&"| |/ ___) _ \\|  _ \\   |  _   _) ___ |/ ___)  _ \\| ___ | | ");
	putstr(&"\n    7ZZZZZI$ZZ  $ZZZZZZZ~       =ZZZZZZZ7..ZZ$?$ZZZZ$          ");
	putstr(&"| | |  | |_| | | | |  | |  \\ \\| ____| |   | | | | ____| | ");
	putstr(&"\n      ZZZZ$:    $ZZZZZZZZZZZZZZZZZZZZZZ=     ~$ZZZ$:           ");
	putstr(&"|_|_|  \\____/|_| |_|  |_|   \\_\\_____)_|   |_| |_|_____)__)");
	putstr(&"\n    7Z$ZZ$,     $ZZZZZZZZZZZZZZZZZZZZ7         ZZZ$Z$          ");
	putstr(&"\n   =ZZZZZZ,     $ZZZZZZZZZZZZZZZZZZZZZZ,       ZZZ$ZZ+         ");
	putstr(&"\n     ,ZZZZ,     $ZZZZZZZ:     =ZZZZZZZZZ     ZZZZZ$:           ");
	putstr(&"\n    =$ZZZZ+     ZZZZZZZZ~       ZZZZZZZZ~   =ZZZZZZZI          ");
	putstr(&"\n    $ZZ$ZZZ$$Z$$ZZZZZZZZZ$$$$   IZZZZZZZZZ$ZZZZZZZZZ$          ");
	putstr(&"\n      :ZZZZZZZZZZZZZZZZZZZZZZ   ~ZZZZZZZZZZZZZZZZZ~            ");
	putstr(&"\n     ,Z$$ZZZZZZZZZZZZZZZZZZZZ    ZZZZZZZZZZZZZZZZZZ~           ");
	putstr(&"\n     =$ZZZZZZZZZZZZZZZZZZZZZZ     $ZZZZZZZZZZZZZZZ$+           ");
	putstr(&"\n        IZZZZZ:.                        . ,ZZZZZ$              ");
	putstr(&"\n       ~$ZZZZZZZZZZZ                 ZZZZ$ZZZZZZZ+             ");
	putstr(&"\n           Z$ZZZ. ,Z~               =Z:.,ZZZ$Z                 ");
	putstr(&"\n          ,ZZZZZ..~Z$.             .7Z:..ZZZZZ:                ");
	putstr(&"\n          ~7+:$ZZZZZZZZI=:.   .,=IZZZZZZZ$Z:=7=                ");
	putstr(&"\n              $$ZZZZZZZZZZZZZZZZZZZZZZ$ZZZZ                    ");
	putstr(&"\n              ==..$ZZZ$ZZZZZZZZZZZ$ZZZZ .~+                    ");
	putstr(&"\n                  I$?.?ZZZ$ZZZ$ZZZI =$7                        ");
	putstr(&"\n                       $7..I$7..I$,                            ");
	putstr(&"\n");
	putstr(&"\n");
}

pub unsafe fn init() {
    buffer = cstr::new(256);
    screen();
    prompt();
}

unsafe fn prompt() {
    if (buffer.streq(&"ls")) { putstr( &"\na\tb") };
    match buffer.getarg(' ', 0) {
	Some(y)	=> {
	    if(y.streq(&"cat")) {
		match buffer.getarg(' ', 1) {
		    Some(x)	=> {
			if(x.streq(&"a")) { putstr( &"\nHello"); }
			if(x.streq(&"b")) { putstr( &"\nworld!"); }
		    }
		    None	=> { }
		};
	    }
	}
	None	=> { }
    };

    putstr(&"\nsgash > ");
    buffer.reset();
}

/* BUFFER MODIFICATION FUNCTIONS */

struct cstr {
    p: *mut u8,
    p_cstr_i: uint,
    max: uint
}

impl cstr {
    pub unsafe fn new(size: uint) -> cstr {
	// Sometimes this doesn't allocate enough memory and gets stuck...
	let (x, y) = memory::allocator.alloc(size);
	let this = cstr {
	    p: x,
	    p_cstr_i: 0,
	    max: y
	};
	*(((this.p as uint)+this.p_cstr_i) as *mut char) = '\0';
	this
    }

    unsafe fn from_str(s: &str) -> cstr {
	let mut this = cstr::new(256);
	for c in slice::iter(as_bytes(s)) {
	    this.add_char(*c);
	};
	this
    }

    fn len(&self) -> uint { self.p_cstr_i }

    // HELP THIS DOESN'T WORK THERE IS NO GARBAGE COLLECTION!!!
    unsafe fn destroy(&self) { memory::allocator.free(self.p); }

    unsafe fn add_char(&mut self, x: u8) -> bool{
	if (self.p_cstr_i == self.max) { return false; }
	*(((self.p as uint)+self.p_cstr_i) as *mut u8) = x;
	self.p_cstr_i += 1;
	*(((self.p as uint)+self.p_cstr_i) as *mut char) = '\0';
	true
    }

    unsafe fn delete_char(&mut self) -> bool {
	if (self.p_cstr_i == 0) { return false; }
	self.p_cstr_i -= 1;
	*(((self.p as uint)+self.p_cstr_i) as *mut char) = '\0';
	true
    }

    unsafe fn reset(&mut self) {
	self.p_cstr_i = 0;
	*(self.p as *mut char) = '\0';
    }

    unsafe fn eq(&self, other: &cstr) -> bool {
	if (self.len() != other.len()) { return false; }
	else {
	    let mut x = 0;
	    let mut selfp: uint = self.p as uint;
	    let mut otherp: uint = other.p as uint;
	    while (x < self.len()) {
		if (*(selfp as *char) != *(otherp as *char)) { return false; }
		selfp += 1;
		otherp += 1;
		x += 1;
	    }
	    true
	}
    }

    unsafe fn streq(&self, other: &str) -> bool {
	let mut x = 0;
	let mut selfp: uint = self.p as uint;
	for c in slice::iter(as_bytes(other)) {
	    if( *c != *(selfp as *u8) ) { return false; }
	    selfp += 1;
	};
	*(selfp as *char) == '\0'
    }

    unsafe fn getarg(&self, delim: char, mut k: uint) -> Option<cstr> {
	let mut ind: uint = 0;
	let mut found = k == 0;
	let mut selfp: uint = self.p as uint;
	let mut s = cstr::new(256);
	loop {
	    if (*(selfp as *char) == '\0') {
		// End of string
		if (found) { return Some(s); }
		else { return None; }
	    };
	    if (*(selfp as *u8) == delim as u8) {
		if (found) { return Some(s); }
		k -= 1;
	    };
	    if (found) {
		s.add_char(*(selfp as *u8));
	    };
	    found = k == 0;
	    selfp += 1;
	    ind += 1;
	    if (ind == self.max) {
		putstr(&"\nSomething broke!");
		return None;
	    }
	}
    }

    unsafe fn split(&self, delim: char) -> (cstr, cstr) {
	let mut selfp: uint = self.p as uint;
	let mut beg = cstr::new(256);
	let mut end = cstr::new(256);
	let mut found = false;
	loop {
	    if (*(selfp as *char) == '\0') {
		return (beg, end);
	    }
	    else if (*(selfp as *u8) == delim as u8) {
		found = true;
	    }
	    else if (!found) {
		beg.add_char(*(selfp as *u8));
	    }
	    else if (found) {
		end.add_char(*(selfp as *u8));
	    };
	    selfp += 1;
	}
    }

}

<commit_msg>For those running on 80 char terminals<commit_after>/* kernel::sgash.rs */

use core::*;
use core::str::*;
use core::option::{Some, Option, None}; // Match statement
use core::iter::Iterator;
use core::vec::Vec;
use core::mem::Allocator;
use kernel::*;
use super::super::platform::*;

pub static mut buffer: cstr = cstr {
				p: 0 as *mut u8,
				p_cstr_i: 0,
				max: 0
			      };
pub fn putchar(key: char) {
    unsafe {
	/*
	 * We need to include a blank asm call to prevent rustc
	 * from optimizing this part out
	 */
	asm!("");
	io::write_char(key, io::UART0);
    }
}

#[lang = "exchange_free"]
fn putstr(msg: &str) {
    for c in slice::iter(as_bytes(msg)) {
	putchar(*c as char);
    }
}

pub unsafe fn putcstr(s: cstr)
{
    let mut p = s.p as uint;
    while *(p as *char) != '\0'
    {
	putchar(*(p as *char));
	p += 1;
    }
}

pub unsafe fn parsekey(x: char) {
    let x = x as u8;
    // Set this to false to learn the keycodes of various keys!
    // Key codes are printed backwards because life is hard
    if (true) {
	match x {
	    13		=>	{ prompt(); }
	    127		=>	{
		if (buffer.delete_char()) {
		    putchar('');
		    putchar(' ');
		    putchar('');
		}
	    }
	    _		=>	{
		if (buffer.add_char(x)) { putchar(x as char); }
	    }
	}
    }
    else {
	keycode(x);
    }
}

fn keycode(x: u8) {
    let mut x = x;
    while ( x != 0 ) {
	putchar((x%10+ ('0' as u8) ) as char);
	x = x/10;
    }
    putchar(' ');
}
fn screen() {

	putstr(&"\n                                                               ");
	putstr(&"\n                                                               ");
	putstr(&"\n                       7=..~$=..:7                             ");
	putstr(&"\n                  +$: =$$$+$$$?$$$+ ,7?                        ");
	putstr(&"\n                  $$$$$$$$$$$$$$$$$$Z$$                        ");
	putstr(&"\n              7$$$$$$$$$$$$. .Z$$$$$Z$$$$$$                    ");
	putstr(&"\n           ~..7$$Z$$$$$7+7$+.?Z7=7$$Z$$Z$$$..:                 ");
	putstr(&"\n          ~$$$$$$$$7:     :ZZZ,     :7ZZZZ$$$$=                ");
	putstr(&"\n           Z$$$$$?                    .+ZZZZ$$                 ");
	putstr(&"\n       +$ZZ$$$Z7                         7ZZZ$Z$$I.            ");
	putstr(&"\n        $$$$ZZZZZZZZZZZZZZZZZZZZZZZZI,    ,ZZZ$$Z              ");
	putstr(&"\n      :+$$$$ZZZZZZZZZZZZZZZZZZZZZZZZZZZ=    $ZZ$$+~,           ");
	putstr(&"\n     ?$Z$$$$ZZZZZZZZZZZZZZZZZZZZZZZZZZZZI   7ZZZ$ZZI           ");
	putstr(&"\n      =Z$$+7Z$$7ZZZZZZZZ$$$$$$$ZZZZZZZZZZ  ~Z$?$ZZ?            ");
	putstr(&"\n    :$Z$Z...$Z  $ZZZZZZZ~       ~ZZZZZZZZ,.ZZ...Z$Z$~          ");
	putstr(&"\n    7ZZZZZI$ZZ  $ZZZZZZZ~       =ZZZZZZZ7..ZZ$?$ZZZZ$          ");
	putstr(&"\n      ZZZZ$:    $ZZZZZZZZZZZZZZZZZZZZZZ=     ~$ZZZ$:           ");
	putstr(&"\n    7Z$ZZ$,     $ZZZZZZZZZZZZZZZZZZZZ7         ZZZ$Z$          ");
	putstr(&"\n   =ZZZZZZ,     $ZZZZZZZZZZZZZZZZZZZZZZ,       ZZZ$ZZ+         ");
	putstr(&"\n     ,ZZZZ,     $ZZZZZZZ:     =ZZZZZZZZZ     ZZZZZ$:           ");
	putstr(&"\n    =$ZZZZ+     ZZZZZZZZ~       ZZZZZZZZ~   =ZZZZZZZI          ");
	putstr(&"\n    $ZZ$ZZZ$$Z$$ZZZZZZZZZ$$$$   IZZZZZZZZZ$ZZZZZZZZZ$          ");
	putstr(&"\n      :ZZZZZZZZZZZZZZZZZZZZZZ   ~ZZZZZZZZZZZZZZZZZ~            ");
	putstr(&"\n     ,Z$$ZZZZZZZZZZZZZZZZZZZZ    ZZZZZZZZZZZZZZZZZZ~           ");
	putstr(&"\n     =$ZZZZZZZZZZZZZZZZZZZZZZ     $ZZZZZZZZZZZZZZZ$+           ");
	putstr(&"\n        IZZZZZ:.                        . ,ZZZZZ$              ");
	putstr(&"\n       ~$ZZZZZZZZZZZ                 ZZZZ$ZZZZZZZ+             ");
	putstr(&"\n           Z$ZZZ. ,Z~               =Z:.,ZZZ$Z                 ");
	putstr(&"\n          ,ZZZZZ..~Z$.             .7Z:..ZZZZZ:                ");
	putstr(&"\n          ~7+:$ZZZZZZZZI=:.   .,=IZZZZZZZ$Z:=7=                ");
	putstr(&"\n              $$ZZZZZZZZZZZZZZZZZZZZZZ$ZZZZ                    ");
	putstr(&"\n              ==..$ZZZ$ZZZZZZZZZZZ$ZZZZ .~+                    ");
	putstr(&"\n                  I$?.?ZZZ$ZZZ$ZZZI =$7                        ");
	putstr(&"\n                       $7..I$7..I$,                            ");
	putstr(&"\n");
	putstr(&"\n _                     _     _                         _  ");
	putstr(&"\n| |                   (_)   | |                       | | ");
	putstr(&"\n| | ____ ___  ____     _____| |_____  ____ ____  _____| | ");
	putstr(&"\n| |/ ___) _ \\|  _ \\   |  _   _) ___ |/ ___)  _ \\| ___ | | ");
	putstr(&"\n| | |  | |_| | | | |  | |  \\ \\| ____| |   | | | | ____| | ");
	putstr(&"\n|_|_|  \\____/|_| |_|  |_|   \\_\\_____)_|   |_| |_|_____)__)\n\n");

}

pub unsafe fn init() {
    buffer = cstr::new(256);
    screen();
    prompt();
}

unsafe fn prompt() {
    if (buffer.streq(&"ls")) { putstr( &"\na\tb") };
    match buffer.getarg(' ', 0) {
	Some(y)	=> {
	    if(y.streq(&"cat")) {
		match buffer.getarg(' ', 1) {
		    Some(x)	=> {
			if(x.streq(&"a")) { putstr( &"\nHello"); }
			if(x.streq(&"b")) { putstr( &"\nworld!"); }
		    }
		    None	=> { }
		};
	    }
	}
	None	=> { }
    };

    putstr(&"\nsgash > ");
    buffer.reset();
}

/* BUFFER MODIFICATION FUNCTIONS */

struct cstr {
    p: *mut u8,
    p_cstr_i: uint,
    max: uint
}

impl cstr {
    pub unsafe fn new(size: uint) -> cstr {
	// Sometimes this doesn't allocate enough memory and gets stuck...
	let (x, y) = memory::allocator.alloc(size);
	let this = cstr {
	    p: x,
	    p_cstr_i: 0,
	    max: y
	};
	*(((this.p as uint)+this.p_cstr_i) as *mut char) = '\0';
	this
    }

    unsafe fn from_str(s: &str) -> cstr {
	let mut this = cstr::new(256);
	for c in slice::iter(as_bytes(s)) {
	    this.add_char(*c);
	};
	this
    }

    fn len(&self) -> uint { self.p_cstr_i }

    // HELP THIS DOESN'T WORK THERE IS NO GARBAGE COLLECTION!!!
    unsafe fn destroy(&self) { memory::allocator.free(self.p); }

    unsafe fn add_char(&mut self, x: u8) -> bool{
	if (self.p_cstr_i == self.max) { return false; }
	*(((self.p as uint)+self.p_cstr_i) as *mut u8) = x;
	self.p_cstr_i += 1;
	*(((self.p as uint)+self.p_cstr_i) as *mut char) = '\0';
	true
    }

    unsafe fn delete_char(&mut self) -> bool {
	if (self.p_cstr_i == 0) { return false; }
	self.p_cstr_i -= 1;
	*(((self.p as uint)+self.p_cstr_i) as *mut char) = '\0';
	true
    }

    unsafe fn reset(&mut self) {
	self.p_cstr_i = 0;
	*(self.p as *mut char) = '\0';
    }

    unsafe fn eq(&self, other: &cstr) -> bool {
	if (self.len() != other.len()) { return false; }
	else {
	    let mut x = 0;
	    let mut selfp: uint = self.p as uint;
	    let mut otherp: uint = other.p as uint;
	    while (x < self.len()) {
		if (*(selfp as *char) != *(otherp as *char)) { return false; }
		selfp += 1;
		otherp += 1;
		x += 1;
	    }
	    true
	}
    }

    unsafe fn streq(&self, other: &str) -> bool {
	let mut x = 0;
	let mut selfp: uint = self.p as uint;
	for c in slice::iter(as_bytes(other)) {
	    if( *c != *(selfp as *u8) ) { return false; }
	    selfp += 1;
	};
	*(selfp as *char) == '\0'
    }

    unsafe fn getarg(&self, delim: char, mut k: uint) -> Option<cstr> {
	let mut ind: uint = 0;
	let mut found = k == 0;
	let mut selfp: uint = self.p as uint;
	let mut s = cstr::new(256);
	loop {
	    if (*(selfp as *char) == '\0') {
		// End of string
		if (found) { return Some(s); }
		else { return None; }
	    };
	    if (*(selfp as *u8) == delim as u8) {
		if (found) { return Some(s); }
		k -= 1;
	    };
	    if (found) {
		s.add_char(*(selfp as *u8));
	    };
	    found = k == 0;
	    selfp += 1;
	    ind += 1;
	    if (ind == self.max) {
		putstr(&"\nSomething broke!");
		return None;
	    }
	}
    }

    unsafe fn split(&self, delim: char) -> (cstr, cstr) {
	let mut selfp: uint = self.p as uint;
	let mut beg = cstr::new(256);
	let mut end = cstr::new(256);
	let mut found = false;
	loop {
	    if (*(selfp as *char) == '\0') {
		return (beg, end);
	    }
	    else if (*(selfp as *u8) == delim as u8) {
		found = true;
	    }
	    else if (!found) {
		beg.add_char(*(selfp as *u8));
	    }
	    else if (found) {
		end.add_char(*(selfp as *u8));
	    };
	    selfp += 1;
	}
    }

}

<|endoftext|>


<commit_before>//! `Definitions` node and its children.

use fnv::FnvHashMap;
use parser::binary::{Parser, ParserSource, Attributes};
use loader::binary::simple::{Result, Error};
use loader::binary::simple::fbx7400::Properties70;

/// `Definitions` node.
#[derive(Debug, Clone, PartialEq)]
pub struct Definitions {
    /// Version of the node.
    pub version: i32,
    /// Reference count?
    pub count: i32,
    /// Property templates for object types.
    pub object_types: Vec<ObjectType>,
}

impl Definitions {
    /// Loads node contents from the parser.
    pub fn load<R, P>(mut parser: P) -> Result<Self>
    where
        R: ParserSource,
        P: Parser<R>,
    {
        let mut version = None;
        let mut count = None;
        let mut object_types = Vec::new();

        loop {
            let node_type = try_get_node_attrs!(parser, DefinitionsChildAttrs::load);
            match node_type {
                DefinitionsChildAttrs::Version(v) => {
                    version = Some(v);
                    parser.skip_current_node()?;
                },
                DefinitionsChildAttrs::Count(v) => {
                    count = Some(v);
                    parser.skip_current_node()?;
                },
                DefinitionsChildAttrs::ObjectType(attrs) => {
                    object_types.push(ObjectType::load(parser.subtree_parser(), attrs)?);
                },
            }
        }
        Ok(Definitions {
            version: ensure_node_exists!(version, "Definitions", "Version"),
            count: ensure_node_exists!(count, "Definitions", "Count"),
            object_types: object_types,
        })
    }

    /// Returns reference to `Properties70` object of the specified node.
    pub fn get_properties70(&self, object_type: &str, node_type: &str) -> Option<&Properties70> {
        self.object_types
            .iter()
            .find(|t| t.object_type == object_type)
            .and_then(|t| t.property_template.get(node_type))
    }
}

child_attr_loader! { DefinitionsChildAttrs {
    "Count" => Count(i32),
    "Version" => Version(i32),
    "ObjectType" => ObjectType(String),
}}

/// An object type and property template for it.
#[derive(Debug, Clone, PartialEq)]
pub struct ObjectType {
    /// Target object type.
    pub object_type: String,
    /// Reference count?
    pub count: i32,
    /// Property templates.
    pub property_template: FnvHashMap<String, Properties70>,
}

impl ObjectType {
    /// Loads node contents from the parser.
    pub fn load<R, P>(mut parser: P, attrs: String) -> Result<Self>
    where
        R: ParserSource,
        P: Parser<R>,
    {
        let mut count = None;
        let mut property_template = FnvHashMap::default();

        loop {
            let node_type = try_get_node_attrs!(parser, ObjectTypeChildAttrs::load);
            match node_type {
                ObjectTypeChildAttrs::Count(v) => {
                    count = Some(v);
                    parser.skip_current_node()?;
                },
                ObjectTypeChildAttrs::PropertyTemplate(attrs) => {
                    let props = load_property_template(parser.subtree_parser())?;
                    property_template.insert(attrs, props);
                },
            }
        }

        Ok(ObjectType {
            object_type: attrs,
            count: ensure_node_exists!(count, "ObjectType", "Count"),
            property_template: property_template,
        })
    }
}

child_attr_loader! { ObjectTypeChildAttrs {
    "Count" => Count(i32),
    "PropertyTemplate" => PropertyTemplate(String),
}}

fn load_property_template<R, P>(mut parser: P) -> Result<Properties70>
where
    R: ParserSource,
    P: Parser<R>,
{
    let mut props = None;

    loop {
        try_get_node_attrs!(parser, |name: &str, _| if name == "Properties70" {
            Ok(())
        } else {
            Err(Error::UnexpectedNode(name.to_owned()))
        });
        props = Some(Properties70::load(parser.subtree_parser())?);
    }
    Ok(ensure_node_exists!(
        props,
        "PropertyTemplate",
        "Properties70"
    ))
}
<commit_msg>Implement `Definitions::get_property_value`<commit_after>//! `Definitions` node and its children.

use fnv::FnvHashMap;
use parser::binary::{Parser, ParserSource, Attributes};
use loader::binary::simple::{Result, Error};
use loader::binary::simple::fbx7400::{Properties70, PropertyMap, PropertyValue};

/// `Definitions` node.
#[derive(Debug, Clone, PartialEq)]
pub struct Definitions {
    /// Version of the node.
    pub version: i32,
    /// Reference count?
    pub count: i32,
    /// Property templates for object types.
    pub object_types: Vec<ObjectType>,
}

impl Definitions {
    /// Loads node contents from the parser.
    pub fn load<R, P>(mut parser: P) -> Result<Self>
    where
        R: ParserSource,
        P: Parser<R>,
    {
        let mut version = None;
        let mut count = None;
        let mut object_types = Vec::new();

        loop {
            let node_type = try_get_node_attrs!(parser, DefinitionsChildAttrs::load);
            match node_type {
                DefinitionsChildAttrs::Version(v) => {
                    version = Some(v);
                    parser.skip_current_node()?;
                },
                DefinitionsChildAttrs::Count(v) => {
                    count = Some(v);
                    parser.skip_current_node()?;
                },
                DefinitionsChildAttrs::ObjectType(attrs) => {
                    object_types.push(ObjectType::load(parser.subtree_parser(), attrs)?);
                },
            }
        }
        Ok(Definitions {
            version: ensure_node_exists!(version, "Definitions", "Version"),
            count: ensure_node_exists!(count, "Definitions", "Count"),
            object_types: object_types,
        })
    }

    /// Returns reference to `Properties70` object of the specified node.
    pub fn get_properties70(&self, object_type: &str, node_type: &str) -> Option<&Properties70> {
        self.object_types
            .iter()
            .find(|t| t.object_type == object_type)
            .and_then(|t| t.property_template.get(node_type))
    }

    /// Looks up and returns the property value.
    pub fn get_property_value<'s, 'p, T, F>(
        &'p self,
        object_type: &'s str,
        node_type: &'s str,
        prop_name: &'s str,
        props: &'p Properties70,
        f: F,
    ) -> Option<&'p PropertyValue<T>>
    where
        F: Fn(&Properties70) -> &PropertyMap<T>,
    {
        f(props).get(prop_name).or_else(|| {
            self.get_properties70(object_type, node_type).and_then(
                |v| {
                    f(v).get(prop_name)
                },
            )
        })
    }
}

child_attr_loader! { DefinitionsChildAttrs {
    "Count" => Count(i32),
    "Version" => Version(i32),
    "ObjectType" => ObjectType(String),
}}

/// An object type and property template for it.
#[derive(Debug, Clone, PartialEq)]
pub struct ObjectType {
    /// Target object type.
    pub object_type: String,
    /// Reference count?
    pub count: i32,
    /// Property templates.
    pub property_template: FnvHashMap<String, Properties70>,
}

impl ObjectType {
    /// Loads node contents from the parser.
    pub fn load<R, P>(mut parser: P, attrs: String) -> Result<Self>
    where
        R: ParserSource,
        P: Parser<R>,
    {
        let mut count = None;
        let mut property_template = FnvHashMap::default();

        loop {
            let node_type = try_get_node_attrs!(parser, ObjectTypeChildAttrs::load);
            match node_type {
                ObjectTypeChildAttrs::Count(v) => {
                    count = Some(v);
                    parser.skip_current_node()?;
                },
                ObjectTypeChildAttrs::PropertyTemplate(attrs) => {
                    let props = load_property_template(parser.subtree_parser())?;
                    property_template.insert(attrs, props);
                },
            }
        }

        Ok(ObjectType {
            object_type: attrs,
            count: ensure_node_exists!(count, "ObjectType", "Count"),
            property_template: property_template,
        })
    }
}

child_attr_loader! { ObjectTypeChildAttrs {
    "Count" => Count(i32),
    "PropertyTemplate" => PropertyTemplate(String),
}}

fn load_property_template<R, P>(mut parser: P) -> Result<Properties70>
where
    R: ParserSource,
    P: Parser<R>,
{
    let mut props = None;

    loop {
        try_get_node_attrs!(parser, |name: &str, _| if name == "Properties70" {
            Ok(())
        } else {
            Err(Error::UnexpectedNode(name.to_owned()))
        });
        props = Some(Properties70::load(parser.subtree_parser())?);
    }
    Ok(ensure_node_exists!(
        props,
        "PropertyTemplate",
        "Properties70"
    ))
}
<|endoftext|>


<commit_before>use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_hir::{
    intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
    Body, Expr, ExprKind, Lit, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::{
    hir::map::Map,
    ty::{self, FloatTy, IntTy, Ty},
};
use rustc_session::{declare_lint_pass, declare_tool_lint};

use if_chain::if_chain;

use crate::utils::span_lint_and_help;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
    /// inference.
    ///
    /// Default numeric fallback means that if numeric types have not yet been bound to concrete
    /// types at the end of type inference, then integer type is bound to `i32`, and similarly
    /// floating type is bound to `f64`.
    ///
    /// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
    ///
    /// **Why is this bad?** For those who are very careful about types, default numeric fallback
    /// can be a pitfall that cause unexpected runtime behavior.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let i = 10;
    /// let f = 1.23;
    /// ```
    ///
    /// Use instead:
    /// ```rust
    /// let i = 10i32;
    /// let f = 1.23f64;
    /// ```
    pub DEFAULT_NUMERIC_FALLBACK,
    restriction,
    "usage of unconstrained numeric literals which may cause default numeric fallback."
}

declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);

impl LateLintPass<'_> for DefaultNumericFallback {
    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
        let mut visitor = NumericFallbackVisitor::new(cx);
        visitor.visit_body(body);
    }
}

struct NumericFallbackVisitor<'a, 'tcx> {
    /// Stack manages type bound of exprs. The top element holds current expr type.
    ty_bounds: Vec<TyBound<'tcx>>,

    cx: &'a LateContext<'tcx>,
}

impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
    fn new(cx: &'a LateContext<'tcx>) -> Self {
        Self {
            ty_bounds: vec![TyBound::Nothing],
            cx,
        }
    }

    /// Check whether a passed literal has potential to cause fallback or not.
    fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
        let ty_bound = self.ty_bounds.last().unwrap();
        if_chain! {
                if matches!(lit.node,
                            LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
                if matches!(lit_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64));
                if !ty_bound.is_integral();
                then {
                    span_lint_and_help(
                        self.cx,
                        DEFAULT_NUMERIC_FALLBACK,
                        lit.span,
                        "default numeric fallback might occur",
                        None,
                        "consider adding suffix to avoid default numeric fallback",
                    );
                }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
    type Map = Map<'tcx>;

    #[allow(clippy::too_many_lines)]
    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
        match &expr.kind {
            ExprKind::Call(func, args) => {
                if_chain! {
                    if let ExprKind::Path(ref func_path) = func.kind;
                    if let Some(def_id) = self.cx.qpath_res(func_path, func.hir_id).opt_def_id();
                    then {
                        let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
                        for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) {
                            // Push found arg type, then visit arg.
                            self.ty_bounds.push(TyBound::Ty(bound));
                            self.visit_expr(expr);
                            self.ty_bounds.pop();
                        }
                        return;
                    }
                }
            },

            ExprKind::MethodCall(_, _, args, _) => {
                if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
                    let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
                    for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) {
                        self.ty_bounds.push(TyBound::Ty(bound));
                        self.visit_expr(expr);
                        self.ty_bounds.pop();
                    }
                    return;
                }
            },

            ExprKind::Lit(lit) => {
                let ty = self.cx.typeck_results().expr_ty(expr);
                self.check_lit(lit, ty);
                return;
            },

            _ => {},
        }

        walk_expr(self, expr);
    }

    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
        match stmt.kind {
            StmtKind::Local(local) => {
                if local.ty.is_some() {
                    self.ty_bounds.push(TyBound::Any)
                } else {
                    self.ty_bounds.push(TyBound::Nothing)
                }
            },

            _ => self.ty_bounds.push(TyBound::Nothing),
        }

        walk_stmt(self, stmt);
        self.ty_bounds.pop();
    }

    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::None
    }
}

#[derive(Debug, Clone, Copy)]
enum TyBound<'ctx> {
    Any,
    Ty(Ty<'ctx>),
    Nothing,
}

impl<'ctx> TyBound<'ctx> {
    fn is_integral(self) -> bool {
        match self {
            TyBound::Any => true,
            TyBound::Ty(t) => t.is_integral(),
            TyBound::Nothing => false,
        }
    }
}
<commit_msg>Add fn_sig_opt to get fn signature from HirId<commit_after>use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_hir::{
    intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
    Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::{
    hir::map::Map,
    ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
};
use rustc_session::{declare_lint_pass, declare_tool_lint};

use if_chain::if_chain;

use crate::utils::span_lint_and_help;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
    /// inference.
    ///
    /// Default numeric fallback means that if numeric types have not yet been bound to concrete
    /// types at the end of type inference, then integer type is bound to `i32`, and similarly
    /// floating type is bound to `f64`.
    ///
    /// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
    ///
    /// **Why is this bad?** For those who are very careful about types, default numeric fallback
    /// can be a pitfall that cause unexpected runtime behavior.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let i = 10;
    /// let f = 1.23;
    /// ```
    ///
    /// Use instead:
    /// ```rust
    /// let i = 10i32;
    /// let f = 1.23f64;
    /// ```
    pub DEFAULT_NUMERIC_FALLBACK,
    restriction,
    "usage of unconstrained numeric literals which may cause default numeric fallback."
}

declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);

impl LateLintPass<'_> for DefaultNumericFallback {
    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
        let mut visitor = NumericFallbackVisitor::new(cx);
        visitor.visit_body(body);
    }
}

struct NumericFallbackVisitor<'a, 'tcx> {
    /// Stack manages type bound of exprs. The top element holds current expr type.
    ty_bounds: Vec<TyBound<'tcx>>,

    cx: &'a LateContext<'tcx>,
}

impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
    fn new(cx: &'a LateContext<'tcx>) -> Self {
        Self {
            ty_bounds: Vec::new(),
            cx,
        }
    }

    /// Check whether a passed literal has potential to cause fallback or not.
    fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
        if_chain! {
                if let Some(ty_bound) = self.ty_bounds.last();
                if matches!(lit.node,
                            LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
                if matches!(lit_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64));
                if !ty_bound.is_integral();
                then {
                    span_lint_and_help(
                        self.cx,
                        DEFAULT_NUMERIC_FALLBACK,
                        lit.span,
                        "default numeric fallback might occur",
                        None,
                        "consider adding suffix to avoid default numeric fallback",
                    );
                }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
    type Map = Map<'tcx>;

    #[allow(clippy::too_many_lines)]
    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
        match &expr.kind {
            ExprKind::Call(func, args) => {
                if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) {
                    for (expr, bound) in args.iter().zip(fn_sig.skip_binder().inputs().iter()) {
                        // Push found arg type, then visit arg.
                        self.ty_bounds.push(TyBound::Ty(bound));
                        self.visit_expr(expr);
                        self.ty_bounds.pop();
                    }
                    return;
                }
            },

            ExprKind::MethodCall(_, _, args, _) => {
                if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
                    let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
                    for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) {
                        self.ty_bounds.push(TyBound::Ty(bound));
                        self.visit_expr(expr);
                        self.ty_bounds.pop();
                    }
                    return;
                }
            },

            ExprKind::Lit(lit) => {
                let ty = self.cx.typeck_results().expr_ty(expr);
                self.check_lit(lit, ty);
                return;
            },

            _ => {},
        }

        walk_expr(self, expr);
    }

    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
        match stmt.kind {
            StmtKind::Local(local) => {
                if local.ty.is_some() {
                    self.ty_bounds.push(TyBound::Any)
                } else {
                    self.ty_bounds.push(TyBound::Nothing)
                }
            },

            _ => self.ty_bounds.push(TyBound::Nothing),
        }

        walk_stmt(self, stmt);
        self.ty_bounds.pop();
    }

    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::None
    }
}

fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<'tcx>> {
    let node_ty = cx.typeck_results().node_type_opt(hir_id)?;
    // We can't use `TyS::fn_sig` because it automatically performs substs, this may result in FNs.
    match node_ty.kind() {
        ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id)),
        ty::FnPtr(fn_sig) => Some(*fn_sig),
        _ => None,
    }
}

#[derive(Debug, Clone, Copy)]
enum TyBound<'ctx> {
    Any,
    Ty(Ty<'ctx>),
    Nothing,
}

impl<'ctx> TyBound<'ctx> {
    fn is_integral(self) -> bool {
        match self {
            TyBound::Any => true,
            TyBound::Ty(t) => t.is_integral(),
            TyBound::Nothing => false,
        }
    }
}
<|endoftext|>