<commit_before>#![feature(convert)]

use std::env;
use std::path::{PathBuf};
use std::process::{Command, Stdio};

static STATIC_LIB_NAME: &'static str = "liblmdb.a";

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    let res = cmd.stdout(Stdio::inherit())
                 .stderr(Stdio::inherit())
                 .status()
                 .unwrap()
                 .success();
    assert!(res);
}

fn ios_cflags(target: &str) -> String {
    let mut cflags = String::new();

    let (sdk_name, sdk_min_ver) = if target.contains("arm") | target.contains("aarch64") {
        ("iphoneos", "ios-version-min")
    } else {
        ("iphonesimulator", "ios-simulator-version-min")
    };

    let sdk_output = Command::new("xcrun").arg("--show-sdk-path")
                                          .arg("--sdk")
                                          .arg(sdk_name)
                                          .stderr(Stdio::inherit())
                                          .output()
                                          .unwrap()
                                          .stdout;
    let sdk_path = String::from_utf8_lossy(&sdk_output);

    let target = target.replace("aarch64-", "arm64-");

    let flags = format!(" -target {} -isysroot {} -m{}=7.0", target, sdk_path.trim(), sdk_min_ver);
    cflags.push_str(&flags);

    // Actually only "arm" arch requires a little patching
    // other branches simply filter out invalid archs
    if !target.starts_with("arm-")
        && !target.starts_with("armv7-")
        && !target.starts_with("arm64-")
        && !target.starts_with("aarch64-")
        && !target.starts_with("armv7s-")
        && !target.starts_with("x86_64-")
        && !target.starts_with("i386-") {
            panic!("Unsupported target for iOS: `{}`", target)
    }

    if target.starts_with("arm-") {
        cflags.push_str(&format!(" -arch armv7"));
    }

    cflags
}

fn cflags() -> String {
    let mut cflags = env::var("CFLAGS").unwrap_or(String::new());

    let target = env::var("TARGET").unwrap();
    // let profile = os::getenv("PROFILE").unwrap();

    if target.contains("-ios") {
        cflags.push_str(" ");
        cflags.push_str(&ios_cflags(&target));
    } else {
        if target.contains("i686") || target.contains("i386") {
            cflags.push_str(" -m32");
        } else if target.contains("x86_64") {
            cflags.push_str(" -m64");
        }

        if !target.contains("i686") {
            cflags.push_str(" -fPIC");
        }
    }

    cflags
}

fn main() {
    let root = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap());
    let dst = PathBuf::from(&env::var("OUT_DIR").unwrap());

    let mut mdb_root = root.clone();

    for sub_dir in vec!["mdb", "libraries", "liblmdb"].iter() {
        mdb_root.push(sub_dir)
    }

    let lib_dir = dst.clone();

    let mut clean_cmd = Command::new("make");
    clean_cmd.arg("-C").arg(&mdb_root);
    clean_cmd.arg("clean");
    run(&mut clean_cmd);

    let mut build_cmd = Command::new("make");
    build_cmd.arg("-C").arg(&mdb_root);
    build_cmd.arg("liblmdb.a");
    build_cmd.arg(&format!("XCFLAGS={}", cflags()));
    run(&mut build_cmd);

    run(Command::new("cp")
        .arg(&mdb_root.join(STATIC_LIB_NAME))
        .arg(&lib_dir.join(STATIC_LIB_NAME)));

    println!("cargo:rustc-flags=-L {} -l lmdb:static", lib_dir.display());
}
<commit_msg>Updated rustc -l format.<commit_after>#![feature(convert)]

use std::env;
use std::path::{PathBuf};
use std::process::{Command, Stdio};

static STATIC_LIB_NAME: &'static str = "liblmdb.a";

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    let res = cmd.stdout(Stdio::inherit())
                 .stderr(Stdio::inherit())
                 .status()
                 .unwrap()
                 .success();
    assert!(res);
}

fn ios_cflags(target: &str) -> String {
    let mut cflags = String::new();

    let (sdk_name, sdk_min_ver) = if target.contains("arm") | target.contains("aarch64") {
        ("iphoneos", "ios-version-min")
    } else {
        ("iphonesimulator", "ios-simulator-version-min")
    };

    let sdk_output = Command::new("xcrun").arg("--show-sdk-path")
                                          .arg("--sdk")
                                          .arg(sdk_name)
                                          .stderr(Stdio::inherit())
                                          .output()
                                          .unwrap()
                                          .stdout;
    let sdk_path = String::from_utf8_lossy(&sdk_output);

    let target = target.replace("aarch64-", "arm64-");

    let flags = format!(" -target {} -isysroot {} -m{}=7.0", target, sdk_path.trim(), sdk_min_ver);
    cflags.push_str(&flags);

    // Actually only "arm" arch requires a little patching
    // other branches simply filter out invalid archs
    if !target.starts_with("arm-")
        && !target.starts_with("armv7-")
        && !target.starts_with("arm64-")
        && !target.starts_with("aarch64-")
        && !target.starts_with("armv7s-")
        && !target.starts_with("x86_64-")
        && !target.starts_with("i386-") {
            panic!("Unsupported target for iOS: `{}`", target)
    }

    if target.starts_with("arm-") {
        cflags.push_str(&format!(" -arch armv7"));
    }

    cflags
}

fn cflags() -> String {
    let mut cflags = env::var("CFLAGS").unwrap_or(String::new());

    let target = env::var("TARGET").unwrap();
    // let profile = os::getenv("PROFILE").unwrap();

    if target.contains("-ios") {
        cflags.push_str(" ");
        cflags.push_str(&ios_cflags(&target));
    } else {
        if target.contains("i686") || target.contains("i386") {
            cflags.push_str(" -m32");
        } else if target.contains("x86_64") {
            cflags.push_str(" -m64");
        }

        if !target.contains("i686") {
            cflags.push_str(" -fPIC");
        }
    }

    cflags
}

fn main() {
    let root = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap());
    let dst = PathBuf::from(&env::var("OUT_DIR").unwrap());

    let mut mdb_root = root.clone();

    for sub_dir in vec!["mdb", "libraries", "liblmdb"].iter() {
        mdb_root.push(sub_dir)
    }

    let lib_dir = dst.clone();

    let mut clean_cmd = Command::new("make");
    clean_cmd.arg("-C").arg(&mdb_root);
    clean_cmd.arg("clean");
    run(&mut clean_cmd);

    let mut build_cmd = Command::new("make");
    build_cmd.arg("-C").arg(&mdb_root);
    build_cmd.arg("liblmdb.a");
    build_cmd.arg(&format!("XCFLAGS={}", cflags()));
    run(&mut build_cmd);

    run(Command::new("cp")
        .arg(&mdb_root.join(STATIC_LIB_NAME))
        .arg(&lib_dir.join(STATIC_LIB_NAME)));

    println!("cargo:rustc-flags=-L {} -l static=lmdb", lib_dir.display());
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Parsing and loading Executable and Linkable Format (ELF) 32- and 64-bit
//! binaries.
//!
//! For more information on the ELF format, refer to:
//!
//!  + [Wikipedia](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format)
//!  + The [OS Dev Wiki](http://wiki.osdev.org/ELF)
//!  + The [ELF Format Specification](elfspec)
//!
//! [elfspec]: http://www.skyfree.org/linux/references/ELF_Format.pdf
#![feature(core_intrinsics)]
#![feature(try_from)]
#![no_std]

#[macro_use] extern crate bitflags;
#[macro_use] extern crate macro_attr;

extern crate memory;

use core::{ intrinsics, ops, mem, slice, convert };
use core::convert::TryFrom;

use memory::{ FrameRange, PhysicalPage };

