<commit_before>//! Provides functions to read and write segment registers.

#[cfg(docsrs)]
use crate::{registers::control::Cr4Flags, structures::gdt::GlobalDescriptorTable};
use crate::{
    registers::model_specific::{FsBase, GsBase, Msr},
    structures::gdt::SegmentSelector,
    VirtAddr,
};

/// An x86 segment
///
/// Segment registers on x86 are 16-bit [`SegmentSelector`]s, which index into
/// the [`GlobalDescriptorTable`]. The corresponding GDT entry is used to
/// configure the segment itself. Note that most segmentation functionality is
/// disabled in 64-bit mode. See the individual segments for more information.
pub trait Segment {
    /// Returns the current value of the segment register.
    fn get_reg() -> SegmentSelector;
    /// Reload the segment register. Depending on the segment, this may also
    /// reconfigure the corresponding segment.
    ///
    /// ## Safety
    ///
    /// This function is unsafe because the caller must ensure that `sel`
    /// is a valid segment descriptor, and that reconfiguring the segment will
    /// not cause undefined behavior.
    unsafe fn set_reg(sel: SegmentSelector);
}

/// An x86 segment which is actually used in 64-bit mode
///
/// While most segments are unused in 64-bit mode, the FS and GS segments are
/// still partially used. Only the 64-bit segment base address is used, and this
/// address can be set via the GDT, or by using the `FSGSBASE` instructions.
pub trait Segment64: Segment {
    /// MSR containing the segment base. This MSR can be used to set the base
    /// when [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set.
    const BASE: Msr;
    /// Reads the segment base address
    ///
    /// ## Safety
    ///
    /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set, this instruction will throw a `#UD`.
    unsafe fn read_base() -> VirtAddr;
    /// Writes the segment base address
    ///
    /// ## Safety
    ///
    /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set, this instruction will throw a `#UD`.
    ///
    /// The caller must ensure that this write operation has no unsafe side
    /// effects, as the segment base address might be in use.
    unsafe fn write_base(base: VirtAddr);
}

macro_rules! get_reg_impl {
    ($name:literal, $asm_get:ident) => {
        fn get_reg() -> SegmentSelector {
            let segment: u16;
            #[cfg(feature = "inline_asm")]
            unsafe {
                asm!(concat!("mov {0:x}, ", $name), out(reg) segment, options(nomem, nostack, preserves_flags));
            }
            #[cfg(not(feature = "inline_asm"))]
            unsafe {
                segment = crate::asm::$asm_get();
            }
            SegmentSelector(segment)
        }
    };
}

macro_rules! segment_impl {
    ($type:ty, $name:literal, $asm_get:ident, $asm_load:ident) => {
        impl Segment for $type {
            get_reg_impl!($name, $asm_get);

            unsafe fn set_reg(sel: SegmentSelector) {
                #[cfg(feature = "inline_asm")]
                asm!(concat!("mov ", $name, ", {0:x}"), in(reg) sel.0, options(nostack, preserves_flags));

                #[cfg(not(feature = "inline_asm"))]
                crate::asm::$asm_load(sel.0);
            }
        }
    };
}

macro_rules! segment64_impl {
    ($type:ty, $name:literal, $base:ty, $asm_rd:ident, $asm_wr:ident) => {
        impl Segment64 for $type {
            const BASE: Msr = <$base>::MSR;
            unsafe fn read_base() -> VirtAddr {
                #[cfg(feature = "inline_asm")]
                {
                    let val: u64;
                    asm!(concat!("rd", $name, "base {}"), out(reg) val, options(nomem, nostack, preserves_flags));
                    VirtAddr::new_unsafe(val)
                }
                #[cfg(not(feature = "inline_asm"))]
                VirtAddr::new_unsafe(crate::asm::$asm_rd())
            }

            unsafe fn write_base(base: VirtAddr) {
                #[cfg(feature = "inline_asm")]
                asm!(concat!("wr", $name, "base {}"), in(reg) base.as_u64(), options(nostack, preserves_flags));

                #[cfg(not(feature = "inline_asm"))]
                crate::asm::$asm_wr(base.as_u64());
            }
        }
    };
}

/// Code Segment
///
/// The segment base and limit are unused in 64-bit mode. Only the L (long), D
/// (default operation size), and DPL (descriptor privilege-level) fields of the
/// descriptor are recognized. So chaning the segment register can be used to
/// change privilege level or enable/disable long mode.
#[derive(Debug)]
pub struct CS;
impl Segment for CS {
    get_reg_impl!("cs", x86_64_asm_get_cs);

    unsafe fn set_reg(sel: SegmentSelector) {
        // Note this is special since we cannot directly move to cs. Instead we
        // push the new segment selector and return value on the stack and use
        // retfq to reload cs and continue at 1:.
        #[cfg(feature = "inline_asm")]
        asm!(
            "push {sel}",
            "lea {tmp}, [1f + rip]",
            "push {tmp}",
            "retfq",
            "1:",
            sel = in(reg) u64::from(sel.0),
            tmp = lateout(reg) _,
            options(preserves_flags),
        );

        #[cfg(not(feature = "inline_asm"))]
        crate::asm::x86_64_asm_set_cs(u64::from(sel.0));
    }
}

/// Stack Segment
///
/// Entirely unused in 64-bit mode, setting the segment register does nothing.
#[derive(Debug)]
pub struct SS;
segment_impl!(SS, "ss", x86_64_asm_get_ss, x86_64_asm_load_ss);

/// Data Segment
///
/// Entirely unused in 64-bit mode, setting the segment register does nothing.
#[derive(Debug)]
pub struct DS;
segment_impl!(DS, "ds", x86_64_asm_get_ds, x86_64_asm_load_ds);

/// ES Segment
///
/// Entirely unused in 64-bit mode, setting the segment register does nothing.
#[derive(Debug)]
pub struct ES;
segment_impl!(ES, "es", x86_64_asm_get_es, x86_64_asm_load_es);

/// FS Segment
///
/// Only base is used in 64-bit mode, see [`Segment64`]. This is often used in
/// user-mode for Thread-Local Storage (TLS).
#[derive(Debug)]
pub struct FS;
segment_impl!(FS, "fs", x86_64_asm_get_fs, x86_64_asm_load_fs);
segment64_impl!(FS, "fs", FsBase, x86_64_asm_rdfsbase, x86_64_asm_wrfsbase);

/// GS Segment
///
/// Only base is used in 64-bit mode, see [`Segment64`]. In kernel-mode, the GS
/// base often points to a per-cpu kernel data structure.
#[derive(Debug)]
pub struct GS;
segment_impl!(GS, "gs", x86_64_asm_get_gs, x86_64_asm_load_gs);
segment64_impl!(GS, "gs", GsBase, x86_64_asm_rdgsbase, x86_64_asm_wrgsbase);

impl GS {
    /// Swap `KernelGsBase` MSR and `GsBase` MSR.
    ///
    /// ## Safety
    ///
    /// This function is unsafe because the caller must ensure that the
    /// swap operation cannot lead to undefined behavior.
    pub unsafe fn swap() {
        #[cfg(feature = "inline_asm")]
        asm!("swapgs", options(nostack, preserves_flags));

        #[cfg(not(feature = "inline_asm"))]
        crate::asm::x86_64_asm_swapgs();
    }
}

/// Alias for [`CS::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `CS::set_reg()` instead")]
#[inline]
pub unsafe fn set_cs(sel: SegmentSelector) {
    CS::set_reg(sel)
}
/// Alias for [`SS::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `SS::set_reg()` instead")]
#[inline]
pub unsafe fn load_ss(sel: SegmentSelector) {
    SS::set_reg(sel)
}
/// Alias for [`DS::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `DS::set_reg()` instead")]
#[inline]
pub unsafe fn load_ds(sel: SegmentSelector) {
    DS::set_reg(sel)
}
/// Alias for [`ES::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `ES::set_reg()` instead")]
#[inline]
pub unsafe fn load_es(sel: SegmentSelector) {
    ES::set_reg(sel)
}
/// Alias for [`FS::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `FS::set_reg()` instead")]
#[inline]
pub unsafe fn load_fs(sel: SegmentSelector) {
    FS::set_reg(sel)
}
/// Alias for [`GS::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `GS::set_reg()` instead")]
#[inline]
pub unsafe fn load_gs(sel: SegmentSelector) {
    GS::set_reg(sel)
}
/// Alias for [`GS::swap()`]
#[deprecated(since = "0.14.4", note = "use `GS::swap()` instead")]
#[inline]
pub unsafe fn swap_gs() {
    GS::swap()
}
/// Alias for [`CS::get_reg()`]
#[deprecated(since = "0.14.4", note = "use `CS::get_reg()` instead")]
#[inline]
pub fn cs() -> SegmentSelector {
    CS::get_reg()
}
/// Alias for [`FS::write_base()`].
///
/// Raises #GP if the provided address is non-canonical.
#[deprecated(since = "0.14.4", note = "use `FS::write_base()` instead")]
#[inline]
pub unsafe fn wrfsbase(val: u64) {
    FS::write_base(VirtAddr::new(val))
}
/// Alias for [`FS::read_base()`]
#[deprecated(since = "0.14.4", note = "use `FS::read_base()` instead")]
#[inline]
pub unsafe fn rdfsbase() -> u64 {
    FS::read_base().as_u64()
}
/// Alias for [`GS::write_base()`].
///
/// Raises #GP if the provided address is non-canonical.
#[deprecated(since = "0.14.4", note = "use `GS::write_base()` instead")]
#[inline]
pub unsafe fn wrgsbase(val: u64) {
    GS::write_base(VirtAddr::new(val))
}
/// Alias for [`GS::read_base()`]
#[deprecated(since = "0.14.4", note = "use `GS::read_base()` instead")]
#[inline]
pub unsafe fn rdgsbase() -> u64 {
    GS::read_base().as_u64()
}
<commit_msg>Allow missing_safety_doc in deprecated segment functions<commit_after>//! Provides functions to read and write segment registers.

#[cfg(docsrs)]
use crate::{registers::control::Cr4Flags, structures::gdt::GlobalDescriptorTable};
use crate::{
    registers::model_specific::{FsBase, GsBase, Msr},
    structures::gdt::SegmentSelector,
    VirtAddr,
};

/// An x86 segment
///
/// Segment registers on x86 are 16-bit [`SegmentSelector`]s, which index into
/// the [`GlobalDescriptorTable`]. The corresponding GDT entry is used to
/// configure the segment itself. Note that most segmentation functionality is
/// disabled in 64-bit mode. See the individual segments for more information.
pub trait Segment {
    /// Returns the current value of the segment register.
    fn get_reg() -> SegmentSelector;
    /// Reload the segment register. Depending on the segment, this may also
    /// reconfigure the corresponding segment.
    ///
    /// ## Safety
    ///
    /// This function is unsafe because the caller must ensure that `sel`
    /// is a valid segment descriptor, and that reconfiguring the segment will
    /// not cause undefined behavior.
    unsafe fn set_reg(sel: SegmentSelector);
}

/// An x86 segment which is actually used in 64-bit mode
///
/// While most segments are unused in 64-bit mode, the FS and GS segments are
/// still partially used. Only the 64-bit segment base address is used, and this
/// address can be set via the GDT, or by using the `FSGSBASE` instructions.
pub trait Segment64: Segment {
    /// MSR containing the segment base. This MSR can be used to set the base
    /// when [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set.
    const BASE: Msr;
    /// Reads the segment base address
    ///
    /// ## Safety
    ///
    /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set, this instruction will throw a `#UD`.
    unsafe fn read_base() -> VirtAddr;
    /// Writes the segment base address
    ///
    /// ## Safety
    ///
    /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is not set, this instruction will throw a `#UD`.
    ///
    /// The caller must ensure that this write operation has no unsafe side
    /// effects, as the segment base address might be in use.
    unsafe fn write_base(base: VirtAddr);
}

macro_rules! get_reg_impl {
    ($name:literal, $asm_get:ident) => {
        fn get_reg() -> SegmentSelector {
            let segment: u16;
            #[cfg(feature = "inline_asm")]
            unsafe {
                asm!(concat!("mov {0:x}, ", $name), out(reg) segment, options(nomem, nostack, preserves_flags));
            }
            #[cfg(not(feature = "inline_asm"))]
            unsafe {
                segment = crate::asm::$asm_get();
            }
            SegmentSelector(segment)
        }
    };
}

macro_rules! segment_impl {
    ($type:ty, $name:literal, $asm_get:ident, $asm_load:ident) => {
        impl Segment for $type {
            get_reg_impl!($name, $asm_get);

            unsafe fn set_reg(sel: SegmentSelector) {
                #[cfg(feature = "inline_asm")]
                asm!(concat!("mov ", $name, ", {0:x}"), in(reg) sel.0, options(nostack, preserves_flags));

                #[cfg(not(feature = "inline_asm"))]
                crate::asm::$asm_load(sel.0);
            }
        }
    };
}

macro_rules! segment64_impl {
    ($type:ty, $name:literal, $base:ty, $asm_rd:ident, $asm_wr:ident) => {
        impl Segment64 for $type {
            const BASE: Msr = <$base>::MSR;
            unsafe fn read_base() -> VirtAddr {
                #[cfg(feature = "inline_asm")]
                {
                    let val: u64;
                    asm!(concat!("rd", $name, "base {}"), out(reg) val, options(nomem, nostack, preserves_flags));
                    VirtAddr::new_unsafe(val)
                }
                #[cfg(not(feature = "inline_asm"))]
                VirtAddr::new_unsafe(crate::asm::$asm_rd())
            }

            unsafe fn write_base(base: VirtAddr) {
                #[cfg(feature = "inline_asm")]
                asm!(concat!("wr", $name, "base {}"), in(reg) base.as_u64(), options(nostack, preserves_flags));

                #[cfg(not(feature = "inline_asm"))]
                crate::asm::$asm_wr(base.as_u64());
            }
        }
    };
}

/// Code Segment
///
/// The segment base and limit are unused in 64-bit mode. Only the L (long), D
/// (default operation size), and DPL (descriptor privilege-level) fields of the
/// descriptor are recognized. So chaning the segment register can be used to
/// change privilege level or enable/disable long mode.
#[derive(Debug)]
pub struct CS;
impl Segment for CS {
    get_reg_impl!("cs", x86_64_asm_get_cs);

    unsafe fn set_reg(sel: SegmentSelector) {
        // Note this is special since we cannot directly move to cs. Instead we
        // push the new segment selector and return value on the stack and use
        // retfq to reload cs and continue at 1:.
        #[cfg(feature = "inline_asm")]
        asm!(
            "push {sel}",
            "lea {tmp}, [1f + rip]",
            "push {tmp}",
            "retfq",
            "1:",
            sel = in(reg) u64::from(sel.0),
            tmp = lateout(reg) _,
            options(preserves_flags),
        );

        #[cfg(not(feature = "inline_asm"))]
        crate::asm::x86_64_asm_set_cs(u64::from(sel.0));
    }
}

/// Stack Segment
///
/// Entirely unused in 64-bit mode, setting the segment register does nothing.
#[derive(Debug)]
pub struct SS;
segment_impl!(SS, "ss", x86_64_asm_get_ss, x86_64_asm_load_ss);

/// Data Segment
///
/// Entirely unused in 64-bit mode, setting the segment register does nothing.
#[derive(Debug)]
pub struct DS;
segment_impl!(DS, "ds", x86_64_asm_get_ds, x86_64_asm_load_ds);

/// ES Segment
///
/// Entirely unused in 64-bit mode, setting the segment register does nothing.
#[derive(Debug)]
pub struct ES;
segment_impl!(ES, "es", x86_64_asm_get_es, x86_64_asm_load_es);

/// FS Segment
///
/// Only base is used in 64-bit mode, see [`Segment64`]. This is often used in
/// user-mode for Thread-Local Storage (TLS).
#[derive(Debug)]
pub struct FS;
segment_impl!(FS, "fs", x86_64_asm_get_fs, x86_64_asm_load_fs);
segment64_impl!(FS, "fs", FsBase, x86_64_asm_rdfsbase, x86_64_asm_wrfsbase);

/// GS Segment
///
/// Only base is used in 64-bit mode, see [`Segment64`]. In kernel-mode, the GS
/// base often points to a per-cpu kernel data structure.
#[derive(Debug)]
pub struct GS;
segment_impl!(GS, "gs", x86_64_asm_get_gs, x86_64_asm_load_gs);
segment64_impl!(GS, "gs", GsBase, x86_64_asm_rdgsbase, x86_64_asm_wrgsbase);

impl GS {
    /// Swap `KernelGsBase` MSR and `GsBase` MSR.
    ///
    /// ## Safety
    ///
    /// This function is unsafe because the caller must ensure that the
    /// swap operation cannot lead to undefined behavior.
    pub unsafe fn swap() {
        #[cfg(feature = "inline_asm")]
        asm!("swapgs", options(nostack, preserves_flags));

        #[cfg(not(feature = "inline_asm"))]
        crate::asm::x86_64_asm_swapgs();
    }
}