macro_rules! impl_getters {
    ($(#[$attr:meta])* pub fn $name:ident(&self) -> $ty:ty; $($rest:tt)*) => {
        $(#[$attr])* #[inline] pub fn $name(&self) -> $ty { self.$name as $ty }
        impl_getters!{ $( $rest )* }
    };
    ($(#[$attr:meta])* fn $name:ident(&self) -> $ty:ty; $($rest:tt)*) => {
        $(#[$attr])* #[inline] fn $name(&self) -> $ty { self.$name as $ty }
        impl_getters!{ $( $rest )* }
    };
    ( $(#[$attr:meta])* pub fn $name: ident (&self)-> $ty:ty; ) => {
        $(#[$attr])* #[inline] pub fn $name(&self) -> $ty { self.$name as $ty }
    };
    ( $(#[$attr:meta])* fn $name: ident (&self)-> $ty:ty; ) => {
        $(#[$attr])* #[inline] fn $name(&self) -> $ty { self.$name as $ty }
    };
    () => {};
}

pub mod section;
pub mod file;
pub mod program;

/// An ELF section header.
pub type Section<W> = section::Header<Word = W>;
pub type ProgramHeader<W> = program::Header<Word = W>;
/// An ELF header file.
pub type FileHeader<W> = file::HeaderRepr<W>;

/// TODO: should ELF have its own error type?
pub type ElfResult<T> = Result<T, &'static str>;

pub trait ElfWord: Sized + Copy + Clone
                         + ops::Add<Self> + ops::Sub<Self>
                         + ops::Mul<Self> + ops::Div<Self>
                         + ops::Shl<Self> + ops::Shr<Self> { }
impl ElfWord for u64 { }
impl ElfWord for u32 { }

#[cfg(target_pointer_width = "32")]
type DefaultWord = u32;
#[cfg(target_pointer_width = "64")]
type DefaultWord = u64;

/// Hack to make the type-system let me do what I want
trait ValidatesWord<Word: ElfWord> {
    fn check(&self) -> ElfResult<()>;
}

/// A handle on a parsed ELF binary
///  TODO: do we want this to own a HashMap of section names to section headers,
///        to speed up section lookup?
//          - eliza, 03/08/2017
#[derive(Debug)]
pub struct Image< 'bytes             // lifetime of the byte slice
                , Word = DefaultWord // default to machine's pointer size
                , ProgHeader = ProgramHeader<Word> // same word type
                , SectHeader = Section<Word>
                , Header = FileHeader<Word> // must have same word type
                > // jesus christ
where Word: ElfWord + 'bytes
    , ProgHeader: program::Header<Word = Word> + Sized + 'bytes
    , SectHeader: section::Header<Word = Word> + Sized + 'bytes
    , Header: file::Header<Word = Word> + 'bytes
    {
    /// the binary's [file header](file/trait.Header.html)
    pub header: &'bytes Header
  , /// references to each [section header](section/struct.Header.html)
    pub sections: &'bytes [SectHeader]
  , /// references to each [program header](program/trait.Header.html)
    pub program_headers: &'bytes [ProgHeader]
  , /// the raw binary contents of the ELF binary.
    /// note that this includes the _entire_ binary contents of the file,
    /// so the file header and each section header is included in this slice.
    binary: &'bytes [u8]
}

impl <'a, Word, ProgHeader, SectHeader, Header>
Image<'a, Word, ProgHeader, SectHeader, Header>
where Word: ElfWord + 'a
    , ProgHeader: program::Header<Word = Word> + Sized + 'a
    , SectHeader: section::Header<Word = Word> + Sized + 'a
    , Header: file::Header<Word = Word> + 'a
    {
    /// Returns the section header [string table].
    ///
    /// [string table]: section/struct.StrTable.html
    pub fn sh_str_table(&'a self) -> section::StrTable<'a> {
        // TODO: do we want to validate that the string table index is
        //       reasonable (e.g. it's not longer than the binary)?
        //          - eliza, 03/08/2017
        // TODO: do we want to cache a ref to the string table?
        //          - eliza, 03/08/2017
        section::StrTable::from(&self.binary[self.header.sh_str_idx()..])
    }

}

impl<'a, Word, PH, SH, FH> TryFrom<&'a [u8]> for Image<'a, Word, PH, SH, FH>
where Word: ElfWord + 'a
    , PH: program::Header<Word = Word> + 'a
    , SH: section::Header<Word = Word> + 'a
    , FH: file::Header<Word = Word> + 'a
    , &'a FH: convert::TryFrom<&'a [u8], Error = &'static str>
    {

    type Error = &'static str;

    fn try_from(bytes: &'a [u8]) -> ElfResult<Self> {
        let header: &'a FH = <&'a FH>::try_from(bytes)?;

        let sections = unsafe { extract_from_slice::<SH>(
            &bytes[header.sh_range()]
          , 0
          , header.sh_count()
        )? };
        let prog_headers = unsafe { extract_from_slice::<PH>(
            &bytes[header.ph_range()]
          , 0
          , header.ph_count()
        )? };
        Ok(Image { header: header
              , sections: sections
              , program_headers: prog_headers
              , binary: bytes
        })
    }
}

/// Extract `n` instances of type `T` from a byte slice.
///
/// This is essentially just a _slightly_ safer wrapper around
/// [`slice::from_raw_parts`]. Unlike `from_raw_parts`, this function takes
/// a valid byte slice, rather than a pointer. Therefore, some of the safety
/// issues with `from_raw_parts` are avoided:
///
/// + the lifetime (`'slice`) of the returned slice should be the same as the
///   lifetime of the input slice (`data`), rather than inferred arbitrarily.
/// + this function will panic rather than reading past the end of the slice.
///
/// # Arguments
///
/// + `data`: the byte slice to extract a slice of `&[T]`s from
/// + `offset`: a start offset into `data`
/// + `n`: the number of instances of `T` which should be contained
///        in `data[offset..]`
///
/// # Safety
///
/// While this function is safer than [`slice::from_raw_parts`],
/// it is still unsafe for the following reasons:
///
/// + The contents of `data` may not be able to be interpreted as instances of
///   type `T`.
///
/// # Caveats
///
/// + If `n` == 0, this will give you an `&[]`. Just a warning.
//    thanks to Max for making  me figure this out.
/// + `offset` must be aligned on a `T`-sized boundary.
///
/// # Panics
///
/// + If the index `offset` is longer than `T`
///
/// TODO: rewrite this as a `TryFrom` implementation (see issue #85)
//          - eliza, 03/09/2017
///       wait, possibly we should NOT do that. actually we should
///       almost certainly not do that. since this function is unsafe,
///       but `TryFrom` is not, and because this would be WAY generic.
//          - eliza, 03/09/2017
/// TODO: is this general enough to move into util?
//          - eliza, 03/09/2017
/// TODO: refactor this to take a `RangeArgument`?
//          - eliza, 03/13/2017
///       or, we could just remove the offset and expect the caller to
///       offset the slice?
//          - eliza, 03/14/2017
///
/// [`slice::from_raw_parts`]: https://doc.rust-lang.org/stable/std/slice/fn.from_raw_parts.html
unsafe fn extract_from_slice<'slice, T: Sized>( data: &'slice [u8]
                                              , offset: usize
                                              , n: usize)
                                              -> ElfResult<&'slice [T]> {
    if offset % mem::align_of::<T>() != 0 {
        // TODO: these error messages don't contain as much information as they
        //       used to, since the return type is `&'static str` that can't be
        //       dynamically formatted as the panic was. refactor this?
        //       (e.g. should ELF get its own error type?)
        //        - eliza, 03/15/2017
        Err("extract_from_slice: Offset not aligned on type T sized boundary!")
        // assert!(
        //        , "Offset {} not aligned on a {}-sized boundary (must be \
        //           divisible by {})."
        //        , offset, type_name::<T>(), mem::align_of::<T>()
        //        );
    } else if data.len() - offset < mem::size_of::<T>() * n {
        Err("extract_from_slice: Slice too short to contain n instances of T!")
        // assert!(
        //        , "Slice too short to contain {} objects of type {}"
        //        , n, type_name::<T>()
        //        );
    } else {
        Ok(slice::from_raw_parts(data[offset..].as_ptr() as *const T, n))
    }
}

impl<'a> convert::Into<FrameRange> for Section<'a> {
    #[inline]
    fn into(self) -> FrameRange {
        let start = PhysicalPage::from(self.addr());
        let end = PhysicalPage::from(self.end_addr());
        start .. end
    }
}
<commit_msg>fix(elf): temporary hacky fix for convert::Into<FrameRange> for Section<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Parsing and loading Executable and Linkable Format (ELF) 32- and 64-bit
//! binaries.
//!
//! For more information on the ELF format, refer to:
//!
//!  + [Wikipedia](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format)
//!  + The [OS Dev Wiki](http://wiki.osdev.org/ELF)
//!  + The [ELF Format Specification](elfspec)
//!
//! [elfspec]: http://www.skyfree.org/linux/references/ELF_Format.pdf
#![feature(core_intrinsics)]
#![feature(try_from)]
#![no_std]

#[macro_use] extern crate bitflags;
#[macro_use] extern crate macro_attr;

extern crate memory;

use core::{ intrinsics, ops, mem, slice, convert };
use core::convert::TryFrom;

use memory::{ FrameRange, PhysicalPage };

macro_rules! impl_getters {
    ($(#[$attr:meta])* pub fn $name:ident(&self) -> $ty:ty; $($rest:tt)*) => {
        $(#[$attr])* #[inline] pub fn $name(&self) -> $ty { self.$name as $ty }
        impl_getters!{ $( $rest )* }
    };
    ($(#[$attr:meta])* fn $name:ident(&self) -> $ty:ty; $($rest:tt)*) => {
        $(#[$attr])* #[inline] fn $name(&self) -> $ty { self.$name as $ty }
        impl_getters!{ $( $rest )* }
    };
    ( $(#[$attr:meta])* pub fn $name: ident (&self)-> $ty:ty; ) => {
        $(#[$attr])* #[inline] pub fn $name(&self) -> $ty { self.$name as $ty }
    };
    ( $(#[$attr:meta])* fn $name: ident (&self)-> $ty:ty; ) => {
        $(#[$attr])* #[inline] fn $name(&self) -> $ty { self.$name as $ty }
    };
    () => {};
}

pub mod section;
pub mod file;
pub mod program;

/// An ELF section header.
pub type Section<W> = section::Header<Word = W>;
pub type ProgramHeader<W> = program::Header<Word = W>;
/// An ELF header file.
pub type FileHeader<W> = file::HeaderRepr<W>;

/// TODO: should ELF have its own error type?
pub type ElfResult<T> = Result<T, &'static str>;

pub trait ElfWord: Sized + Copy + Clone
                         + ops::Add<Self> + ops::Sub<Self>
                         + ops::Mul<Self> + ops::Div<Self>
                         + ops::Shl<Self> + ops::Shr<Self> { }
impl ElfWord for u64 { }
impl ElfWord for u32 { }

#[cfg(target_pointer_width = "32")]
type DefaultWord = u32;
#[cfg(target_pointer_width = "64")]
type DefaultWord = u64;

/// Hack to make the type-system let me do what I want
trait ValidatesWord<Word: ElfWord> {
    fn check(&self) -> ElfResult<()>;
}

/// A handle on a parsed ELF binary
///  TODO: do we want this to own a HashMap of section names to section headers,
///        to speed up section lookup?
//          - eliza, 03/08/2017
#[derive(Debug)]
pub struct Image< 'bytes             // lifetime of the byte slice
                , Word = DefaultWord // default to machine's pointer size
                , ProgHeader = ProgramHeader<Word> // same word type
                , SectHeader = Section<Word>
                , Header = FileHeader<Word> // must have same word type
                > // jesus christ
where Word: ElfWord + 'bytes
    , ProgHeader: program::Header<Word = Word> + Sized + 'bytes
    , SectHeader: section::Header<Word = Word> + Sized + 'bytes
    , Header: file::Header<Word = Word> + 'bytes
    {
    /// the binary's [file header](file/trait.Header.html)
    pub header: &'bytes Header
  , /// references to each [section header](section/struct.Header.html)
    pub sections: &'bytes [SectHeader]
  , /// references to each [program header](program/trait.Header.html)
    pub program_headers: &'bytes [ProgHeader]
  , /// the raw binary contents of the ELF binary.
    /// note that this includes the _entire_ binary contents of the file,
    /// so the file header and each section header is included in this slice.
    binary: &'bytes [u8]
}

impl <'a, Word, ProgHeader, SectHeader, Header>
Image<'a, Word, ProgHeader, SectHeader, Header>
where Word: ElfWord + 'a
    , ProgHeader: program::Header<Word = Word> + Sized + 'a
    , SectHeader: section::Header<Word = Word> + Sized + 'a
    , Header: file::Header<Word = Word> + 'a
    {
    /// Returns the section header [string table].
    ///
    /// [string table]: section/struct.StrTable.html
    pub fn sh_str_table(&'a self) -> section::StrTable<'a> {
        // TODO: do we want to validate that the string table index is
        //       reasonable (e.g. it's not longer than the binary)?
        //          - eliza, 03/08/2017
        // TODO: do we want to cache a ref to the string table?
        //          - eliza, 03/08/2017
        section::StrTable::from(&self.binary[self.header.sh_str_idx()..])
    }

}

impl<'a, Word, PH, SH, FH> TryFrom<&'a [u8]> for Image<'a, Word, PH, SH, FH>
where Word: ElfWord + 'a
    , PH: program::Header<Word = Word> + 'a
    , SH: section::Header<Word = Word> + 'a
    , FH: file::Header<Word = Word> + 'a
    , &'a FH: convert::TryFrom<&'a [u8], Error = &'static str>
    {

    type Error = &'static str;

    fn try_from(bytes: &'a [u8]) -> ElfResult<Self> {
        let header: &'a FH = <&'a FH>::try_from(bytes)?;

        let sections = unsafe { extract_from_slice::<SH>(
            &bytes[header.sh_range()]
          , 0
          , header.sh_count()
        )? };
        let prog_headers = unsafe { extract_from_slice::<PH>(
            &bytes[header.ph_range()]
          , 0
          , header.ph_count()
        )? };
        Ok(Image { header: header
              , sections: sections
              , program_headers: prog_headers
              , binary: bytes
        })
    }
}

/// Extract `n` instances of type `T` from a byte slice.
///
/// This is essentially just a _slightly_ safer wrapper around
/// [`slice::from_raw_parts`]. Unlike `from_raw_parts`, this function takes
/// a valid byte slice, rather than a pointer. Therefore, some of the safety
/// issues with `from_raw_parts` are avoided:
///
/// + the lifetime (`'slice`) of the returned slice should be the same as the
///   lifetime of the input slice (`data`), rather than inferred arbitrarily.
/// + this function will panic rather than reading past the end of the slice.
///
/// # Arguments
///
/// + `data`: the byte slice to extract a slice of `&[T]`s from
/// + `offset`: a start offset into `data`
/// + `n`: the number of instances of `T` which should be contained
///        in `data[offset..]`
///
/// # Safety
///
/// While this function is safer than [`slice::from_raw_parts`],
/// it is still unsafe for the following reasons:
///
/// + The contents of `data` may not be able to be interpreted as instances of
///   type `T`.
///
/// # Caveats
///
/// + If `n` == 0, this will give you an `&[]`. Just a warning.
//    thanks to Max for making  me figure this out.
/// + `offset` must be aligned on a `T`-sized boundary.
///
/// # Panics
///
/// + If the index `offset` is longer than `T`
///
/// TODO: rewrite this as a `TryFrom` implementation (see issue #85)
//          - eliza, 03/09/2017
///       wait, possibly we should NOT do that. actually we should
///       almost certainly not do that. since this function is unsafe,
///       but `TryFrom` is not, and because this would be WAY generic.
//          - eliza, 03/09/2017
/// TODO: is this general enough to move into util?
//          - eliza, 03/09/2017
/// TODO: refactor this to take a `RangeArgument`?
//          - eliza, 03/13/2017
///       or, we could just remove the offset and expect the caller to
///       offset the slice?
//          - eliza, 03/14/2017
///
/// [`slice::from_raw_parts`]: https://doc.rust-lang.org/stable/std/slice/fn.from_raw_parts.html
unsafe fn extract_from_slice<'slice, T: Sized>( data: &'slice [u8]
                                              , offset: usize
                                              , n: usize)
                                              -> ElfResult<&'slice [T]> {
    if offset % mem::align_of::<T>() != 0 {
        // TODO: these error messages don't contain as much information as they
        //       used to, since the return type is `&'static str` that can't be
        //       dynamically formatted as the panic was. refactor this?
        //       (e.g. should ELF get its own error type?)
        //        - eliza, 03/15/2017
        Err("extract_from_slice: Offset not aligned on type T sized boundary!")
        // assert!(
        //        , "Offset {} not aligned on a {}-sized boundary (must be \
        //           divisible by {})."
        //        , offset, type_name::<T>(), mem::align_of::<T>()
        //        );
    } else if data.len() - offset < mem::size_of::<T>() * n {
        Err("extract_from_slice: Slice too short to contain n instances of T!")
        // assert!(
        //        , "Slice too short to contain {} objects of type {}"
        //        , n, type_name::<T>()
        //        );
    } else {
        Ok(slice::from_raw_parts(data[offset..].as_ptr() as *const T, n))
    }
}

impl<'a, W: ElfWord> convert::Into<FrameRange> for &'a Section<W> {
    #[inline]
    fn into(self) -> FrameRange {
        use memory::PAddr;
        let start = PhysicalPage::from(PAddr::from(self.address() as u64));
        let end = PhysicalPage::from(PAddr::from(self.end_address() as u64));
        start .. end
    }
}
<|endoftext|>


<commit_before>use story_builder::article_provider::*;
use std::rc::Rc;
use std::borrow::Borrow;
use std::ops::Deref;
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
use std::collections::HashSet;

pub struct StoryBuilder {
    article_provider: Arc<(ArticleProvider + Send + Sync)>,
    max_depth: u8,
    visited_nodes: Arc<HashSet<String>>,
}

impl StoryBuilder  {
    pub fn new(article_provider: Arc<(ArticleProvider + Send + Sync)>) -> StoryBuilder {
        StoryBuilder {
            article_provider,
            max_depth: 5, // default value for now
            visited_nodes: Arc::new(HashSet::new()),
        }
    }

    pub fn build_story(&mut self, start_topic: &str , end_topic: &str) -> Result<String, String> {
        let start_topic = start_topic.to_lowercase();
        let end_topic = end_topic.to_lowercase();
        // If one of the topics is an empty string, do not try to make a story out of it.
        if start_topic == "" {
            return Err("Missing start topic.".to_owned());
        }
        if end_topic == "" {
            return Err("Missing end topic.".to_owned());
        }

        // If both topics are the same,
        // there is no point in trying to figure out the story.
        if start_topic == end_topic {
            return Err("No story to build; same start and end topics.".to_owned());
        }

        // Load the first article
        let start_article = match self.article_provider.get(&start_topic) {
            Some(start_article) => start_article,
            None => {return Err(self.build_suggestions_msg(&start_topic));},
        };
        Arc::get_mut(&mut self.visited_nodes).unwrap().insert(start_topic);
        // Load the end article
        match self.article_provider.get(&end_topic) {
            Some(end_topic) => end_topic,
            None => {return Err(self.build_suggestions_msg(&end_topic));},
        };

        /* To build a story, we need to build a tree starting at the start_article
           node and going down in a "breadth-first" way; that way, once we find
           the end note, we know it is the shortest path to it. Also, going depth-first
           will be impossibly long to complete sincethe depth of the wikipedia
           article tree is almost infinite. */
        /* We look for a paragraph that holds a reference to our end topic
           somewhere in the last level we fetched: */
        let mut last_level: Vec<Rc<ArticleNode>> = vec![Rc::new(ArticleNode::new(start_article))]; // starts with start article
        for i in 0..self.max_depth { // To prevent overloading the system, stop after X level deep
            // Start by loading the next level of articles:
            if i > 0 {
                // Any other iteration: go one level deeper:
                let mut current_level = vec![];
                for rc_article_node in last_level.iter() {
                    /* Iterate on each paragraph of this article, then map on it to
                       get the article for each related topic in it. */
                    let rc_local = rc_article_node.clone();
                    let article = rc_local.deref();
                    for paragraph in article.get_paragraphs().iter() {
                        // First: spawn all threads first
                        let mut threads: Vec<JoinHandle<Option<Box<Article + Send + Sync>>>> = Vec::new();
                        for topic in paragraph.topics.iter() {
                            let topic = topic.to_lowercase();
                            if !Arc::get_mut(&mut self.visited_nodes).unwrap().contains(&topic) {
                                // Do not access the same article more than once!!
                                let article_provider = self.article_provider.clone();
                                let topic_for_thread = topic.to_owned();
                                threads.push(thread::spawn(move || {
                                    article_provider.get(&topic_for_thread)
                                }));
                                Arc::get_mut(&mut self.visited_nodes).unwrap().insert(topic);
                            } else {
                                println!("-------------- Node {} has already been visited. Skipping. --------------", &topic);
                            }
                        }
                        // Then, join them up one at a time.
                        for t in threads.into_iter() {
                            match t.join().unwrap() {
                                Some(content) => {
                                    let mut new_node = ArticleNode::new(content);
                                    new_node.attach_to(rc_local.clone(), &paragraph.text);
                                    current_level.push(Rc::new(new_node));
                                },
                                None => (),
                            }
                        }
                    }
                }
                last_level = current_level;
            }
            // Replace the previous level with this level.

            for article_node in last_level.iter() {
                if let Some(text) = StoryBuilder::find_text_for_topic_in_article(article_node.deref().deref().borrow(), &end_topic) {
                    // Found the topic. Format and return.
                    return Ok(StoryBuilder::build_final_text(article_node.clone(), text, &end_topic));
                }
            }
        }

        Err(format!("Reached depth of <{}> without finding <{}>. Stopping search.", self.max_depth, end_topic).to_owned())
    }

    fn build_suggestions_msg(&self, topic: &str) -> String {
        let mut msg = String::from(format!("Cannot find wikipedia article for <{}>, try one of the following suggestions:\r\n", topic));
        for sugg in self.article_provider.search(topic) {
            msg.push_str(&format!("- {}\r\n", &sugg));
        }

        msg
    }

    fn find_text_for_topic_in_article<'b>(article: &'b (Article + Send + Sync), topic: &str) -> Option<&'b str> {
        if let Some(paragraph) = article.get_paragraphs().iter().find(|par| {
            // if any of the topics in the paragraph is <end>, return it.
            par.topics.iter().any(|t| {&t.to_lowercase() == topic})
        }) {
            // We found the paragraph; return it directly.
            return Some(&paragraph.text);
        } else {
            None
        }
    }

    fn build_final_text(article_node: Rc<ArticleNode>, final_text: &str, final_topic: &str) -> String {
        let mut texts: Vec<String> = Vec::new();
        let mut last_topic = final_topic.to_owned();
        texts.push(format!("{}\r\n", final_text));
        {
            let n: &ArticleNode = article_node.borrow();
            if let Some(n_text) = n.text() {
                let new_topic = n.get_topic().to_owned();
                texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                last_topic = new_topic;
                texts.push(format!("{}\r\n", n_text));
            }
        }

        let mut node = article_node;
        loop {
            node = match node.parent() {
                Some(rc) => {
                    {
                        let n: &ArticleNode = rc.borrow();
                        if let Some(n_text) = n.text() {
                            let new_topic = n.get_topic().to_owned();
                            texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                            last_topic = new_topic;
                            texts.push(format!("{}\r\n", n_text));
                        }
                    }
                    rc
                },
                _ => {
                    let new_topic = node.get_topic().to_owned();
                    texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                    return texts.into_iter().rev().collect();
                },
            }
        }

        texts.into_iter().rev().collect()
    }
}

struct ArticleNode {
    data: Box<Article + Send + Sync>,
    parent: Option<Rc<ArticleNode>>,
    text: Option<String>,
}

impl ArticleNode {
    fn new(data: Box<Article + Send + Sync>) -> ArticleNode {
        ArticleNode {
            data,
            parent: None,
            text: None,
        }
    }
    fn attach_to(&mut self, parent: Rc<ArticleNode>, paragraph_text: &str) {
        self.parent = Some(parent);
        self.text = Some(String::from(paragraph_text));
    }
    fn parent(&self) -> Option<Rc<ArticleNode>> {
        self.parent.clone()
    }
    fn text(&self) -> Option<String> {
        self.text.clone()
    }
    fn topic(&self) -> &str {
        self.data.get_topic()
    }
}

impl <'n> Deref for ArticleNode {
    type Target = Box<Article + Send + Sync>;

    fn deref(&self) -> &Box<Article + Send + Sync> {
        &self.data
    }
}

#[cfg(test)]
mod tests;
<commit_msg>Fixed unused or unreachable code warnings.<commit_after>use story_builder::article_provider::*;
use std::rc::Rc;
use std::borrow::Borrow;
use std::ops::Deref;
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
use std::collections::HashSet;

pub struct StoryBuilder {
    article_provider: Arc<(ArticleProvider + Send + Sync)>,
    max_depth: u8,
    visited_nodes: Arc<HashSet<String>>,
}

impl StoryBuilder  {
    pub fn new(article_provider: Arc<(ArticleProvider + Send + Sync)>) -> StoryBuilder {
        StoryBuilder {
            article_provider,
            max_depth: 5, // default value for now
            visited_nodes: Arc::new(HashSet::new()),
        }
    }

    pub fn build_story(&mut self, start_topic: &str , end_topic: &str) -> Result<String, String> {
        let start_topic = start_topic.to_lowercase();
        let end_topic = end_topic.to_lowercase();
        // If one of the topics is an empty string, do not try to make a story out of it.
        if start_topic == "" {
            return Err("Missing start topic.".to_owned());
        }
        if end_topic == "" {
            return Err("Missing end topic.".to_owned());
        }

        // If both topics are the same,
        // there is no point in trying to figure out the story.
        if start_topic == end_topic {
            return Err("No story to build; same start and end topics.".to_owned());
        }

        // Load the first article
        let start_article = match self.article_provider.get(&start_topic) {
            Some(start_article) => start_article,
            None => {return Err(self.build_suggestions_msg(&start_topic));},
        };
        Arc::get_mut(&mut self.visited_nodes).unwrap().insert(start_topic);
        // Load the end article
        match self.article_provider.get(&end_topic) {
            Some(end_topic) => end_topic,
            None => {return Err(self.build_suggestions_msg(&end_topic));},
        };

        /* To build a story, we need to build a tree starting at the start_article
           node and going down in a "breadth-first" way; that way, once we find
           the end note, we know it is the shortest path to it. Also, going depth-first
           will be impossibly long to complete sincethe depth of the wikipedia
           article tree is almost infinite. */
        /* We look for a paragraph that holds a reference to our end topic
           somewhere in the last level we fetched: */
        let mut last_level: Vec<Rc<ArticleNode>> = vec![Rc::new(ArticleNode::new(start_article))]; // starts with start article
        for i in 0..self.max_depth { // To prevent overloading the system, stop after X level deep
            // Start by loading the next level of articles:
            if i > 0 {
                // Any other iteration: go one level deeper:
                let mut current_level = vec![];
                for rc_article_node in last_level.iter() {
                    /* Iterate on each paragraph of this article, then map on it to
                       get the article for each related topic in it. */
                    let rc_local = rc_article_node.clone();
                    let article = rc_local.deref();
                    for paragraph in article.get_paragraphs().iter() {
                        // First: spawn all threads first
                        let mut threads: Vec<JoinHandle<Option<Box<Article + Send + Sync>>>> = Vec::new();
                        for topic in paragraph.topics.iter() {
                            let topic = topic.to_lowercase();
                            if !Arc::get_mut(&mut self.visited_nodes).unwrap().contains(&topic) {
                                // Do not access the same article more than once!!
                                let article_provider = self.article_provider.clone();
                                let topic_for_thread = topic.to_owned();
                                threads.push(thread::spawn(move || {
                                    article_provider.get(&topic_for_thread)
                                }));
                                Arc::get_mut(&mut self.visited_nodes).unwrap().insert(topic);
                            } else {
                                println!("-------------- Node {} has already been visited. Skipping. --------------", &topic);
                            }
                        }
                        // Then, join them up one at a time.
                        for t in threads.into_iter() {
                            match t.join().unwrap() {
                                Some(content) => {
                                    let mut new_node = ArticleNode::new(content);
                                    new_node.attach_to(rc_local.clone(), &paragraph.text);
                                    current_level.push(Rc::new(new_node));
                                },
                                None => (),
                            }
                        }
                    }
                }
                last_level = current_level;
            }
            // Replace the previous level with this level.

            for article_node in last_level.iter() {
                if let Some(text) = StoryBuilder::find_text_for_topic_in_article(article_node.deref().deref().borrow(), &end_topic) {
                    // Found the topic. Format and return.
                    return Ok(StoryBuilder::build_final_text(article_node.clone(), text, &end_topic));
                }
            }
        }

        Err(format!("Reached depth of <{}> without finding <{}>. Stopping search.", self.max_depth, end_topic).to_owned())
    }

    fn build_suggestions_msg(&self, topic: &str) -> String {
        let mut msg = String::from(format!("Cannot find wikipedia article for <{}>, try one of the following suggestions:\r\n", topic));
        for sugg in self.article_provider.search(topic) {
            msg.push_str(&format!("- {}\r\n", &sugg));
        }

        msg
    }

    fn find_text_for_topic_in_article<'b>(article: &'b (Article + Send + Sync), topic: &str) -> Option<&'b str> {
        if let Some(paragraph) = article.get_paragraphs().iter().find(|par| {
            // if any of the topics in the paragraph is <end>, return it.
            par.topics.iter().any(|t| {&t.to_lowercase() == topic})
        }) {
            // We found the paragraph; return it directly.
            return Some(&paragraph.text);
        } else {
            None
        }
    }

    fn build_final_text(article_node: Rc<ArticleNode>, final_text: &str, final_topic: &str) -> String {
        let mut texts: Vec<String> = Vec::new();
        let mut last_topic = final_topic.to_owned();
        texts.push(format!("{}\r\n", final_text));
        {
            let n: &ArticleNode = article_node.borrow();
            if let Some(n_text) = n.text() {
                let new_topic = n.get_topic().to_owned();
                texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                last_topic = new_topic;
                texts.push(format!("{}\r\n", n_text));
            }
        }

        let mut node = article_node;
        loop {
            node = match node.parent() {
                Some(rc) => {
                    {
                        let n: &ArticleNode = rc.borrow();
                        if let Some(n_text) = n.text() {
                            let new_topic = n.get_topic().to_owned();
                            texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                            last_topic = new_topic;
                            texts.push(format!("{}\r\n", n_text));
                        }
                    }
                    rc
                },
                _ => {
                    let new_topic = node.get_topic().to_owned();
                    texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                    return texts.into_iter().rev().collect();
                },
            }
        }
    }
}

struct ArticleNode {
    data: Box<Article + Send + Sync>,
    parent: Option<Rc<ArticleNode>>,
    text: Option<String>,
}

impl ArticleNode {
    fn new(data: Box<Article + Send + Sync>) -> ArticleNode {
        ArticleNode {
            data,
            parent: None,
            text: None,
        }
    }
    fn attach_to(&mut self, parent: Rc<ArticleNode>, paragraph_text: &str) {
        self.parent = Some(parent);
        self.text = Some(String::from(paragraph_text));
    }
    fn parent(&self) -> Option<Rc<ArticleNode>> {
        self.parent.clone()
    }
    fn text(&self) -> Option<String> {
        self.text.clone()
    }
}

impl <'n> Deref for ArticleNode {
    type Target = Box<Article + Send + Sync>;

    fn deref(&self) -> &Box<Article + Send + Sync> {
        &self.data
    }
}

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>use crate::{io, RootFilter};
use crossbeam_channel::Sender;
use drop_bomb::DropBomb;
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher};
use std::{
    path::{Path, PathBuf},
    sync::mpsc,
    thread,
    time::Duration,
};
use walkdir::WalkDir;

#[derive(Debug)]
pub enum WatcherChange {
    Create(PathBuf),
    Write(PathBuf),
    Remove(PathBuf),
    Rescan,
}

fn handle_change_event(
    ev: DebouncedEvent,
    sender: &Sender<io::Task>,
) -> Result<(), Box<std::error::Error>> {
    match ev {
        DebouncedEvent::NoticeWrite(_)
        | DebouncedEvent::NoticeRemove(_)
        | DebouncedEvent::Chmod(_) => {
            // ignore
        }
        DebouncedEvent::Rescan => {
            sender.send(io::Task::HandleChange(WatcherChange::Rescan))?;
        }
        DebouncedEvent::Create(path) => {
            sender.send(io::Task::HandleChange(WatcherChange::Create(path)))?;
        }
        DebouncedEvent::Write(path) => {
            sender.send(io::Task::HandleChange(WatcherChange::Write(path)))?;
        }
        DebouncedEvent::Remove(path) => {
            sender.send(io::Task::HandleChange(WatcherChange::Remove(path)))?;
        }
        DebouncedEvent::Rename(src, dst) => {
            sender.send(io::Task::HandleChange(WatcherChange::Remove(src)))?;
            sender.send(io::Task::HandleChange(WatcherChange::Create(dst)))?;
        }
        DebouncedEvent::Error(err, path) => {
            // TODO should we reload the file contents?
            log::warn!("watcher error \"{}\", {:?}", err, path);
        }
    }
    Ok(())
}

const WATCHER_DELAY: Duration = Duration::from_millis(250);

pub(crate) struct Watcher {
    watcher: RecommendedWatcher,
    thread: thread::JoinHandle<()>,
    bomb: DropBomb,
    sender: Sender<io::Task>,
}

impl Watcher {
    pub(crate) fn start(
        output_sender: Sender<io::Task>,
    ) -> Result<Watcher, Box<std::error::Error>> {
        let (input_sender, input_receiver) = mpsc::channel();
        let watcher = notify::watcher(input_sender, WATCHER_DELAY)?;
        let sender = output_sender.clone();
        let thread = thread::spawn(move || {
            input_receiver
                .into_iter()
                // forward relevant events only
                .try_for_each(|change| handle_change_event(change, &output_sender))
                .unwrap()
        });
        Ok(Watcher {
            watcher,
            thread,
            sender,
            bomb: DropBomb::new(format!("Watcher was not shutdown")),
        })
    }

    pub fn watch_recursive(&mut self, dir: &Path, filter: &RootFilter, emit_for_contents: bool) {
        for res in WalkDir::new(dir)
            .into_iter()
            .filter_entry(|entry| filter.can_contain(entry.path()).is_some())
        {
            match res {
                Ok(entry) => {
                    if entry.path().is_dir() {
                        match self.watcher.watch(dir, RecursiveMode::NonRecursive) {
                            Ok(()) => log::debug!("watching \"{}\"", dir.display()),
                            Err(e) => log::warn!("could not watch \"{}\": {}", dir.display(), e),
                        }
                    }
                    if emit_for_contents && entry.depth() > 0 {
                        // emit as create because we haven't seen it yet
                        if let Err(e) =
                            self.sender
                                .send(io::Task::HandleChange(WatcherChange::Create(
                                    entry.path().to_path_buf(),
                                )))
                        {
                            log::warn!("watcher error: {}", e)
                        }
                    }
                }
                Err(e) => log::warn!("watcher error: {}", e),
            }
        }
    }

    pub fn shutdown(mut self) -> thread::Result<()> {
        self.bomb.defuse();
        drop(self.watcher);
        let res = self.thread.join();
        match &res {
            Ok(()) => log::info!("... Watcher terminated with ok"),
            Err(_) => log::error!("... Watcher terminated with err"),
        }
        res
    }
}
<commit_msg>fix recursive watch<commit_after>use crate::{io, RootFilter};
use crossbeam_channel::Sender;
use drop_bomb::DropBomb;
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher};
use std::{
    path::{Path, PathBuf},
    sync::mpsc,
    thread,
    time::Duration,
};
use walkdir::WalkDir;

#[derive(Debug)]
pub enum WatcherChange {
    Create(PathBuf),
    Write(PathBuf),
    Remove(PathBuf),
    Rescan,
}

fn handle_change_event(
    ev: DebouncedEvent,
    sender: &Sender<io::Task>,
) -> Result<(), Box<std::error::Error>> {
    match ev {
        DebouncedEvent::NoticeWrite(_)
        | DebouncedEvent::NoticeRemove(_)
        | DebouncedEvent::Chmod(_) => {
            // ignore
        }
        DebouncedEvent::Rescan => {
            sender.send(io::Task::HandleChange(WatcherChange::Rescan))?;
        }
        DebouncedEvent::Create(path) => {
            sender.send(io::Task::HandleChange(WatcherChange::Create(path)))?;
        }
        DebouncedEvent::Write(path) => {
            sender.send(io::Task::HandleChange(WatcherChange::Write(path)))?;
        }
        DebouncedEvent::Remove(path) => {
            sender.send(io::Task::HandleChange(WatcherChange::Remove(path)))?;
        }
        DebouncedEvent::Rename(src, dst) => {
            sender.send(io::Task::HandleChange(WatcherChange::Remove(src)))?;
            sender.send(io::Task::HandleChange(WatcherChange::Create(dst)))?;
        }
        DebouncedEvent::Error(err, path) => {
            // TODO should we reload the file contents?
            log::warn!("watcher error \"{}\", {:?}", err, path);
        }
    }
    Ok(())
}

const WATCHER_DELAY: Duration = Duration::from_millis(250);

pub(crate) struct Watcher {
    watcher: RecommendedWatcher,
    thread: thread::JoinHandle<()>,
    bomb: DropBomb,
    sender: Sender<io::Task>,
}

impl Watcher {
    pub(crate) fn start(
        output_sender: Sender<io::Task>,
    ) -> Result<Watcher, Box<std::error::Error>> {
        let (input_sender, input_receiver) = mpsc::channel();
        let watcher = notify::watcher(input_sender, WATCHER_DELAY)?;
        let sender = output_sender.clone();
        let thread = thread::spawn(move || {
            input_receiver
                .into_iter()
                // forward relevant events only
                .try_for_each(|change| handle_change_event(change, &output_sender))
                .unwrap()
        });
        Ok(Watcher {
            watcher,
            thread,
            sender,
            bomb: DropBomb::new(format!("Watcher was not shutdown")),
        })
    }

    pub fn watch_recursive(&mut self, dir: &Path, filter: &RootFilter, emit_for_contents: bool) {
        for res in WalkDir::new(dir)
            .into_iter()
            .filter_entry(|entry| filter.can_contain(entry.path()).is_some())
        {
            match res {
                Ok(entry) => {
                    if entry.path().is_dir() {
                        match self
                            .watcher
                            .watch(entry.path(), RecursiveMode::NonRecursive)
                        {
                            Ok(()) => log::debug!("watching \"{}\"", entry.path().display()),
                            Err(e) => {
                                log::warn!("could not watch \"{}\": {}", entry.path().display(), e)
                            }
                        }
                    }
                    if emit_for_contents && entry.depth() > 0 {
                        // emit as create because we haven't seen it yet
                        if let Err(e) =
                            self.sender
                                .send(io::Task::HandleChange(WatcherChange::Create(
                                    entry.path().to_path_buf(),
                                )))
                        {
                            log::warn!("watcher error: {}", e)
                        }
                    }
                }
                Err(e) => log::warn!("watcher error: {}", e),
            }
        }
    }

    pub fn shutdown(mut self) -> thread::Result<()> {
        self.bomb.defuse();
        drop(self.watcher);
        let res = self.thread.join();
        match &res {
            Ok(()) => log::info!("... Watcher terminated with ok"),
            Err(_) => log::error!("... Watcher terminated with err"),
        }
        res
    }
}
<|endoftext|>


<commit_before>//! This module implements syntax validation that the parser doesn't handle.
//!
//! A failed validation emits a diagnostic.

mod block;

use rowan::Direction;
use rustc_lexer::unescape::{
    self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
};

use crate::{
    algo,
    ast::{self, HasVisibility},
    match_ast, AstNode, SyntaxError,
    SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS},
    SyntaxNode, SyntaxToken, TextSize, T,
};

pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
    // FIXME:
    // * Add unescape validation of raw string literals and raw byte string literals
    // * Add validation of doc comments are being attached to nodes

    let mut errors = Vec::new();
    for node in root.descendants() {
        match_ast! {
            match node {
                ast::Literal(it) => validate_literal(it, &mut errors),
                ast::Const(it) => validate_const(it, &mut errors),
                ast::BlockExpr(it) => block::validate_block_expr(it, &mut errors),
                ast::FieldExpr(it) => validate_numeric_name(it.name_ref(), &mut errors),
                ast::RecordExprField(it) => validate_numeric_name(it.name_ref(), &mut errors),
                ast::Visibility(it) => validate_visibility(it, &mut errors),
                ast::RangeExpr(it) => validate_range_expr(it, &mut errors),
                ast::PathSegment(it) => validate_path_keywords(it, &mut errors),
                ast::RefType(it) => validate_trait_object_ref_ty(it, &mut errors),
                ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors),
                ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors),
                ast::MacroRules(it) => validate_macro_rules(it, &mut errors),
                ast::LetExpr(it) => validate_let_expr(it, &mut errors),
                _ => (),
            }
        }
    }
    errors
}

fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
    use unescape::EscapeError as EE;

    #[rustfmt::skip]
    let err_message = match err {
        EE::ZeroChars => {
            "Literal must not be empty"
        }
        EE::MoreThanOneChar => {
            "Literal must be one character long"
        }
        EE::LoneSlash => {
            "Character must be escaped: `\\`"
        }
        EE::InvalidEscape => {
            "Invalid escape"
        }
        EE::BareCarriageReturn | EE::BareCarriageReturnInRawString => {
            "Character must be escaped: `\r`"
        }
        EE::EscapeOnlyChar => {
            "Escape character `\\` must be escaped itself"
        }
        EE::TooShortHexEscape => {
            "ASCII hex escape code must have exactly two digits"
        }
        EE::InvalidCharInHexEscape => {
            "ASCII hex escape code must contain only hex characters"
        }
        EE::OutOfRangeHexEscape => {
            "ASCII hex escape code must be at most 0x7F"
        }
        EE::NoBraceInUnicodeEscape => {
            "Missing `{` to begin the unicode escape"
        }
        EE::InvalidCharInUnicodeEscape => {
            "Unicode escape must contain only hex characters and underscores"
        }
        EE::EmptyUnicodeEscape => {
            "Unicode escape must not be empty"
        }
        EE::UnclosedUnicodeEscape => {
            "Missing `}` to terminate the unicode escape"
        }
        EE::LeadingUnderscoreUnicodeEscape => {
            "Unicode escape code must not begin with an underscore"
        }
        EE::OverlongUnicodeEscape => {
            "Unicode escape code must have at most 6 digits"
        }
        EE::LoneSurrogateUnicodeEscape => {
            "Unicode escape code must not be a surrogate"
        }
        EE::OutOfRangeUnicodeEscape => {
            "Unicode escape code must be at most 0x10FFFF"
        }
        EE::UnicodeEscapeInByte => {
            "Byte literals must not contain unicode escapes"
        }
        EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => {
            "Byte literals must not contain non-ASCII characters"
        }
    };

    err_message
}

fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
    // FIXME: move this function to outer scope (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366196658)
    fn unquote(text: &str, prefix_len: usize, end_delimiter: char) -> Option<&str> {
        text.rfind(end_delimiter).and_then(|end| text.get(prefix_len..end))
    }

    let token = literal.token();
    let text = token.text();

    // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366199205)
    let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {
        let off = token.text_range().start() + TextSize::try_from(off + prefix_len).unwrap();
        acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off));
    };

    match literal.kind() {
        ast::LiteralKind::String(s) => {
            if !s.is_raw() {
                if let Some(without_quotes) = unquote(text, 1, '"') {
                    unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
                        if let Err(err) = char {
                            push_err(1, (range.start, err));
                        }
                    });
                }
            }
        }
        ast::LiteralKind::ByteString(s) => {
            if !s.is_raw() {
                if let Some(without_quotes) = unquote(text, 2, '"') {
                    unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
                        if let Err(err) = char {
                            push_err(2, (range.start, err));
                        }
                    });
                }
            }
        }
        ast::LiteralKind::Char => {
            if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
                push_err(1, e);
            }
        }
        ast::LiteralKind::Byte => {
            if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
                push_err(2, e);
            }
        }
        ast::LiteralKind::IntNumber(_)
        | ast::LiteralKind::FloatNumber(_)
        | ast::LiteralKind::Bool(_) => {}
    }
}