/// Alias for [`CS::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `CS::set_reg()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn set_cs(sel: SegmentSelector) {
    CS::set_reg(sel)
}
/// Alias for [`SS::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `SS::set_reg()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn load_ss(sel: SegmentSelector) {
    SS::set_reg(sel)
}
/// Alias for [`DS::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `DS::set_reg()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn load_ds(sel: SegmentSelector) {
    DS::set_reg(sel)
}
/// Alias for [`ES::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `ES::set_reg()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn load_es(sel: SegmentSelector) {
    ES::set_reg(sel)
}
/// Alias for [`FS::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `FS::set_reg()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn load_fs(sel: SegmentSelector) {
    FS::set_reg(sel)
}
/// Alias for [`GS::set_reg()`]
#[deprecated(since = "0.14.4", note = "use `GS::set_reg()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn load_gs(sel: SegmentSelector) {
    GS::set_reg(sel)
}
/// Alias for [`GS::swap()`]
#[deprecated(since = "0.14.4", note = "use `GS::swap()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn swap_gs() {
    GS::swap()
}
/// Alias for [`CS::get_reg()`]
#[deprecated(since = "0.14.4", note = "use `CS::get_reg()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub fn cs() -> SegmentSelector {
    CS::get_reg()
}
/// Alias for [`FS::write_base()`].
///
/// Raises #GP if the provided address is non-canonical.
#[deprecated(since = "0.14.4", note = "use `FS::write_base()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn wrfsbase(val: u64) {
    FS::write_base(VirtAddr::new(val))
}
/// Alias for [`FS::read_base()`]
#[deprecated(since = "0.14.4", note = "use `FS::read_base()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn rdfsbase() -> u64 {
    FS::read_base().as_u64()
}
/// Alias for [`GS::write_base()`].
///
/// Raises #GP if the provided address is non-canonical.
#[deprecated(since = "0.14.4", note = "use `GS::write_base()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn wrgsbase(val: u64) {
    GS::write_base(VirtAddr::new(val))
}
/// Alias for [`GS::read_base()`]
#[deprecated(since = "0.14.4", note = "use `GS::read_base()` instead")]
#[allow(clippy::missing_safety_doc)]
#[inline]
pub unsafe fn rdgsbase() -> u64 {
    GS::read_base().as_u64()
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A pass that annotates every item and method with its stability level,
//! propagating default levels lexically from parent to children ast nodes.

use util::nodemap::{NodeMap, DefIdMap};
use syntax::codemap::Span;
use syntax::{attr, visit};
use syntax::ast;
use syntax::ast::{Attribute, Block, Crate, DefId, FnDecl, NodeId, Variant};
use syntax::ast::{Item, RequiredMethod, ProvidedMethod, TraitItem};
use syntax::ast::{TypeMethod, Method, Generics, StructField, TypeTraitItem};
use syntax::ast_util::is_local;
use syntax::attr::Stability;
use syntax::visit::{FnKind, FkMethod, Visitor};
use middle::ty;
use metadata::csearch;

use std::mem::replace;

/// A stability index, giving the stability level for items and methods.
pub struct Index {
    // stability for crate-local items; unmarked stability == no entry
    local: NodeMap<Stability>,
    // cache for extern-crate items; unmarked stability == entry with None
    extern_cache: DefIdMap<Option<Stability>>
}

// A private tree-walker for producing an Index.
struct Annotator {
    index: Index,
    parent: Option<Stability>
}

impl Annotator {
    // Determine the stability for a node based on its attributes and inherited
    // stability. The stability is recorded in the index and used as the parent.
    fn annotate<F>(&mut self, id: NodeId, attrs: &Vec<Attribute>, f: F) where
        F: FnOnce(&mut Annotator),
    {
        match attr::find_stability(attrs.as_slice()) {
            Some(stab) => {
                self.index.local.insert(id, stab.clone());

                // Don't inherit #[stable]
                if stab.level != attr::Stable {
                    let parent = replace(&mut self.parent, Some(stab));
                    f(self);
                    self.parent = parent;
                } else {
                    f(self);
                }
            }
            None => {
                self.parent.clone().map(|stab| self.index.local.insert(id, stab));
                f(self);
            }
        }
    }
}

impl<'v> Visitor<'v> for Annotator {
    fn visit_item(&mut self, i: &Item) {
        self.annotate(i.id, &i.attrs, |v| visit::walk_item(v, i));

        if let ast::ItemStruct(ref sd, _) = i.node {
            sd.ctor_id.map(|id| {
                self.annotate(id, &i.attrs, |_| {})
            });
        }
    }

    fn visit_fn(&mut self, fk: FnKind<'v>, _: &'v FnDecl,
                _: &'v Block, _: Span, _: NodeId) {
        if let FkMethod(_, _, meth) = fk {
            // Methods are not already annotated, so we annotate it
            self.annotate(meth.id, &meth.attrs, |_| {});
        }
        // Items defined in a function body have no reason to have
        // a stability attribute, so we don't recurse.
    }

    fn visit_trait_item(&mut self, t: &TraitItem) {
        let (id, attrs) = match *t {
            RequiredMethod(TypeMethod {id, ref attrs, ..}) => (id, attrs),

            // work around lack of pattern matching for @ types
            ProvidedMethod(ref method) => {
                match **method {
                    Method {ref attrs, id, ..} => (id, attrs),
                }
            }

            TypeTraitItem(ref typedef) => (typedef.ty_param.id, &typedef.attrs),
        };
        self.annotate(id, attrs, |v| visit::walk_trait_item(v, t));
    }

    fn visit_variant(&mut self, var: &Variant, g: &'v Generics) {
        self.annotate(var.node.id, &var.node.attrs, |v| visit::walk_variant(v, var, g))
    }

    fn visit_struct_field(&mut self, s: &StructField) {
        self.annotate(s.node.id, &s.node.attrs, |v| visit::walk_struct_field(v, s));
    }
}

impl Index {
    /// Construct the stability index for a crate being compiled.
    pub fn build(krate: &Crate) -> Index {
        let mut annotator = Annotator {
            index: Index {
                local: NodeMap::new(),
                extern_cache: DefIdMap::new()
            },
            parent: None
        };
        annotator.annotate(ast::CRATE_NODE_ID, &krate.attrs, |v| visit::walk_crate(v, krate));
        annotator.index
    }
}

/// Lookup the stability for a node, loading external crate
/// metadata as necessary.
pub fn lookup(tcx: &ty::ctxt, id: DefId) -> Option<Stability> {
    // is this definition the implementation of a trait method?
    match ty::trait_item_of_item(tcx, id) {
        Some(ty::MethodTraitItemId(trait_method_id))
                if trait_method_id != id => {
            lookup(tcx, trait_method_id)
        }
        _ if is_local(id) => {
            tcx.stability.borrow().local.get(&id.node).cloned()
        }
        _ => {
            let stab = csearch::get_stability(&tcx.sess.cstore, id);
            let mut index = tcx.stability.borrow_mut();
            (*index).extern_cache.insert(id, stab.clone());
            stab
        }
    }
}
<commit_msg>Stability: tweak rules for trait impls<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A pass that annotates every item and method with its stability level,
//! propagating default levels lexically from parent to children ast nodes.

use util::nodemap::{NodeMap, DefIdMap};
use syntax::codemap::Span;
use syntax::{attr, visit};
use syntax::ast;
use syntax::ast::{Attribute, Block, Crate, DefId, FnDecl, NodeId, Variant};
use syntax::ast::{Item, RequiredMethod, ProvidedMethod, TraitItem};
use syntax::ast::{TypeMethod, Method, Generics, StructField, TypeTraitItem};
use syntax::ast_util::is_local;
use syntax::attr::Stability;
use syntax::visit::{FnKind, FkMethod, Visitor};
use middle::ty;
use metadata::csearch;

use std::mem::replace;

/// A stability index, giving the stability level for items and methods.
pub struct Index {
    // stability for crate-local items; unmarked stability == no entry
    local: NodeMap<Stability>,
    // cache for extern-crate items; unmarked stability == entry with None
    extern_cache: DefIdMap<Option<Stability>>
}

// A private tree-walker for producing an Index.
struct Annotator {
    index: Index,
    parent: Option<Stability>
}

impl Annotator {
    // Determine the stability for a node based on its attributes and inherited
    // stability. The stability is recorded in the index and used as the parent.
    fn annotate<F>(&mut self, id: NodeId, use_parent: bool,
                   attrs: &Vec<Attribute>, f: F) where
        F: FnOnce(&mut Annotator),
    {
        match attr::find_stability(attrs.as_slice()) {
            Some(stab) => {
                self.index.local.insert(id, stab.clone());

                // Don't inherit #[stable]
                if stab.level != attr::Stable {
                    let parent = replace(&mut self.parent, Some(stab));
                    f(self);
                    self.parent = parent;
                } else {
                    f(self);
                }
            }
            None => {
                if use_parent {
                    self.parent.clone().map(|stab| self.index.local.insert(id, stab));
                }
                f(self);
            }
        }
    }
}

impl<'v> Visitor<'v> for Annotator {
    fn visit_item(&mut self, i: &Item) {
        // FIXME (#18969): the following is a hack around the fact
        // that we cannot currently annotate the stability of
        // `deriving`.  Basically, we do *not* allow stability
        // inheritance on trait implementations, so that derived
        // implementations appear to be unannotated. This then allows
        // derived implementations to be automatically tagged with the
        // stability of the trait. This is WRONG, but expedient to get
        // libstd stabilized for the 1.0 release.
        let use_parent = match i.node {
            ast::ItemImpl(_, _, Some(_), _, _) => false,
            _ => true,
        };

        self.annotate(i.id, use_parent, &i.attrs, |v| visit::walk_item(v, i));

        if let ast::ItemStruct(ref sd, _) = i.node {
            sd.ctor_id.map(|id| {
                self.annotate(id, true, &i.attrs, |_| {})
            });
        }
    }

    fn visit_fn(&mut self, fk: FnKind<'v>, _: &'v FnDecl,
                _: &'v Block, _: Span, _: NodeId) {
        if let FkMethod(_, _, meth) = fk {
            // Methods are not already annotated, so we annotate it
            self.annotate(meth.id, true, &meth.attrs, |_| {});
        }
        // Items defined in a function body have no reason to have
        // a stability attribute, so we don't recurse.
    }

    fn visit_trait_item(&mut self, t: &TraitItem) {
        let (id, attrs) = match *t {
            RequiredMethod(TypeMethod {id, ref attrs, ..}) => (id, attrs),

            // work around lack of pattern matching for @ types
            ProvidedMethod(ref method) => {
                match **method {
                    Method {ref attrs, id, ..} => (id, attrs),
                }
            }

            TypeTraitItem(ref typedef) => (typedef.ty_param.id, &typedef.attrs),
        };
        self.annotate(id, true, attrs, |v| visit::walk_trait_item(v, t));
    }

    fn visit_variant(&mut self, var: &Variant, g: &'v Generics) {
        self.annotate(var.node.id, true, &var.node.attrs,
                      |v| visit::walk_variant(v, var, g))
    }

    fn visit_struct_field(&mut self, s: &StructField) {
        self.annotate(s.node.id, true, &s.node.attrs,
                      |v| visit::walk_struct_field(v, s));
    }
}

impl Index {
    /// Construct the stability index for a crate being compiled.
    pub fn build(krate: &Crate) -> Index {
        let mut annotator = Annotator {
            index: Index {
                local: NodeMap::new(),
                extern_cache: DefIdMap::new()
            },
            parent: None
        };
        annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs,
                           |v| visit::walk_crate(v, krate));
        annotator.index
    }
}

/// Lookup the stability for a node, loading external crate
/// metadata as necessary.
pub fn lookup(tcx: &ty::ctxt, id: DefId) -> Option<Stability> {
    // is this definition the implementation of a trait method?
    match ty::trait_item_of_item(tcx, id) {
        Some(ty::MethodTraitItemId(trait_method_id))
                if trait_method_id != id => {
            return lookup(tcx, trait_method_id)
        }
        _ => {}
    }

    let item_stab = if is_local(id) {
        tcx.stability.borrow().local.get(&id.node).cloned()
    } else {
        let stab = csearch::get_stability(&tcx.sess.cstore, id);
        let mut index = tcx.stability.borrow_mut();
        (*index).extern_cache.insert(id, stab.clone());
        stab
    };

    item_stab.or_else(|| {
        if let Some(trait_id) = ty::trait_id_of_impl(tcx, id) {
            // FIXME (#18969): for the time being, simply use the
            // stability of the trait to determine the stability of any
            // unmarked impls for it. See FIXME above for more details.

            lookup(tcx, trait_id)
        } else {
            None
        }
    })
}
<|endoftext|>


<commit_before>extern crate pkg_config;
extern crate cmake;