pub(crate) fn validate_block_structure(root: &SyntaxNode) {
    let mut stack = Vec::new();
    for node in root.descendants_with_tokens() {
        match node.kind() {
            T!['{'] => stack.push(node),
            T!['}'] => {
                if let Some(pair) = stack.pop() {
                    assert_eq!(
                        node.parent(),
                        pair.parent(),
                        "\nunpaired curlys:\n{}\n{:#?}\n",
                        root.text(),
                        root,
                    );
                    assert!(
                        node.next_sibling_or_token().is_none()
                            && pair.prev_sibling_or_token().is_none(),
                        "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
                        node,
                        root.text(),
                        node,
                    );
                }
            }
            _ => (),
        }
    }
}

fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<SyntaxError>) {
    if let Some(int_token) = int_token(name_ref) {
        if int_token.text().chars().any(|c| !c.is_digit(10)) {
            errors.push(SyntaxError::new(
                "Tuple (struct) field access is only allowed through \
                decimal integers with no underscores or suffix",
                int_token.text_range(),
            ));
        }
    }

    fn int_token(name_ref: Option<ast::NameRef>) -> Option<SyntaxToken> {
        name_ref?.syntax().first_child_or_token()?.into_token().filter(|it| it.kind() == INT_NUMBER)
    }
}

fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
    let path_without_in_token = vis.in_token().is_none()
        && vis.path().and_then(|p| p.as_single_name_ref()).and_then(|n| n.ident_token()).is_some();
    if path_without_in_token {
        errors.push(SyntaxError::new("incorrect visibility restriction", vis.syntax.text_range()));
    }
    let parent = match vis.syntax().parent() {
        Some(it) => it,
        None => return,
    };
    match parent.kind() {
        FN | CONST | TYPE_ALIAS => (),
        _ => return,
    }

    let impl_def = match parent.parent().and_then(|it| it.parent()).and_then(ast::Impl::cast) {
        Some(it) => it,
        None => return,
    };
    if impl_def.trait_().is_some() {
        errors.push(SyntaxError::new("Unnecessary visibility qualifier", vis.syntax.text_range()));
    }
}

fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) {
    if expr.op_kind() == Some(ast::RangeOp::Inclusive) && expr.end().is_none() {
        errors.push(SyntaxError::new(
            "An inclusive range must have an end expression",
            expr.syntax().text_range(),
        ));
    }
}

fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxError>) {
    let path = segment.parent_path();
    let is_path_start = segment.coloncolon_token().is_none() && path.qualifier().is_none();

    if let Some(token) = segment.self_token() {
        if !is_path_start {
            errors.push(SyntaxError::new(
                "The `self` keyword is only allowed as the first segment of a path",
                token.text_range(),
            ));
        }
    } else if let Some(token) = segment.crate_token() {
        if !is_path_start || use_prefix(path).is_some() {
            errors.push(SyntaxError::new(
                "The `crate` keyword is only allowed as the first segment of a path",
                token.text_range(),
            ));
        }
    }

    fn use_prefix(mut path: ast::Path) -> Option<ast::Path> {
        for node in path.syntax().ancestors().skip(1) {
            match_ast! {
                match node {
                    ast::UseTree(it) => if let Some(tree_path) = it.path() {
                        // Even a top-level path exists within a `UseTree` so we must explicitly
                        // allow our path but disallow anything else
                        if tree_path != path {
                            return Some(tree_path);
                        }
                    },
                    ast::UseTreeList(_) => continue,
                    ast::Path(parent) => path = parent,
                    _ => return None,
                }
            };
        }
        None
    }
}