use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e{
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

fn main() {
    let https = env::var("CARGO_FEATURE_HTTPS").is_ok();
    let ssh = env::var("CARGO_FEATURE_SSH").is_ok();
    if ssh {
        register_dep("SSH2");
    }
    if https {
        register_dep("OPENSSL");
    }
    let has_pkgconfig = Command::new("pkg-config").output().is_ok();

    if env::var("LIBGIT2_SYS_USE_PKG_CONFIG").is_ok() {
        if pkg_config::find_library("libgit2").is_ok() {
            return
        }
    }

    let target = env::var("TARGET").unwrap();
    let windows = target.contains("windows");
    let msvc = target.contains("msvc");
    let mut cfg = cmake::Config::new("libgit2");

    if msvc {
        // libgit2 passes the /GL flag to enable whole program optimization, but
        // this requires that the /LTCG flag is passed to the linker later on,
        // and currently the compiler does not do that, so we disable whole
        // program optimization entirely.
        cfg.cflag("/GL-");

        // Currently liblibc links to msvcrt which apparently is a dynamic CRT,
        // so we need to turn this off to get it to link right.
        cfg.define("STATIC_CRT", "OFF");
    }

    // libgit2 uses pkg-config to discover libssh2, but this doesn't work on
    // windows as libssh2 doesn't come with a libssh2.pc file in that install
    // (or when pkg-config isn't found). As a result we just manually turn on
    // SSH support in libgit2 (a little jankily) here...
    if ssh && (windows || !has_pkgconfig) {
        if let Ok(libssh2_include) = env::var("DEP_SSH2_INCLUDE") {
            if msvc {
                cfg.cflag(format!("/I{}", libssh2_include))
                   .cflag("/DGIT_SSH");
            } else {
                cfg.cflag(format!("-I{}", libssh2_include))
                   .cflag("-DGIT_SSH");
            }
        }
    }

    if ssh {
        cfg.register_dep("SSH2");
    } else {
        cfg.define("USE_SSH", "OFF");
    }
    if https {
        cfg.register_dep("OPENSSL");
    } else {
        cfg.define("USE_OPENSSL", "OFF");
    }

    let _ = fs::remove_dir_all(env::var("OUT_DIR").unwrap());
    t!(fs::create_dir_all(env::var("OUT_DIR").unwrap()));

    let dst = cfg.define("BUILD_SHARED_LIBS", "OFF")
                 .define("BUILD_CLAR", "OFF")
                 .define("CURL", "OFF")
                 .register_dep("Z")
                 .build();

    // Make sure libssh2 was detected on unix systems, because it definitely
    // should have been!
    if ssh && !msvc {
        let flags = dst.join("build/CMakeFiles/git2.dir/flags.make");
        let mut contents = String::new();
        t!(t!(File::open(flags)).read_to_string(&mut contents));
        if !contents.contains("-DGIT_SSH") {
            panic!("libgit2 failed to find libssh2, and SSH support is required");
        }
    }

    if target.contains("windows") {
        println!("cargo:rustc-link-lib=winhttp");
        println!("cargo:rustc-link-lib=rpcrt4");
        println!("cargo:rustc-link-lib=ole32");
        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=static=git2");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
        return
    }

    println!("cargo:rustc-link-lib=static=git2");
    println!("cargo:rustc-link-search=native={}", dst.join("lib").display());
    if target.contains("apple") {
        println!("cargo:rustc-link-lib=iconv");
    }
}

fn register_dep(dep: &str) {
    match env::var(&format!("DEP_{}_ROOT", dep)) {
        Ok(s) => {
            prepend("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        }
        Err(..) => {}
    }
}

fn prepend(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let mut v = vec![val];
    v.extend(env::split_paths(&prefix));
    env::set_var(var, &env::join_paths(v).unwrap());
}
<commit_msg>Fix linkage on OSX<commit_after>extern crate pkg_config;
extern crate cmake;

use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e{
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

fn main() {
    let https = env::var("CARGO_FEATURE_HTTPS").is_ok();
    let ssh = env::var("CARGO_FEATURE_SSH").is_ok();
    if ssh {
        register_dep("SSH2");
    }
    if https {
        register_dep("OPENSSL");
    }
    let has_pkgconfig = Command::new("pkg-config").output().is_ok();

    if env::var("LIBGIT2_SYS_USE_PKG_CONFIG").is_ok() {
        if pkg_config::find_library("libgit2").is_ok() {
            return
        }
    }

    let target = env::var("TARGET").unwrap();
    let windows = target.contains("windows");
    let msvc = target.contains("msvc");
    let mut cfg = cmake::Config::new("libgit2");

    if msvc {
        // libgit2 passes the /GL flag to enable whole program optimization, but
        // this requires that the /LTCG flag is passed to the linker later on,
        // and currently the compiler does not do that, so we disable whole
        // program optimization entirely.
        cfg.cflag("/GL-");

        // Currently liblibc links to msvcrt which apparently is a dynamic CRT,
        // so we need to turn this off to get it to link right.
        cfg.define("STATIC_CRT", "OFF");
    }

    // libgit2 uses pkg-config to discover libssh2, but this doesn't work on
    // windows as libssh2 doesn't come with a libssh2.pc file in that install
    // (or when pkg-config isn't found). As a result we just manually turn on
    // SSH support in libgit2 (a little jankily) here...
    if ssh && (windows || !has_pkgconfig) {
        if let Ok(libssh2_include) = env::var("DEP_SSH2_INCLUDE") {
            if msvc {
                cfg.cflag(format!("/I{}", libssh2_include))
                   .cflag("/DGIT_SSH");
            } else {
                cfg.cflag(format!("-I{}", libssh2_include))
                   .cflag("-DGIT_SSH");
            }
        }
    }

    if ssh {
        cfg.register_dep("SSH2");
    } else {
        cfg.define("USE_SSH", "OFF");
    }
    if https {
        cfg.register_dep("OPENSSL");
    } else {
        cfg.define("USE_OPENSSL", "OFF");
    }

    let _ = fs::remove_dir_all(env::var("OUT_DIR").unwrap());
    t!(fs::create_dir_all(env::var("OUT_DIR").unwrap()));

    let dst = cfg.define("BUILD_SHARED_LIBS", "OFF")
                 .define("BUILD_CLAR", "OFF")
                 .define("CURL", "OFF")
                 .register_dep("Z")
                 .build();

    // Make sure libssh2 was detected on unix systems, because it definitely
    // should have been!
    if ssh && !msvc {
        let flags = dst.join("build/CMakeFiles/git2.dir/flags.make");
        let mut contents = String::new();
        t!(t!(File::open(flags)).read_to_string(&mut contents));
        if !contents.contains("-DGIT_SSH") {
            panic!("libgit2 failed to find libssh2, and SSH support is required");
        }
    }

    if target.contains("windows") {
        println!("cargo:rustc-link-lib=winhttp");
        println!("cargo:rustc-link-lib=rpcrt4");
        println!("cargo:rustc-link-lib=ole32");
        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=static=git2");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
        return
    }

    println!("cargo:rustc-link-lib=static=git2");
    println!("cargo:rustc-link-search=native={}", dst.join("lib").display());
    if target.contains("apple") {
        println!("cargo:rustc-link-lib=iconv");
        println!("cargo:rustc-link-lib=framework=Security");
        println!("cargo:rustc-link-lib=framework=CoreFoundation");
    }
}

fn register_dep(dep: &str) {
    match env::var(&format!("DEP_{}_ROOT", dep)) {
        Ok(s) => {
            prepend("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        }
        Err(..) => {}
    }
}

fn prepend(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let mut v = vec![val];
    v.extend(env::split_paths(&prefix));
    env::set_var(var, &env::join_paths(v).unwrap());
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Basic syntax highlighting functionality.
//!
//! This module uses libsyntax's lexer to provide token-based highlighting for
//! the HTML documentation generated by rustdoc.
//!
//! If you just want to syntax highlighting for a Rust program, then you can use
//! the `render_inner_with_highlighting` or `render_with_highlighting`
//! functions. For more advanced use cases (if you want to supply your own css
//! classes or control how the HTML is generated, or even generate something
//! other then HTML), then you should implement the the `Writer` trait and use a
//! `Classifier`.

use html::escape::Escape;

use std::fmt::Display;
use std::io;
use std::io::prelude::*;

use syntax::codemap::CodeMap;
use syntax::parse::lexer::{self, Reader, TokenAndSpan};
use syntax::parse::token;
use syntax::parse;
use syntax_pos::Span;

/// Highlights `src`, returning the HTML output.
pub fn render_with_highlighting(src: &str, class: Option<&str>, id: Option<&str>,
                                extension: Option<&str>) -> String {
    debug!("highlighting: ================\n{}\n==============", src);
    let sess = parse::ParseSess::new();
    let fm = sess.codemap().new_filemap("<stdin>".to_string(), None, src.to_string());

    let mut out = Vec::new();
    write_header(class, id, &mut out).unwrap();

    let mut classifier = Classifier::new(lexer::StringReader::new(&sess.span_diagnostic, fm),
                                         sess.codemap());
    if let Err(_) = classifier.write_source(&mut out) {
        return format!("
{}
", src);
    }

    if let Some(extension) = extension {
        write!(out, "{}", extension).unwrap();
    }
    write_footer(&mut out).unwrap();
    String::from_utf8_lossy(&out[..]).into_owned()
}

/// Highlights `src`, returning the HTML output. Returns only the inner html to
/// be inserted into an element. C.f., `render_with_highlighting` which includes
/// an enclosing `<pre>` block.
pub fn render_inner_with_highlighting(src: &str) -> io::Result<String> {
    let sess = parse::ParseSess::new();
    let fm = sess.codemap().new_filemap("<stdin>".to_string(), None, src.to_string());

    let mut out = Vec::new();
    let mut classifier = Classifier::new(lexer::StringReader::new(&sess.span_diagnostic, fm),
                                         sess.codemap());
    classifier.write_source(&mut out)?;

    Ok(String::from_utf8_lossy(&out).into_owned())
}

/// Processes a program (nested in the internal `lexer`), classifying strings of
/// text by highlighting category (`Class`). Calls out to a `Writer` to write
/// each span of text in sequence.
pub struct Classifier<'a> {
    lexer: lexer::StringReader<'a>,
    codemap: &'a CodeMap,

    // State of the classifier.
    in_attribute: bool,
    in_macro: bool,
    in_macro_nonterminal: bool,
}

/// How a span of text is classified. Mostly corresponds to token kinds.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Class {
    None,
    Comment,
    DocComment,
    Attribute,
    KeyWord,
    // Keywords that do pointer/reference stuff.
    RefKeyWord,
    Self_,
    Op,
    Macro,
    MacroNonTerminal,
    String,
    Number,
    Bool,
    Ident,
    Lifetime,
    PreludeTy,
    PreludeVal,
}

/// Trait that controls writing the output of syntax highlighting. Users should
/// implement this trait to customise writing output.
///
/// The classifier will call into the `Writer` implementation as it finds spans
/// of text to highlight. Exactly how that text should be highlighted is up to
/// the implementation.
pub trait Writer {
    /// Called when we start processing a span of text that should be highlighted.
    /// The `Class` argument specifies how it should be highlighted.
    fn enter_span(&mut self, Class) -> io::Result<()>;

    /// Called at the end of a span of highlighted text.
    fn exit_span(&mut self) -> io::Result<()>;

    /// Called for a span of text, usually, but not always, a single token. If
    /// the string of text (`T`) does correspond to a token, then the token will
    /// also be passed. If the text should be highlighted differently from the
    /// surrounding text, then the `Class` argument will be a value other than
    /// `None`.
    /// The following sequences of callbacks are equivalent:
    /// ```plain
    ///     enter_span(Foo), string("text", None), exit_span()
    ///     string("text", Foo)
    /// ```
    /// The latter can be thought of as a shorthand for the former, which is
    /// more flexible.
    fn string<T: Display>(&mut self, T, Class, Option<&TokenAndSpan>) -> io::Result<()>;
}

// Implement `Writer` for anthing that can be written to, this just implements
// the default rustdoc behaviour.
impl Writer for U {
    fn string<T: Display>(&mut self,
                          text: T,
                          klass: Class,
                          _tas: Option<&TokenAndSpan>)
                          -> io::Result<()> {
        match klass {
            Class::None => write!(self, "{}", text),
            klass => write!(self, "{}", klass.rustdoc_class(), text),
        }
    }

    fn enter_span(&mut self, klass: Class) -> io::Result<()> {
        write!(self, "", klass.rustdoc_class())
    }

    fn exit_span(&mut self) -> io::Result<()> {
        write!(self, "")
    }
}

impl<'a> Classifier<'a> {
    pub fn new(lexer: lexer::StringReader<'a>, codemap: &'a CodeMap) -> Classifier<'a> {
        Classifier {
            lexer: lexer,
            codemap: codemap,
            in_attribute: false,
            in_macro: false,
            in_macro_nonterminal: false,
        }
    }

    /// Exhausts the `lexer` writing the output into `out`.
    ///
    /// The general structure for this method is to iterate over each token,
    /// possibly giving it an HTML span with a class specifying what flavor of token
    /// is used. All source code emission is done as slices from the source map,
    /// not from the tokens themselves, in order to stay true to the original
    /// source.
    pub fn write_source<W: Writer>(&mut self,
                                   out: &mut W)
                                   -> io::Result<()> {
        loop {
            let next = match self.lexer.try_next_token() {
                Ok(tas) => tas,
                Err(_) => {
                    self.lexer.emit_fatal_errors();
                    self.lexer.span_diagnostic.struct_warn("Backing out of syntax highlighting")
                                              .note("You probably did not intend to render this \
                                                     as a rust code-block")
                                              .emit();
                    return Err(io::Error::new(io::ErrorKind::Other, ""));
                }
            };

            if next.tok == token::Eof {
                break;
            }

            self.write_token(out, next)?;
        }

        Ok(())
    }

    // Handles an individual token from the lexer.
    fn write_token<W: Writer>(&mut self,
                              out: &mut W,
                              tas: TokenAndSpan)
                              -> io::Result<()> {
        let klass = match tas.tok {
            token::Shebang(s) => {
                out.string(Escape(&s.as_str()), Class::None, Some(&tas))?;
                return Ok(());
            },

            token::Whitespace => Class::None,
            token::Comment => Class::Comment,
            token::DocComment(..) => Class::DocComment,

            // If this '&' token is directly adjacent to another token, assume
            // that it's the address-of operator instead of the and-operator.
            token::BinOp(token::And) if self.lexer.peek().sp.lo == tas.sp.hi => Class::RefKeyWord,

            // Consider this as part of a macro invocation if there was a
            // leading identifier.
            token::Not if self.in_macro => {
                self.in_macro = false;
                Class::Macro
            }

            // Operators.
            token::Eq | token::Lt | token::Le | token::EqEq | token::Ne | token::Ge | token::Gt |
                token::AndAnd | token::OrOr | token::Not | token::BinOp(..) | token::RArrow |
                token::BinOpEq(..) | token::FatArrow => Class::Op,

            // Miscellaneous, no highlighting.
            token::Dot | token::DotDot | token::DotDotDot | token::Comma | token::Semi |
                token::Colon | token::ModSep | token::LArrow | token::OpenDelim(_) |
                token::CloseDelim(token::Brace) | token::CloseDelim(token::Paren) |
                token::CloseDelim(token::NoDelim) |
                token::Question => Class::None,
            token::Dollar => {
                if self.lexer.peek().tok.is_ident() {
                    self.in_macro_nonterminal = true;
                    Class::MacroNonTerminal
                } else {
                    Class::None
                }
            }

            // This is the start of an attribute. We're going to want to
            // continue highlighting it as an attribute until the ending ']' is
            // seen, so skip out early. Down below we terminate the attribute
            // span when we see the ']'.
            token::Pound => {
                self.in_attribute = true;
                out.enter_span(Class::Attribute)?;
                out.string("#", Class::None, None)?;
                return Ok(());
            }
            token::CloseDelim(token::Bracket) => {
                if self.in_attribute {
                    self.in_attribute = false;
                    out.string("]", Class::None, None)?;
                    out.exit_span()?;
                    return Ok(());
                } else {
                    Class::None
                }
            }

            token::Literal(lit, _suf) => {
                match lit {
                    // Text literals.
                    token::Byte(..) | token::Char(..) |
                        token::ByteStr(..) | token::ByteStrRaw(..) |
                        token::Str_(..) | token::StrRaw(..) => Class::String,

                    // Number literals.
                    token::Integer(..) | token::Float(..) => Class::Number,
                }
            }

            // Keywords are also included in the identifier set.
            token::Ident(ident) => {
                match &*ident.name.as_str() {
                    "ref" | "mut" => Class::RefKeyWord,

                    "self" |"Self" => Class::Self_,
                    "false" | "true" => Class::Bool,

                    "Option" | "Result" => Class::PreludeTy,
                    "Some" | "None" | "Ok" | "Err" => Class::PreludeVal,

                    _ if tas.tok.is_any_keyword() => Class::KeyWord,
                    _ => {
                        if self.in_macro_nonterminal {
                            self.in_macro_nonterminal = false;
                            Class::MacroNonTerminal
                        } else if self.lexer.peek().tok == token::Not {
                            self.in_macro = true;
                            Class::Macro
                        } else {
                            Class::Ident
                        }
                    }
                }
            }

            // Special macro vars are like keywords.
            token::SpecialVarNt(_) => Class::KeyWord,

            token::Lifetime(..) => Class::Lifetime,

            token::Underscore | token::Eof | token::Interpolated(..) |
            token::MatchNt(..) | token::SubstNt(..) | token::Tilde | token::At => Class::None,
        };

        // Anything that didn't return above is the simple case where we the
        // class just spans a single token, so we can use the `string` method.
        out.string(Escape(&self.snip(tas.sp)), klass, Some(&tas))
    }

    // Helper function to get a snippet from the codemap.
    fn snip(&self, sp: Span) -> String {
        self.codemap.span_to_snippet(sp).unwrap()
    }
}

impl Class {
    /// Returns the css class expected by rustdoc for each `Class`.
    pub fn rustdoc_class(self) -> &'static str {
        match self {
            Class::None => "",
            Class::Comment => "comment",
            Class::DocComment => "doccomment",
            Class::Attribute => "attribute",
            Class::KeyWord => "kw",
            Class::RefKeyWord => "kw-2",
            Class::Self_ => "self",
            Class::Op => "op",
            Class::Macro => "macro",
            Class::MacroNonTerminal => "macro-nonterminal",
            Class::String => "string",
            Class::Number => "number",
            Class::Bool => "bool-val",
            Class::Ident => "ident",
            Class::Lifetime => "lifetime",
            Class::PreludeTy => "prelude-ty",
            Class::PreludeVal => "prelude-val",
        }
    }
}

fn write_header(class: Option<&str>,
                id: Option<&str>,
                out: &mut Write)
                -> io::Result<()> {
    write!(out, "
\n", class.unwrap_or(""))
}

fn write_footer(out: &mut Write) -> io::Result<()> {
    write!(out, "
\n")
}
<commit_msg>duplicate the<commit_after>// Copyright 2014-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Basic syntax highlighting functionality.
//!
//! This module uses libsyntax's lexer to provide token-based highlighting for
//! the HTML documentation generated by rustdoc.
//!
//! If you just want to syntax highlighting for a Rust program, then you can use
//! the `render_inner_with_highlighting` or `render_with_highlighting`
//! functions. For more advanced use cases (if you want to supply your own css
//! classes or control how the HTML is generated, or even generate something
//! other then HTML), then you should implement the `Writer` trait and use a
//! `Classifier`.

use html::escape::Escape;

use std::fmt::Display;
use std::io;
use std::io::prelude::*;

use syntax::codemap::CodeMap;
use syntax::parse::lexer::{self, Reader, TokenAndSpan};
use syntax::parse::token;
use syntax::parse;
use syntax_pos::Span;

/// Highlights `src`, returning the HTML output.
pub fn render_with_highlighting(src: &str, class: Option<&str>, id: Option<&str>,
                                extension: Option<&str>) -> String {
    debug!("highlighting: ================\n{}\n==============", src);
    let sess = parse::ParseSess::new();
    let fm = sess.codemap().new_filemap("<stdin>".to_string(), None, src.to_string());

    let mut out = Vec::new();
    write_header(class, id, &mut out).unwrap();

    let mut classifier = Classifier::new(lexer::StringReader::new(&sess.span_diagnostic, fm),
                                         sess.codemap());
    if let Err(_) = classifier.write_source(&mut out) {
        return format!("
{}
", src);
    }

    if let Some(extension) = extension {
        write!(out, "{}", extension).unwrap();
    }
    write_footer(&mut out).unwrap();
    String::from_utf8_lossy(&out[..]).into_owned()
}

/// Highlights `src`, returning the HTML output. Returns only the inner html to
/// be inserted into an element. C.f., `render_with_highlighting` which includes
/// an enclosing `<pre>` block.
pub fn render_inner_with_highlighting(src: &str) -> io::Result<String> {
    let sess = parse::ParseSess::new();
    let fm = sess.codemap().new_filemap("<stdin>".to_string(), None, src.to_string());

    let mut out = Vec::new();
    let mut classifier = Classifier::new(lexer::StringReader::new(&sess.span_diagnostic, fm),
                                         sess.codemap());
    classifier.write_source(&mut out)?;

    Ok(String::from_utf8_lossy(&out).into_owned())
}

/// Processes a program (nested in the internal `lexer`), classifying strings of
/// text by highlighting category (`Class`). Calls out to a `Writer` to write
/// each span of text in sequence.
pub struct Classifier<'a> {
    lexer: lexer::StringReader<'a>,
    codemap: &'a CodeMap,

    // State of the classifier.
    in_attribute: bool,
    in_macro: bool,
    in_macro_nonterminal: bool,
}

/// How a span of text is classified. Mostly corresponds to token kinds.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Class {
    None,
    Comment,
    DocComment,
    Attribute,
    KeyWord,
    // Keywords that do pointer/reference stuff.
    RefKeyWord,
    Self_,
    Op,
    Macro,
    MacroNonTerminal,
    String,
    Number,
    Bool,
    Ident,
    Lifetime,
    PreludeTy,
    PreludeVal,
}

/// Trait that controls writing the output of syntax highlighting. Users should
/// implement this trait to customise writing output.
///
/// The classifier will call into the `Writer` implementation as it finds spans
/// of text to highlight. Exactly how that text should be highlighted is up to
/// the implementation.
pub trait Writer {
    /// Called when we start processing a span of text that should be highlighted.
    /// The `Class` argument specifies how it should be highlighted.
    fn enter_span(&mut self, Class) -> io::Result<()>;

    /// Called at the end of a span of highlighted text.
    fn exit_span(&mut self) -> io::Result<()>;

    /// Called for a span of text, usually, but not always, a single token. If
    /// the string of text (`T`) does correspond to a token, then the token will
    /// also be passed. If the text should be highlighted differently from the
    /// surrounding text, then the `Class` argument will be a value other than
    /// `None`.
    /// The following sequences of callbacks are equivalent:
    /// ```plain
    ///     enter_span(Foo), string("text", None), exit_span()
    ///     string("text", Foo)
    /// ```
    /// The latter can be thought of as a shorthand for the former, which is
    /// more flexible.
    fn string<T: Display>(&mut self, T, Class, Option<&TokenAndSpan>) -> io::Result<()>;
}

// Implement `Writer` for anthing that can be written to, this just implements
// the default rustdoc behaviour.
impl Writer for U {
    fn string<T: Display>(&mut self,
                          text: T,
                          klass: Class,
                          _tas: Option<&TokenAndSpan>)
                          -> io::Result<()> {
        match klass {
            Class::None => write!(self, "{}", text),
            klass => write!(self, "{}", klass.rustdoc_class(), text),
        }
    }

    fn enter_span(&mut self, klass: Class) -> io::Result<()> {
        write!(self, "", klass.rustdoc_class())
    }

    fn exit_span(&mut self) -> io::Result<()> {
        write!(self, "")
    }
}

impl<'a> Classifier<'a> {
    pub fn new(lexer: lexer::StringReader<'a>, codemap: &'a CodeMap) -> Classifier<'a> {
        Classifier {
            lexer: lexer,
            codemap: codemap,
            in_attribute: false,
            in_macro: false,
            in_macro_nonterminal: false,
        }
    }

    /// Exhausts the `lexer` writing the output into `out`.
    ///
    /// The general structure for this method is to iterate over each token,
    /// possibly giving it an HTML span with a class specifying what flavor of token
    /// is used. All source code emission is done as slices from the source map,
    /// not from the tokens themselves, in order to stay true to the original
    /// source.
    pub fn write_source<W: Writer>(&mut self,
                                   out: &mut W)
                                   -> io::Result<()> {
        loop {
            let next = match self.lexer.try_next_token() {
                Ok(tas) => tas,
                Err(_) => {
                    self.lexer.emit_fatal_errors();
                    self.lexer.span_diagnostic.struct_warn("Backing out of syntax highlighting")
                                              .note("You probably did not intend to render this \
                                                     as a rust code-block")
                                              .emit();
                    return Err(io::Error::new(io::ErrorKind::Other, ""));
                }
            };

            if next.tok == token::Eof {
                break;
            }

            self.write_token(out, next)?;
        }

        Ok(())
    }

    // Handles an individual token from the lexer.
    fn write_token<W: Writer>(&mut self,
                              out: &mut W,
                              tas: TokenAndSpan)
                              -> io::Result<()> {
        let klass = match tas.tok {
            token::Shebang(s) => {
                out.string(Escape(&s.as_str()), Class::None, Some(&tas))?;
                return Ok(());
            },

            token::Whitespace => Class::None,
            token::Comment => Class::Comment,
            token::DocComment(..) => Class::DocComment,

            // If this '&' token is directly adjacent to another token, assume
            // that it's the address-of operator instead of the and-operator.
            token::BinOp(token::And) if self.lexer.peek().sp.lo == tas.sp.hi => Class::RefKeyWord,

            // Consider this as part of a macro invocation if there was a
            // leading identifier.
            token::Not if self.in_macro => {
                self.in_macro = false;
                Class::Macro
            }

            // Operators.
            token::Eq | token::Lt | token::Le | token::EqEq | token::Ne | token::Ge | token::Gt |
                token::AndAnd | token::OrOr | token::Not | token::BinOp(..) | token::RArrow |
                token::BinOpEq(..) | token::FatArrow => Class::Op,

            // Miscellaneous, no highlighting.
            token::Dot | token::DotDot | token::DotDotDot | token::Comma | token::Semi |
                token::Colon | token::ModSep | token::LArrow | token::OpenDelim(_) |
                token::CloseDelim(token::Brace) | token::CloseDelim(token::Paren) |
                token::CloseDelim(token::NoDelim) |
                token::Question => Class::None,
            token::Dollar => {
                if self.lexer.peek().tok.is_ident() {
                    self.in_macro_nonterminal = true;
                    Class::MacroNonTerminal
                } else {
                    Class::None
                }
            }

            // This is the start of an attribute. We're going to want to
            // continue highlighting it as an attribute until the ending ']' is
            // seen, so skip out early. Down below we terminate the attribute
            // span when we see the ']'.
            token::Pound => {
                self.in_attribute = true;
                out.enter_span(Class::Attribute)?;
                out.string("#", Class::None, None)?;
                return Ok(());
            }
            token::CloseDelim(token::Bracket) => {
                if self.in_attribute {
                    self.in_attribute = false;
                    out.string("]", Class::None, None)?;
                    out.exit_span()?;
                    return Ok(());
                } else {
                    Class::None
                }
            }

            token::Literal(lit, _suf) => {
                match lit {
                    // Text literals.
                    token::Byte(..) | token::Char(..) |
                        token::ByteStr(..) | token::ByteStrRaw(..) |
                        token::Str_(..) | token::StrRaw(..) => Class::String,

                    // Number literals.
                    token::Integer(..) | token::Float(..) => Class::Number,
                }
            }

            // Keywords are also included in the identifier set.
            token::Ident(ident) => {
                match &*ident.name.as_str() {
                    "ref" | "mut" => Class::RefKeyWord,

                    "self" |"Self" => Class::Self_,
                    "false" | "true" => Class::Bool,

                    "Option" | "Result" => Class::PreludeTy,
                    "Some" | "None" | "Ok" | "Err" => Class::PreludeVal,

                    _ if tas.tok.is_any_keyword() => Class::KeyWord,
                    _ => {
                        if self.in_macro_nonterminal {
                            self.in_macro_nonterminal = false;
                            Class::MacroNonTerminal
                        } else if self.lexer.peek().tok == token::Not {
                            self.in_macro = true;
                            Class::Macro
                        } else {
                            Class::Ident
                        }
                    }
                }
            }

            // Special macro vars are like keywords.
            token::SpecialVarNt(_) => Class::KeyWord,

            token::Lifetime(..) => Class::Lifetime,

            token::Underscore | token::Eof | token::Interpolated(..) |
            token::MatchNt(..) | token::SubstNt(..) | token::Tilde | token::At => Class::None,
        };

        // Anything that didn't return above is the simple case where we the
        // class just spans a single token, so we can use the `string` method.
        out.string(Escape(&self.snip(tas.sp)), klass, Some(&tas))
    }

    // Helper function to get a snippet from the codemap.
    fn snip(&self, sp: Span) -> String {
        self.codemap.span_to_snippet(sp).unwrap()
    }
}

impl Class {
    /// Returns the css class expected by rustdoc for each `Class`.
    pub fn rustdoc_class(self) -> &'static str {
        match self {
            Class::None => "",
            Class::Comment => "comment",
            Class::DocComment => "doccomment",
            Class::Attribute => "attribute",
            Class::KeyWord => "kw",
            Class::RefKeyWord => "kw-2",
            Class::Self_ => "self",
            Class::Op => "op",
            Class::Macro => "macro",
            Class::MacroNonTerminal => "macro-nonterminal",
            Class::String => "string",
            Class::Number => "number",
            Class::Bool => "bool-val",
            Class::Ident => "ident",
            Class::Lifetime => "lifetime",
            Class::PreludeTy => "prelude-ty",
            Class::PreludeVal => "prelude-val",
        }
    }
}

fn write_header(class: Option<&str>,
                id: Option<&str>,
                out: &mut Write)
                -> io::Result<()> {
    write!(out, "
\n", class.unwrap_or(""))
}

fn write_footer(out: &mut Write) -> io::Result<()> {
    write!(out, "
\n")
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use codemap;
use codemap::{FileMap, Loc, Pos, span};
use ext::base::*;
use ext::base;
use ext::build::{mk_base_vec_e, mk_uint, mk_u8, mk_base_str};
use print::pprust;

use core::io;
use core::prelude::*;
use core::result;
use core::str;
use core::vec;

/* line!(): expands to the current line number */
pub fn expand_line(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    base::check_zero_tts(cx, sp, tts, "line!");
    let loc = cx.codemap().lookup_char_pos(sp.lo);
    base::MRExpr(mk_uint(cx, sp, loc.line))
}

/* col!(): expands to the current column number */
pub fn expand_col(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    base::check_zero_tts(cx, sp, tts, "col!");
    let loc = cx.codemap().lookup_char_pos(sp.lo);
    base::MRExpr(mk_uint(cx, sp, loc.col.to_uint()))
}

/* file!(): expands to the current filename */
/* The filemap (`loc.file`) contains a bunch more information we could spit
 * out if we wanted. */
pub fn expand_file(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    base::check_zero_tts(cx, sp, tts, "file!");
    let Loc { file: @FileMap { name: filename, _ }, _ } =
        cx.codemap().lookup_char_pos(sp.lo);
    base::MRExpr(mk_base_str(cx, sp, filename))
}

pub fn expand_stringify(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    let s = pprust::tts_to_str(tts, cx.parse_sess().interner);
    base::MRExpr(mk_base_str(cx, sp, s))
}

pub fn expand_mod(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    base::check_zero_tts(cx, sp, tts, "module_path!");
    base::MRExpr(mk_base_str(cx, sp,
                              str::connect(cx.mod_path().map(
                                  |x| cx.str_of(*x)), ~"::")))
}

pub fn expand_include(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    let file = get_single_str_from_tts(cx, sp, tts, "include!");
    let p = parse::new_sub_parser_from_file(
        cx.parse_sess(), cx.cfg(),
        &res_rel_file(cx, sp, &Path(file)), sp);
    base::MRExpr(p.parse_expr())
}

pub fn expand_include_str(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    let file = get_single_str_from_tts(cx, sp, tts, "include_str!");
    let res = io::read_whole_file_str(&res_rel_file(cx, sp, &Path(file)));
    match res {
      result::Ok(_) => { /* Continue. */ }
      result::Err(ref e) => {
        cx.parse_sess().span_diagnostic.handler().fatal((*e));
      }
    }

    base::MRExpr(mk_base_str(cx, sp, result::unwrap(res)))
}

pub fn expand_include_bin(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    let file = get_single_str_from_tts(cx, sp, tts, "include_bin!");
    match io::read_whole_file(&res_rel_file(cx, sp, &Path(file))) {
      result::Ok(src) => {
        let u8_exprs = vec::map(src, |char| {
            mk_u8(cx, sp, *char)
        });
        base::MRExpr(mk_base_vec_e(cx, sp, u8_exprs))
      }
      result::Err(ref e) => {
        cx.parse_sess().span_diagnostic.handler().fatal((*e))
      }
    }
}

fn res_rel_file(cx: ext_ctxt, sp: codemap::span, arg: &Path) -> Path {
    // NB: relative paths are resolved relative to the compilation unit
    if !arg.is_absolute {
        let cu = Path(cx.codemap().span_to_filename(sp));
        cu.dir_path().push_many(arg.components)
    } else {
        copy *arg
    }
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<commit_msg>Use topmost span for macro expansion location. Fixes behaviour of file!, line! and col!<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use codemap;
use codemap::{FileMap, Loc, Pos, ExpandedFrom, span};
use ext::base::*;
use ext::base;
use ext::build::{mk_base_vec_e, mk_uint, mk_u8, mk_base_str};
use print::pprust;

use core::io;
use core::prelude::*;
use core::result;
use core::str;
use core::vec;

fn topmost_expn_info(expn_info: @codemap::ExpnInfo) -> @codemap::ExpnInfo {
    let ExpandedFrom({call_site, _}) = *expn_info;
    match call_site.expn_info {
        Some(next_expn_info) => {
            let ExpandedFrom({callie: {name, _}, _}) = *next_expn_info;
            // Don't recurse into file using "include!"
            if name == ~"include" { return expn_info; }

            topmost_expn_info(next_expn_info)
        },
        None => expn_info
    }
}

/* line!(): expands to the current line number */
pub fn expand_line(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    base::check_zero_tts(cx, sp, tts, "line!");

    let topmost = topmost_expn_info(cx.backtrace().get());
    let loc = cx.codemap().lookup_char_pos(topmost.call_site.lo);

    base::MRExpr(mk_uint(cx, topmost.call_site, loc.line))
}

/* col!(): expands to the current column number */
pub fn expand_col(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    base::check_zero_tts(cx, sp, tts, "col!");

    let topmost = topmost_expn_info(cx.backtrace().get());
    let loc = cx.codemap().lookup_char_pos(topmost.call_site.lo);
    base::MRExpr(mk_uint(cx, topmost.call_site, loc.col.to_uint()))
}

/* file!(): expands to the current filename */
/* The filemap (`loc.file`) contains a bunch more information we could spit
 * out if we wanted. */
pub fn expand_file(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    base::check_zero_tts(cx, sp, tts, "file!");

    let topmost = topmost_expn_info(cx.backtrace().get());
    let Loc { file: @FileMap { name: filename, _ }, _ } =
        cx.codemap().lookup_char_pos(topmost.call_site.lo);
    base::MRExpr(mk_base_str(cx, topmost.call_site, filename))
}

pub fn expand_stringify(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    let s = pprust::tts_to_str(tts, cx.parse_sess().interner);
    base::MRExpr(mk_base_str(cx, sp, s))
}

pub fn expand_mod(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    base::check_zero_tts(cx, sp, tts, "module_path!");
    base::MRExpr(mk_base_str(cx, sp,
                              str::connect(cx.mod_path().map(
                                  |x| cx.str_of(*x)), ~"::")))
}

pub fn expand_include(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    let file = get_single_str_from_tts(cx, sp, tts, "include!");
    let p = parse::new_sub_parser_from_file(
        cx.parse_sess(), cx.cfg(),
        &res_rel_file(cx, sp, &Path(file)), sp);
    base::MRExpr(p.parse_expr())
}

pub fn expand_include_str(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    let file = get_single_str_from_tts(cx, sp, tts, "include_str!");
    let res = io::read_whole_file_str(&res_rel_file(cx, sp, &Path(file)));
    match res {
      result::Ok(_) => { /* Continue. */ }
      result::Err(ref e) => {
        cx.parse_sess().span_diagnostic.handler().fatal((*e));
      }
    }

    base::MRExpr(mk_base_str(cx, sp, result::unwrap(res)))
}

pub fn expand_include_bin(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree])
    -> base::MacResult {
    let file = get_single_str_from_tts(cx, sp, tts, "include_bin!");
    match io::read_whole_file(&res_rel_file(cx, sp, &Path(file))) {
      result::Ok(src) => {
        let u8_exprs = vec::map(src, |char| {
            mk_u8(cx, sp, *char)
        });
        base::MRExpr(mk_base_vec_e(cx, sp, u8_exprs))
      }
      result::Err(ref e) => {
        cx.parse_sess().span_diagnostic.handler().fatal((*e))
      }
    }
}

fn res_rel_file(cx: ext_ctxt, sp: codemap::span, arg: &Path) -> Path {
    // NB: relative paths are resolved relative to the compilation unit
    if !arg.is_absolute {
        let cu = Path(cx.codemap().span_to_filename(sp));
        cu.dir_path().push_many(arg.components)
    } else {
        copy *arg
    }
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<|endoftext|>


<commit_before>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;
extern crate github;

use self::github::notification::PushNotification;
use self::github::clone_url::CloneUrl;
use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pub struct PendingBuild {
    pub clone_url: CloneUrl,
    pub branch: String,
    build_id: i32
}

/// Type A is some key
pub trait Database : Send {
    fn add_pending(&self, entry: PushNotification);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<PendingBuild>;

    fn add_test_results(&self, entry: &PendingBuild, results: BuildResult);
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    extern crate time;
    extern crate pg_typeprovider;
    extern crate github;
    extern crate openssl;

    use super::PendingBuild;

    use self::github::notification::PushNotification;
    use self::github::clone_url::CloneUrl;

    use self::time::{now, Timespec};
    use self::pg_typeprovider::util::Joinable;

    use self::openssl::ssl::{SslContext, SslMethod};

    use super::postgres::{Connection, GenericConnection, SslMode, ToSql};

    use builder::BuildResult;

    use super::EntryStatus::{Pending, InProgress, Done};
    use super::Database;

    // TODO: these must all be string literals, so we can't
    // simply define a constant.  Defining a macro is also not
    // enough, since pg_table! will not expand passed macros (yet),
    // so it sees only a macro invocation.
    pg_table!(builds, "postgres://jroesch@localhost/gradr-test")
    pg_table!(commits, "postgres://jroesch@localhost/gradr-test")
    pg_table!(users, "postgres://jroesch@localhost/gradr-test")
    pg_table!(assignments, "postgres://jroesch@localhost/gradr-test")
    pg_table!(submissions, "postgres://jroesch@localhost/gradr-test")

    pub struct PostgresDatabase {
        db: Connection
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(
                loc,
                &SslMode::Require(
                    SslContext::new(SslMethod::Sslv23).unwrap())).ok().map(|db| {
                    PostgresDatabase {
                        db: db
                    }
                })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-test");
            match retval {
                Some(ref db) => {
                    db.with_connection(|conn| {
                        let delete_table = |s: &str| {
                            conn.execute(
                                format!("DELETE FROM {}", s).as_slice(),
                                &[]).unwrap();
                        };
                        delete_table("builds");
                        delete_table("commits");
                        delete_table("submissions");
                    })
                },
                None => ()
            };
            retval
        }

        pub fn with_connection<A>(&self, f: |&Connection| -> A) -> A {
            f(&self.db)
        }
    }

    fn get_one_build(conn: &GenericConnection) -> Option<Build> {
        BuildSearch::new()
            .where_status((&Pending).to_int())
            .search(conn, Some(1)).pop()
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &GenericConnection, b: &Build) -> bool {
        BuildUpdate::new()
            .status_to((&InProgress).to_int())
            .where_id(b.id)
            .where_status((&Pending).to_int())
            .update(conn) == 1
    }

    trait ToPendingBuild {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild;
    }

    impl ToPendingBuild for Build {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild {
            let commit = CommitSearch::new()
                .where_id(self.commit_id)
                .search(conn, Some(1))
                .pop()
                .unwrap(); // should be in there
            PendingBuild {
                clone_url: CloneUrl::new_from_str(
                    commit.clone_url.as_slice()).unwrap(),
                branch: commit.branch_name,
                build_id: self.id
            }
        }
    }

    fn get_user_by_github_username(conn: &GenericConnection,
                                   name: &str) -> Option<User> {
        UserSearch::new()
            .where_github_username(name.to_string())
            .search(conn, Some(1))
            .pop()
    }

    fn get_assignment_by_git_project_name(conn: &GenericConnection,
                                          project_name: &str) -> Option<Assignment> {
        AssignmentSearch::new()
            .where_git_project_name(project_name.to_string())
            .search(conn, Some(1))
            .pop()
    }

    fn insert_submission(conn: &GenericConnection,
                         user: &User,
                         assignment: &Assignment) -> Submission {
        let current_time = now().to_timespec();
        SubmissionInsert {
            user_id: user.id,
            assignment_id: assignment.id,
            created_at: current_time.clone(),
            updated_at: current_time.clone()
        }.insert(conn);
        SubmissionSearch::new()
            .where_created_at(current_time)
            .search(conn, Some(1))
            .pop()
            .unwrap()
    }

    fn insert_commit(conn: &GenericConnection,
                     user: &User,
                     assignment: &Assignment,
                     submission: &Submission,
                     pn: &PushNotification) -> Commit {
        let current_time = now().to_timespec();
        CommitInsert {
            assignment_id: assignment.id,
            user_id: user.id,
            created_at: current_time.clone(),
            updated_at: current_time.clone(),
            submission_id: submission.id,
            branch_name: pn.branch.clone(),
            clone_url: pn.clone_url.url.to_string()
        }.insert(conn);
        CommitSearch::new()
            .where_created_at(current_time)
            .search(conn, Some(1))
            .pop()
            .unwrap()
    }

    fn insert_build(conn: &GenericConnection,
                    user: &User,
                    assignment: &Assignment,
                    commit: &Commit) {
        let current_time = now().to_timespec();
        BuildInsert {
            commit_id: commit.id,
            user_id: user.id,
            assignment_id: assignment.id,
            course_id: assignment.course_id,
            created_at: current_time.clone(),
            updated_at: current_time,
            status: (&Pending).to_int(),
            results: "".to_string()
        }.insert(conn);
    }

    impl Database for PostgresDatabase {
        fn add_pending(&self, entry: PushNotification) {
            self.with_connection(|conn| {
                let trans = conn.transaction().unwrap();
                let op_user = get_user_by_github_username(
                    &trans, entry.clone_url.username());
                let op_assignment = get_assignment_by_git_project_name(
                    &trans, entry.clone_url.project_name());
                match (op_user, op_assignment) {
                    (Some(user), Some(assignment)) => {
                        let submission = insert_submission(&trans,
                                                           &user,
                                                           &assignment);
                        let commit = insert_commit(&trans,
                                                   &user,
                                                   &assignment,
                                                   &submission,
                                                   &entry);
                        insert_build(&trans, &user, &assignment, &commit);
                        trans.commit().unwrap();
                    },
                    _ => {
                        trans.set_rollback();
                        trans.finish().unwrap();
                    }
                }
            });
        }

        fn get_pending(&self) -> Option<PendingBuild> {
            self.with_connection(|conn| {
                loop {
                    match get_one_build(conn) {
                        Some(b) => {
                            if try_lock_build(conn, &b) {
                                return Some(b.to_pending_build(conn));
                            }
                        },
                        None => { return None; }
                    }
                }

                // Code below uses a transaction to accomplish the same
                // thing.  In small tests there doesn't seem to be any
                // difference in timing, so I'm defaulting to the simpler,
                // lock-free approach above
                // loop {
                //     let start_time = current_time_millis();
                //     let trans = conn.transaction().unwrap();
                //     match get_one_build(&trans) {
                //         Some(b) => {
                //             let res = try_lock_build(&trans, &b);
                //             assert!(res);
                //             if trans.commit().is_ok() {
                //                 let end_time = current_time_millis();
                //                 println!("TIME TAKEN: {}ms", end_time - start_time);
                //                 return Some(b.to_pending_build(conn))
                //             }
                //         },
                //         None => { return None; }
                //     }
                // }
            })
        }

        fn add_test_results(&self, entry: &PendingBuild, results: BuildResult) {
            let num_updated =
                BuildUpdate::new()
                .status_to((&Done).to_int())
                .results_to(results.consume_to_json().to_string())
                .where_id(entry.build_id)
                // not using with_connection to avoid copying results
                .update(&self.db);
            assert_eq!(num_updated, 1);
        }
    }
}
<commit_msg>Now sets the updated_at field when adding test results.<commit_after>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;
extern crate github;

use self::github::notification::PushNotification;
use self::github::clone_url::CloneUrl;
use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pub struct PendingBuild {
    pub clone_url: CloneUrl,
    pub branch: String,
    build_id: i32
}

/// Type A is some key
pub trait Database : Send {
    fn add_pending(&self, entry: PushNotification);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<PendingBuild>;

    fn add_test_results(&self, entry: &PendingBuild, results: BuildResult);
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    extern crate time;
    extern crate pg_typeprovider;
    extern crate github;
    extern crate openssl;

    use super::PendingBuild;

    use self::github::notification::PushNotification;
    use self::github::clone_url::CloneUrl;

    use self::time::{now, Timespec};
    use self::pg_typeprovider::util::Joinable;

    use self::openssl::ssl::{SslContext, SslMethod};

    use super::postgres::{Connection, GenericConnection, SslMode, ToSql};

    use builder::BuildResult;

    use super::EntryStatus::{Pending, InProgress, Done};
    use super::Database;

    // TODO: these must all be string literals, so we can't
    // simply define a constant.  Defining a macro is also not
    // enough, since pg_table! will not expand passed macros (yet),
    // so it sees only a macro invocation.
    pg_table!(builds, "postgres://jroesch@localhost/gradr-test")
    pg_table!(commits, "postgres://jroesch@localhost/gradr-test")
    pg_table!(users, "postgres://jroesch@localhost/gradr-test")
    pg_table!(assignments, "postgres://jroesch@localhost/gradr-test")
    pg_table!(submissions, "postgres://jroesch@localhost/gradr-test")

    pub struct PostgresDatabase {
        db: Connection
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(
                loc,
                &SslMode::Require(
                    SslContext::new(SslMethod::Sslv23).unwrap())).ok().map(|db| {
                    PostgresDatabase {
                        db: db
                    }
                })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-test");
            match retval {
                Some(ref db) => {
                    db.with_connection(|conn| {
                        let delete_table = |s: &str| {
                            conn.execute(
                                format!("DELETE FROM {}", s).as_slice(),
                                &[]).unwrap();
                        };
                        delete_table("builds");
                        delete_table("commits");
                        delete_table("submissions");
                    })
                },
                None => ()
            };
            retval
        }

        pub fn with_connection<A>(&self, f: |&Connection| -> A) -> A {
            f(&self.db)
        }
    }

    fn get_one_build(conn: &GenericConnection) -> Option<Build> {
        BuildSearch::new()
            .where_status((&Pending).to_int())
            .search(conn, Some(1)).pop()
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &GenericConnection, b: &Build) -> bool {
        BuildUpdate::new()
            .status_to((&InProgress).to_int())
            .where_id(b.id)
            .where_status((&Pending).to_int())
            .update(conn) == 1
    }

    trait ToPendingBuild {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild;
    }

    impl ToPendingBuild for Build {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild {
            let commit = CommitSearch::new()
                .where_id(self.commit_id)
                .search(conn, Some(1))
                .pop()
                .unwrap(); // should be in there
            PendingBuild {
                clone_url: CloneUrl::new_from_str(
                    commit.clone_url.as_slice()).unwrap(),
                branch: commit.branch_name,
                build_id: self.id
            }
        }
    }

    fn get_user_by_github_username(conn: &GenericConnection,
                                   name: &str) -> Option<User> {
        UserSearch::new()
            .where_github_username(name.to_string())
            .search(conn, Some(1))
            .pop()
    }

    fn get_assignment_by_git_project_name(conn: &GenericConnection,
                                          project_name: &str) -> Option<Assignment> {
        AssignmentSearch::new()
            .where_git_project_name(project_name.to_string())
            .search(conn, Some(1))
            .pop()
    }

    fn insert_submission(conn: &GenericConnection,
                         user: &User,
                         assignment: &Assignment) -> Submission {
        let current_time = now().to_timespec();
        SubmissionInsert {
            user_id: user.id,
            assignment_id: assignment.id,
            created_at: current_time.clone(),
            updated_at: current_time.clone()
        }.insert(conn);
        SubmissionSearch::new()
            .where_created_at(current_time)
            .search(conn, Some(1))
            .pop()
            .unwrap()
    }

    fn insert_commit(conn: &GenericConnection,
                     user: &User,
                     assignment: &Assignment,
                     submission: &Submission,
                     pn: &PushNotification) -> Commit {
        let current_time = now().to_timespec();
        CommitInsert {
            assignment_id: assignment.id,
            user_id: user.id,
            created_at: current_time.clone(),
            updated_at: current_time.clone(),
            submission_id: submission.id,
            branch_name: pn.branch.clone(),
            clone_url: pn.clone_url.url.to_string()
        }.insert(conn);
        CommitSearch::new()
            .where_created_at(current_time)
            .search(conn, Some(1))
            .pop()
            .unwrap()
    }

    fn insert_build(conn: &GenericConnection,
                    user: &User,
                    assignment: &Assignment,
                    commit: &Commit) {
        let current_time = now().to_timespec();
        BuildInsert {
            commit_id: commit.id,
            user_id: user.id,
            assignment_id: assignment.id,
            course_id: assignment.course_id,
            created_at: current_time.clone(),
            updated_at: current_time,
            status: (&Pending).to_int(),
            results: "".to_string()
        }.insert(conn);
    }

    impl Database for PostgresDatabase {
        fn add_pending(&self, entry: PushNotification) {
            self.with_connection(|conn| {
                let trans = conn.transaction().unwrap();
                let op_user = get_user_by_github_username(
                    &trans, entry.clone_url.username());
                let op_assignment = get_assignment_by_git_project_name(
                    &trans, entry.clone_url.project_name());
                match (op_user, op_assignment) {
                    (Some(user), Some(assignment)) => {
                        let submission = insert_submission(&trans,
                                                           &user,
                                                           &assignment);
                        let commit = insert_commit(&trans,
                                                   &user,
                                                   &assignment,
                                                   &submission,
                                                   &entry);
                        insert_build(&trans, &user, &assignment, &commit);
                        trans.commit().unwrap();
                    },
                    _ => {
                        trans.set_rollback();
                        trans.finish().unwrap();
                    }
                }
            });
        }

        fn get_pending(&self) -> Option<PendingBuild> {
            self.with_connection(|conn| {
                loop {
                    match get_one_build(conn) {
                        Some(b) => {
                            if try_lock_build(conn, &b) {
                                return Some(b.to_pending_build(conn));
                            }
                        },
                        None => { return None; }
                    }
                }

                // Code below uses a transaction to accomplish the same
                // thing.  In small tests there doesn't seem to be any
                // difference in timing, so I'm defaulting to the simpler,
                // lock-free approach above
                // loop {
                //     let start_time = current_time_millis();
                //     let trans = conn.transaction().unwrap();
                //     match get_one_build(&trans) {
                //         Some(b) => {
                //             let res = try_lock_build(&trans, &b);
                //             assert!(res);
                //             if trans.commit().is_ok() {
                //                 let end_time = current_time_millis();
                //                 println!("TIME TAKEN: {}ms", end_time - start_time);
                //                 return Some(b.to_pending_build(conn))
                //             }
                //         },
                //         None => { return None; }
                //     }
                // }
            })
        }

        fn add_test_results(&self, entry: &PendingBuild, results: BuildResult) {
            let num_updated =
                BuildUpdate::new()
                .status_to((&Done).to_int())
                .results_to(results.consume_to_json().to_string())
                .updated_at_to(now().to_timespec())
                .where_id(entry.build_id)
                // not using with_connection to avoid copying results
                .update(&self.db);
            assert_eq!(num_updated, 1);
        }
    }
}
<|endoftext|>