fn validate_trait_object_ref_ty(ty: ast::RefType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_ptr_ty(ty: ast::PtrType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_fn_ptr_ret_ty(ty: ast::FnPtrType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ret_type().and_then(|ty| ty.ty()) {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> {
    let tbl = ty.type_bound_list()?;

    if tbl.bounds().count() > 1 {
        let dyn_token = ty.dyn_token()?;
        let potential_parenthesis =
            algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
        let kind = potential_parenthesis.kind();
        if !matches!(kind, T!['('] | T![<] | T![=]) {
            return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
        }
    }
    None
}

fn validate_macro_rules(mac: ast::MacroRules, errors: &mut Vec<SyntaxError>) {
    if let Some(vis) = mac.visibility() {
        errors.push(SyntaxError::new(
            "visibilities are not allowed on `macro_rules!` items",
            vis.syntax().text_range(),
        ));
    }
}

fn validate_const(const_: ast::Const, errors: &mut Vec<SyntaxError>) {
    if let Some(mut_token) = const_
        .const_token()
        .and_then(|t| t.next_token())
        .and_then(|t| algo::skip_trivia_token(t, Direction::Next))
        .filter(|t| t.kind() == T![mut])
    {
        errors.push(SyntaxError::new("const globals cannot be mutable", mut_token.text_range()));
    }
}

fn validate_let_expr(let_: ast::LetExpr, errors: &mut Vec<SyntaxError>) {
    let mut token = let_.syntax().clone();
    loop {
        token = match token.parent() {
            Some(it) => it,
            None => break,
        };

        if ast::ParenExpr::can_cast(token.kind()) {
            continue;
        } else if let Some(it) = ast::BinExpr::cast(token.clone()) {
            if it.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) {
                continue;
            }
        } else if ast::IfExpr::can_cast(token.kind())
            || ast::WhileExpr::can_cast(token.kind())
            || ast::MatchGuard::can_cast(token.kind())
        {
            // It must be part of the condition since the expressions are inside a block.
            return;
        }

        break;
    }
    errors.push(SyntaxError::new(
        "`let` expressions are not supported here",
        let_.syntax().text_range(),
    ));
}
<commit_msg>fix visibility token validation in trait impl with proc-macro<commit_after>//! This module implements syntax validation that the parser doesn't handle.
//!
//! A failed validation emits a diagnostic.

mod block;

use rowan::Direction;
use rustc_lexer::unescape::{
    self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
};

use crate::{
    algo,
    ast::{self, HasAttrs, HasVisibility},
    match_ast, AstNode, SyntaxError,
    SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS},
    SyntaxNode, SyntaxToken, TextSize, T,
};

pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
    // FIXME:
    // * Add unescape validation of raw string literals and raw byte string literals
    // * Add validation of doc comments are being attached to nodes

    let mut errors = Vec::new();
    for node in root.descendants() {
        match_ast! {
            match node {
                ast::Literal(it) => validate_literal(it, &mut errors),
                ast::Const(it) => validate_const(it, &mut errors),
                ast::BlockExpr(it) => block::validate_block_expr(it, &mut errors),
                ast::FieldExpr(it) => validate_numeric_name(it.name_ref(), &mut errors),
                ast::RecordExprField(it) => validate_numeric_name(it.name_ref(), &mut errors),
                ast::Visibility(it) => validate_visibility(it, &mut errors),
                ast::RangeExpr(it) => validate_range_expr(it, &mut errors),
                ast::PathSegment(it) => validate_path_keywords(it, &mut errors),
                ast::RefType(it) => validate_trait_object_ref_ty(it, &mut errors),
                ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors),
                ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors),
                ast::MacroRules(it) => validate_macro_rules(it, &mut errors),
                ast::LetExpr(it) => validate_let_expr(it, &mut errors),
                _ => (),
            }
        }
    }
    errors
}

fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
    use unescape::EscapeError as EE;

    #[rustfmt::skip]
    let err_message = match err {
        EE::ZeroChars => {
            "Literal must not be empty"
        }
        EE::MoreThanOneChar => {
            "Literal must be one character long"
        }
        EE::LoneSlash => {
            "Character must be escaped: `\\`"
        }
        EE::InvalidEscape => {
            "Invalid escape"
        }
        EE::BareCarriageReturn | EE::BareCarriageReturnInRawString => {
            "Character must be escaped: `\r`"
        }
        EE::EscapeOnlyChar => {
            "Escape character `\\` must be escaped itself"
        }
        EE::TooShortHexEscape => {
            "ASCII hex escape code must have exactly two digits"
        }
        EE::InvalidCharInHexEscape => {
            "ASCII hex escape code must contain only hex characters"
        }
        EE::OutOfRangeHexEscape => {
            "ASCII hex escape code must be at most 0x7F"
        }
        EE::NoBraceInUnicodeEscape => {
            "Missing `{` to begin the unicode escape"
        }
        EE::InvalidCharInUnicodeEscape => {
            "Unicode escape must contain only hex characters and underscores"
        }
        EE::EmptyUnicodeEscape => {
            "Unicode escape must not be empty"
        }
        EE::UnclosedUnicodeEscape => {
            "Missing `}` to terminate the unicode escape"
        }
        EE::LeadingUnderscoreUnicodeEscape => {
            "Unicode escape code must not begin with an underscore"
        }
        EE::OverlongUnicodeEscape => {
            "Unicode escape code must have at most 6 digits"
        }
        EE::LoneSurrogateUnicodeEscape => {
            "Unicode escape code must not be a surrogate"
        }
        EE::OutOfRangeUnicodeEscape => {
            "Unicode escape code must be at most 0x10FFFF"
        }
        EE::UnicodeEscapeInByte => {
            "Byte literals must not contain unicode escapes"
        }
        EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => {
            "Byte literals must not contain non-ASCII characters"
        }
    };

    err_message
}

fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
    // FIXME: move this function to outer scope (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366196658)
    fn unquote(text: &str, prefix_len: usize, end_delimiter: char) -> Option<&str> {
        text.rfind(end_delimiter).and_then(|end| text.get(prefix_len..end))
    }

    let token = literal.token();
    let text = token.text();

    // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-analyzer/rust-analyzer/pull/2834#discussion_r366199205)
    let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {
        let off = token.text_range().start() + TextSize::try_from(off + prefix_len).unwrap();
        acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off));
    };

    match literal.kind() {
        ast::LiteralKind::String(s) => {
            if !s.is_raw() {
                if let Some(without_quotes) = unquote(text, 1, '"') {
                    unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
                        if let Err(err) = char {
                            push_err(1, (range.start, err));
                        }
                    });
                }
            }
        }
        ast::LiteralKind::ByteString(s) => {
            if !s.is_raw() {
                if let Some(without_quotes) = unquote(text, 2, '"') {
                    unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
                        if let Err(err) = char {
                            push_err(2, (range.start, err));
                        }
                    });
                }
            }
        }
        ast::LiteralKind::Char => {
            if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
                push_err(1, e);
            }
        }
        ast::LiteralKind::Byte => {
            if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
                push_err(2, e);
            }
        }
        ast::LiteralKind::IntNumber(_)
        | ast::LiteralKind::FloatNumber(_)
        | ast::LiteralKind::Bool(_) => {}
    }
}

pub(crate) fn validate_block_structure(root: &SyntaxNode) {
    let mut stack = Vec::new();
    for node in root.descendants_with_tokens() {
        match node.kind() {
            T!['{'] => stack.push(node),
            T!['}'] => {
                if let Some(pair) = stack.pop() {
                    assert_eq!(
                        node.parent(),
                        pair.parent(),
                        "\nunpaired curlys:\n{}\n{:#?}\n",
                        root.text(),
                        root,
                    );
                    assert!(
                        node.next_sibling_or_token().is_none()
                            && pair.prev_sibling_or_token().is_none(),
                        "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
                        node,
                        root.text(),
                        node,
                    );
                }
            }
            _ => (),
        }
    }
}

fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<SyntaxError>) {
    if let Some(int_token) = int_token(name_ref) {
        if int_token.text().chars().any(|c| !c.is_digit(10)) {
            errors.push(SyntaxError::new(
                "Tuple (struct) field access is only allowed through \
                decimal integers with no underscores or suffix",
                int_token.text_range(),
            ));
        }
    }

    fn int_token(name_ref: Option<ast::NameRef>) -> Option<SyntaxToken> {
        name_ref?.syntax().first_child_or_token()?.into_token().filter(|it| it.kind() == INT_NUMBER)
    }
}

fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
    let path_without_in_token = vis.in_token().is_none()
        && vis.path().and_then(|p| p.as_single_name_ref()).and_then(|n| n.ident_token()).is_some();
    if path_without_in_token {
        errors.push(SyntaxError::new("incorrect visibility restriction", vis.syntax.text_range()));
    }
    let parent = match vis.syntax().parent() {
        Some(it) => it,
        None => return,
    };
    match parent.kind() {
        FN | CONST | TYPE_ALIAS => (),
        _ => return,
    }

    let impl_def = match parent.parent().and_then(|it| it.parent()).and_then(ast::Impl::cast) {
        Some(it) => it,
        None => return,
    };
    // FIXME: disable validation if there's an attribute, since some proc macros use this syntax.
    // ideally the validation would run only on the fully expanded code, then this wouldn't be necessary.
    if impl_def.trait_().is_some() && impl_def.attrs().next().is_none() {
        errors.push(SyntaxError::new("Unnecessary visibility qualifier", vis.syntax.text_range()));
    }
}

fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) {
    if expr.op_kind() == Some(ast::RangeOp::Inclusive) && expr.end().is_none() {
        errors.push(SyntaxError::new(
            "An inclusive range must have an end expression",
            expr.syntax().text_range(),
        ));
    }
}

fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxError>) {
    let path = segment.parent_path();
    let is_path_start = segment.coloncolon_token().is_none() && path.qualifier().is_none();

    if let Some(token) = segment.self_token() {
        if !is_path_start {
            errors.push(SyntaxError::new(
                "The `self` keyword is only allowed as the first segment of a path",
                token.text_range(),
            ));
        }
    } else if let Some(token) = segment.crate_token() {
        if !is_path_start || use_prefix(path).is_some() {
            errors.push(SyntaxError::new(
                "The `crate` keyword is only allowed as the first segment of a path",
                token.text_range(),
            ));
        }
    }

    fn use_prefix(mut path: ast::Path) -> Option<ast::Path> {
        for node in path.syntax().ancestors().skip(1) {
            match_ast! {
                match node {
                    ast::UseTree(it) => if let Some(tree_path) = it.path() {
                        // Even a top-level path exists within a `UseTree` so we must explicitly
                        // allow our path but disallow anything else
                        if tree_path != path {
                            return Some(tree_path);
                        }
                    },
                    ast::UseTreeList(_) => continue,
                    ast::Path(parent) => path = parent,
                    _ => return None,
                }
            };
        }
        None
    }
}

fn validate_trait_object_ref_ty(ty: ast::RefType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_ptr_ty(ty: ast::PtrType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_fn_ptr_ret_ty(ty: ast::FnPtrType, errors: &mut Vec<SyntaxError>) {
    if let Some(ast::Type::DynTraitType(ty)) = ty.ret_type().and_then(|ty| ty.ty()) {
        if let Some(err) = validate_trait_object_ty(ty) {
            errors.push(err);
        }
    }
}

fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> {
    let tbl = ty.type_bound_list()?;

    if tbl.bounds().count() > 1 {
        let dyn_token = ty.dyn_token()?;
        let potential_parenthesis =
            algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
        let kind = potential_parenthesis.kind();
        if !matches!(kind, T!['('] | T![<] | T![=]) {
            return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
        }
    }
    None
}

fn validate_macro_rules(mac: ast::MacroRules, errors: &mut Vec<SyntaxError>) {
    if let Some(vis) = mac.visibility() {
        errors.push(SyntaxError::new(
            "visibilities are not allowed on `macro_rules!` items",
            vis.syntax().text_range(),
        ));
    }
}

fn validate_const(const_: ast::Const, errors: &mut Vec<SyntaxError>) {
    if let Some(mut_token) = const_
        .const_token()
        .and_then(|t| t.next_token())
        .and_then(|t| algo::skip_trivia_token(t, Direction::Next))
        .filter(|t| t.kind() == T![mut])
    {
        errors.push(SyntaxError::new("const globals cannot be mutable", mut_token.text_range()));
    }
}

fn validate_let_expr(let_: ast::LetExpr, errors: &mut Vec<SyntaxError>) {
    let mut token = let_.syntax().clone();
    loop {
        token = match token.parent() {
            Some(it) => it,
            None => break,
        };

        if ast::ParenExpr::can_cast(token.kind()) {
            continue;
        } else if let Some(it) = ast::BinExpr::cast(token.clone()) {
            if it.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) {
                continue;
            }
        } else if ast::IfExpr::can_cast(token.kind())
            || ast::WhileExpr::can_cast(token.kind())
            || ast::MatchGuard::can_cast(token.kind())
        {
            // It must be part of the condition since the expressions are inside a block.
            return;
        }

        break;
    }
    errors.push(SyntaxError::new(
        "`let` expressions are not supported here",
        let_.syntax().text_range(),
    ));
}
<|endoftext|>


<commit_before>// ignore-tidy-linelength

// min-lldb-version: 310

// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:run

// gdb-command:print *t0
// gdb-check:$1 = 1
// gdb-command:print *t1
// gdb-check:$2 = 2.5
// gdb-command:print ret
// gdbg-check:$3 = {__0 = {__0 = 1, __1 = 2.5}, __1 = {__0 = 2.5, __1 = 1}}
// gdbr-check:$3 = ((1, 2.5), (2.5, 1))
// gdb-command:continue

// gdb-command:print *t0
// gdb-check:$4 = 3.5
// gdb-command:print *t1
// gdb-check:$5 = 4
// gdb-command:print ret
// gdbg-check:$6 = {__0 = {__0 = 3.5, __1 = 4}, __1 = {__0 = 4, __1 = 3.5}}
// gdbr-check:$6 = ((3.5, 4), (4, 3.5))
// gdb-command:continue

// gdb-command:print *t0
// gdb-check:$7 = 5
// gdb-command:print *t1
// gdbg-check:$8 = {a = 6, b = 7.5}
// gdbr-check:$8 = generic_function::Struct {a: 6, b: 7.5}
// gdb-command:print ret
// gdbg-check:$9 = {__0 = {__0 = 5, __1 = {a = 6, b = 7.5}}, __1 = {__0 = {a = 6, b = 7.5}, __1 = 5}}
// gdbr-check:$9 = ((5, generic_function::Struct {a: 6, b: 7.5}), (generic_function::Struct {a: 6, b: 7.5}, 5))
// gdb-command:continue

// === LLDB TESTS ==================================================================================

// lldb-command:run

// lldb-command:print *t0
// lldbg-check:[...]$0 = 1
// lldbr-check:(i32) *t0 = 1
// lldb-command:print *t1
// lldbg-check:[...]$1 = 2.5
// lldbr-check:(f64) *t1 = 2.5
// lldb-command:print ret
// lldbg-check:[...]$2 = ((1, 2.5), (2.5, 1))
// lldbr-check:(((i32, f64), (f64, i32))) ret = { = { = 1 = 2.5 } = { = 2.5 = 1 } }
// lldb-command:continue

// lldb-command:print *t0
// lldbg-check:[...]$3 = 3.5
// lldbr-check:(f64) *t0 = 3.5
// lldb-command:print *t1
// lldbg-check:[...]$4 = 4
// lldbr-check:(u16) *t1 = 4
// lldb-command:print ret
// lldbg-check:[...]$5 = ((3.5, 4), (4, 3.5))
// lldbr-check:(((f64, u16), (u16, f64))) ret = { = { = 3.5 = 4 } = { = 4 = 3.5 } }
// lldb-command:continue

// lldb-command:print *t0
// lldbg-check:[...]$6 = 5
// lldbr-check:(i32) *t0 = 5
// lldb-command:print *t1
// lldbg-check:[...]$7 = Struct { a: 6, b: 7.5 }
// lldbr-check:(generic_function::Struct) *t1 = Struct { a: 6, b: 7.5 }
// lldb-command:print ret
// lldbg-check:[...]$8 = ((5, Struct { a: 6, b: 7.5 }), (Struct { a: 6, b: 7.5 }, 5))
// lldbr-check:(((i32, generic_function::Struct), (generic_function::Struct, i32))) ret = { = { = 5 = Struct { a: 6, b: 7.5 } } = { = Struct { a: 6, b: 7.5 } = 5 } }
// lldb-command:continue

#![feature(omit_gdb_pretty_printer_section)]
#![omit_gdb_pretty_printer_section]

#[derive(Clone)]
struct Struct {
    a: isize,
    b: f64
}

fn dup_tup<T0: Clone, T1: Clone>(t0: &T0, t1: &T1) -> ((T0, T1), (T1, T0)) {
    let ret = ((t0.clone(), t1.clone()), (t1.clone(), t0.clone()));
    zzz(); // #break
    ret
}

fn main() {

    let _ = dup_tup(&1, &2.5f64);
    let _ = dup_tup(&3.5f64, &4_u16);
    let _ = dup_tup(&5, &Struct { a: 6, b: 7.5 });
}

fn zzz() {()}
<commit_msg>`ret` has been optimized away in debuginfo test<commit_after>// min-lldb-version: 310

// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:run

// gdb-command:print *t0
// gdb-check:$1 = 1
// gdb-command:print *t1
// gdb-check:$2 = 2.5
// gdb-command:continue

// gdb-command:print *t0
// gdb-check:$3 = 3.5
// gdb-command:print *t1
// gdb-check:$4 = 4
// gdb-command:continue

// gdb-command:print *t0
// gdb-check:$5 = 5
// gdb-command:print *t1
// gdbg-check:$6 = {a = 6, b = 7.5}
// gdbr-check:$6 = generic_function::Struct {a: 6, b: 7.5}
// gdb-command:continue

// === LLDB TESTS ==================================================================================

// lldb-command:run

// lldb-command:print *t0
// lldbg-check:[...]$0 = 1
// lldbr-check:(i32) *t0 = 1
// lldb-command:print *t1
// lldbg-check:[...]$1 = 2.5
// lldbr-check:(f64) *t1 = 2.5
// lldb-command:continue

// lldb-command:print *t0
// lldbg-check:[...]$2 = 3.5
// lldbr-check:(f64) *t0 = 3.5
// lldb-command:print *t1
// lldbg-check:[...]$3 = 4
// lldbr-check:(u16) *t1 = 4
// lldb-command:continue

// lldb-command:print *t0
// lldbg-check:[...]$4 = 5
// lldbr-check:(i32) *t0 = 5
// lldb-command:print *t1
// lldbg-check:[...]$5 = Struct { a: 6, b: 7.5 }
// lldbr-check:(generic_function::Struct) *t1 = Struct { a: 6, b: 7.5 }
// lldb-command:continue

#![feature(omit_gdb_pretty_printer_section)]
#![omit_gdb_pretty_printer_section]

#[derive(Clone)]
struct Struct {
    a: isize,
    b: f64
}

fn dup_tup<T0: Clone, T1: Clone>(t0: &T0, t1: &T1) -> ((T0, T1), (T1, T0)) {
    let ret = ((t0.clone(), t1.clone()), (t1.clone(), t0.clone()));
    zzz(); // #break
    ret
}

fn main() {

    let _ = dup_tup(&1, &2.5f64);
    let _ = dup_tup(&3.5f64, &4_u16);
    let _ = dup_tup(&5, &Struct { a: 6, b: 7.5 });
}

fn zzz() {()}
<|endoftext|>


<commit_before>use extra::url;
use extra::json;
use std::str;

use http::client::RequestWriter;
use http::method::Get;
use http::headers::request::ExtensionHeader;

use api;

fn make_mixes_url(smart_id: &str) -> url::Url {
    from_str(format!("http://8tracks.com/mix_sets/{}.json?include=mixes[likes_count]", smart_id)).unwrap()
}

fn make_play_token_url() -> url::Url {
    from_str("http://8tracks.com/sets/new.json").unwrap()
}

fn make_play_url(pt: &api::PlayToken, mix: &api::Mix) -> url::Url {
    from_str(format!("http://8tracks.com/sets/{}/play.json?mix_id={}",
                     **pt, mix.id)).unwrap()
}

fn make_next_track_url(pt: &api::PlayToken, mix: &api::Mix) -> url::Url {
    from_str(format!("http://8tracks.com/sets/{}/next.json?mix_id={}",
                     **pt, mix.id)).unwrap()
}

fn make_skip_track_url(pt: &api::PlayToken, mix: &api::Mix) -> url::Url {
    from_str(format!("http://8tracks.com/sets/{}/skip.json?mix_id={}",
                     **pt, mix.id)).unwrap()
}

fn make_report_url(pt: &api::PlayToken, track_id: uint, mix_id: uint) -> url::Url {
    from_str(format!("http://8tracks.com/sets/{}/report.json?track_id={}&mix_id={}",
        **pt, track_id, mix_id)).unwrap()
}

pub fn get_data_from_url_str(s: &str) -> ~[u8] {
    let url = from_str(s).unwrap();
    get_data_from_url(url)
}

fn get_data_from_url(url: url::Url) -> ~[u8] {
    let mut request = RequestWriter::new(Get, url);
    request.headers.insert(ExtensionHeader(~"X-Api-Key", api::API_KEY.to_str()));
    request.headers.insert(ExtensionHeader(~"X-Api-Version", api::API_VERSION.to_str()));
    let mut response = match request.read_response() {
        Ok(response) => response,
        Err(_) => fail!("failed to get mixes"),
    };
    response.read_to_end()
}

fn get_json_from_url(url: url::Url) -> json::Json {
    let data = get_data_from_url(url);
    json::from_str(str::from_utf8_owned(data)).unwrap()
}

pub fn get_mix_set(smart_id: &str) -> json::Json {
    get_json_from_url(make_mixes_url(smart_id))
}

pub fn get_play_token() -> json::Json {
    get_json_from_url(make_play_token_url())
}

pub fn get_play_state(pt: &api::PlayToken, mix: &api::Mix) -> json::Json {
    get_json_from_url(make_play_url(pt, mix))
}

pub fn get_next_track(pt: &api::PlayToken, mix: &api::Mix) -> json::Json {
    get_json_from_url(make_next_track_url(pt, mix))
}

pub fn get_skip_track(pt: &api::PlayToken, mix: &api::Mix) -> json::Json {
    get_json_from_url(make_skip_track_url(pt, mix))
}

/// Ignoring returned json, if it doesn't work, meh
pub fn report_track(pt: &api::PlayToken, track_id: uint, mix_id: uint) {
    let resp = get_json_from_url(make_report_url(pt, track_id, mix_id));
    debug!("reported track, response was {}", resp.to_str());
}
<commit_msg>webinterface: change failure message to be more appropriate<commit_after>use extra::url;
use extra::json;
use std::str;

use http::client::RequestWriter;
use http::method::Get;
use http::headers::request::ExtensionHeader;

use api;

fn make_mixes_url(smart_id: &str) -> url::Url {
    from_str(format!("http://8tracks.com/mix_sets/{}.json?include=mixes[likes_count]", smart_id)).unwrap()
}

fn make_play_token_url() -> url::Url {
    from_str("http://8tracks.com/sets/new.json").unwrap()
}

fn make_play_url(pt: &api::PlayToken, mix: &api::Mix) -> url::Url {
    from_str(format!("http://8tracks.com/sets/{}/play.json?mix_id={}",
                     **pt, mix.id)).unwrap()
}

fn make_next_track_url(pt: &api::PlayToken, mix: &api::Mix) -> url::Url {
    from_str(format!("http://8tracks.com/sets/{}/next.json?mix_id={}",
                     **pt, mix.id)).unwrap()
}

fn make_skip_track_url(pt: &api::PlayToken, mix: &api::Mix) -> url::Url {
    from_str(format!("http://8tracks.com/sets/{}/skip.json?mix_id={}",
                     **pt, mix.id)).unwrap()
}

fn make_report_url(pt: &api::PlayToken, track_id: uint, mix_id: uint) -> url::Url {
    from_str(format!("http://8tracks.com/sets/{}/report.json?track_id={}&mix_id={}",
        **pt, track_id, mix_id)).unwrap()
}

pub fn get_data_from_url_str(s: &str) -> ~[u8] {
    let url = from_str(s).unwrap();
    get_data_from_url(url)
}

fn get_data_from_url(url: url::Url) -> ~[u8] {
    let mut request = RequestWriter::new(Get, url);
    request.headers.insert(ExtensionHeader(~"X-Api-Key", api::API_KEY.to_str()));
    request.headers.insert(ExtensionHeader(~"X-Api-Version", api::API_VERSION.to_str()));
    let mut response = match request.read_response() {
        Ok(response) => response,
        Err(_) => fail!("failed to fetch data"),
    };
    response.read_to_end()
}

fn get_json_from_url(url: url::Url) -> json::Json {
    let data = get_data_from_url(url);
    json::from_str(str::from_utf8_owned(data)).unwrap()
}

pub fn get_mix_set(smart_id: &str) -> json::Json {
    get_json_from_url(make_mixes_url(smart_id))
}

pub fn get_play_token() -> json::Json {
    get_json_from_url(make_play_token_url())
}

pub fn get_play_state(pt: &api::PlayToken, mix: &api::Mix) -> json::Json {
    get_json_from_url(make_play_url(pt, mix))
}

pub fn get_next_track(pt: &api::PlayToken, mix: &api::Mix) -> json::Json {
    get_json_from_url(make_next_track_url(pt, mix))
}

pub fn get_skip_track(pt: &api::PlayToken, mix: &api::Mix) -> json::Json {
    get_json_from_url(make_skip_track_url(pt, mix))
}

/// Ignoring returned json, if it doesn't work, meh
pub fn report_track(pt: &api::PlayToken, track_id: uint, mix_id: uint) {
    let resp = get_json_from_url(make_report_url(pt, track_id, mix_id));
    debug!("reported track, response was {}", resp.to_str());
}
<|endoftext|>