<commit_before>#![feature(test)] // compiletest_rs requires this attribute
#![feature(once_cell)]

use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;

use std::env::{self, remove_var, set_var, var_os};
use std::ffi::{OsStr, OsString};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

mod cargo;

// whether to run internal tests or not
const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");

fn host_lib() -> PathBuf {
    option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
}

fn clippy_driver_path() -> PathBuf {
    option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
}

// When we'll want to use `extern crate ..` for a dependency that is used
// both by the crate and the compiler itself, we can't simply pass -L flags
// as we'll get a duplicate matching versions. Instead, disambiguate with
// `--extern dep=path`.
// See https://github.com/rust-lang/rust-clippy/issues/4015.
//
// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files.
//        Because it would force-rebuild if the options passed to `build` command is not the same
//        as what we manually pass to `cargo` invocation
fn third_party_crates() -> String {
    use std::collections::HashMap;
    static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"];
    let dep_dir = cargo::TARGET_LIB.join("deps");
    let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
    for entry in fs::read_dir(dep_dir).unwrap() {
        let path = match entry {
            Ok(entry) => entry.path(),
            Err(_) => continue,
        };
        if let Some(name) = path.file_name().and_then(OsStr::to_str) {
            for dep in CRATES {
                if name.starts_with(&format!("lib{}-", dep))
                    && name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true)
                {
                    if let Some(old) = crates.insert(dep, path.clone()) {
                        // Check which action should be done in order to remove compiled deps.
                        // If pre-installed version of compiler is used, `cargo clean` will do.
                        // Otherwise (for bootstrapped compiler), the dependencies directory
                        // must be removed manually.
                        let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() {
                            "remove the stageN-tools directory"
                        } else {
                            "run `cargo clean`"
                        };

                        panic!(
                            "\n---------------------------------------------------\n\n \
                            Found multiple rlibs for crate `{}`: `{:?}` and `{:?}`.\n \
                            Probably, you need to {} before running tests again.\n \
                            \nFor details on that error see https://github.com/rust-lang/rust-clippy/issues/7343 \
                            \n---------------------------------------------------\n",
                            dep, old, path, suggested_action
                        );
                    }
                    break;
                }
            }
        }
    }

    let v: Vec<_> = crates
        .into_iter()
        .map(|(dep, path)| format!("--extern {}={}", dep, path.display()))
        .collect();
    v.join(" ")
}

fn default_config() -> compiletest::Config {
    let mut config = compiletest::Config::default();

    if let Ok(filters) = env::var("TESTNAME") {
        config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
    }

    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
        let path = PathBuf::from(path);
        config.run_lib_path = path.clone();
        config.compile_lib_path = path;
    }

    config.target_rustcflags = Some(format!(
        "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}",
        host_lib().join("deps").display(),
        cargo::TARGET_LIB.join("deps").display(),
        third_party_crates(),
    ));

    config.build_base = host_lib().join("test_build_base");
    config.rustc_path = clippy_driver_path();
    config
}

fn run_ui(cfg: &mut compiletest::Config) {
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui");
    // use tests/clippy.toml
    let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
    compiletest::run_tests(cfg);
}

fn run_internal_tests(cfg: &mut compiletest::Config) {
    // only run internal tests with the internal-tests feature
    if !RUN_INTERNAL_TESTS {
        return;
    }
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui-internal");
    compiletest::run_tests(cfg);
}

fn run_ui_toml(config: &mut compiletest::Config) {
    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);
        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }
            let dir_path = dir.path();
            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
            for file in fs::read_dir(&dir_path)? {
                let file = file?;
                let file_path = file.path();
                if file.file_type()?.is_dir() {
                    continue;
                }
                if file_path.extension() != Some(OsStr::new("rs")) {
                    continue;
                }
                let paths = compiletest::common::TestPaths {
                    file: file_path,
                    base: config.src_base.clone(),
                    relative_dir: dir_path.file_name().unwrap().into(),
                };
                let test_name = compiletest::make_test_name(config, &paths);
                let index = tests
                    .iter()
                    .position(|test| test.desc.name == test_name)
                    .expect("The test should be in there");
                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let res = run_tests(config, tests);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn run_ui_cargo(config: &mut compiletest::Config) {
    fn run_tests(
        config: &compiletest::Config,
        filters: &[String],
        mut tests: Vec<tester::TestDescAndFn>,
    ) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);

        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }

            // Use the filter if provided
            let dir_path = dir.path();
            for filter in filters {
                if !dir_path.ends_with(filter) {
                    continue;
                }
            }

            for case in fs::read_dir(&dir_path)? {
                let case = case?;
                if !case.file_type()?.is_dir() {
                    continue;
                }

                let src_path = case.path().join("src");

                // When switching between branches, if the previous branch had a test
                // that the current branch does not have, the directory is not removed
                // because an ignored Cargo.lock file exists.
                if !src_path.exists() {
                    continue;
                }

                env::set_current_dir(&src_path)?;
                for file in fs::read_dir(&src_path)? {
                    let file = file?;
                    if file.file_type()?.is_dir() {
                        continue;
                    }

                    // Search for the main file to avoid running a test for each file in the project
                    let file_path = file.path();
                    match file_path.file_name().and_then(OsStr::to_str) {
                        Some("main.rs") => {},
                        _ => continue,
                    }
                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
                    let paths = compiletest::common::TestPaths {
                        file: file_path,
                        base: config.src_base.clone(),
                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
                    };
                    let test_name = compiletest::make_test_name(config, &paths);
                    let index = tests
                        .iter()
                        .position(|test| test.desc.name == test_name)
                        .expect("The test should be in there");
                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
                }
            }
        }
        Ok(result)
    }

    if cargo::is_rustc_test_suite() {
        return;
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let current_dir = env::current_dir().unwrap();
    let res = run_tests(config, &config.filters, tests);
    env::set_current_dir(current_dir).unwrap();

    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("__CLIPPY_INTERNAL_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    let mut config = default_config();
    run_ui(&mut config);
    run_ui_toml(&mut config);
    run_ui_cargo(&mut config);
    run_internal_tests(&mut config);
}

/// Restores an env var on drop
#[must_use]
struct VarGuard {
    key: &'static str,
    value: Option<OsString>,
}

impl VarGuard {
    fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
        let value = var_os(key);
        set_var(key, val);
        Self { key, value }
    }
}

impl Drop for VarGuard {
    fn drop(&mut self) {
        match self.value.as_deref() {
            None => remove_var(self.key),
            Some(value) => set_var(self.key, value),
        }
    }
}
<commit_msg>Auto merge of #7485 - camsteffen:add-test-externs, r=flip1995<commit_after>#![feature(test)] // compiletest_rs requires this attribute
#![feature(once_cell)]

use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode;

use std::env::{self, remove_var, set_var, var_os};
use std::ffi::{OsStr, OsString};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

mod cargo;

// whether to run internal tests or not
const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");

fn host_lib() -> PathBuf {
    option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
}

fn clippy_driver_path() -> PathBuf {
    option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
}

// When we'll want to use `extern crate ..` for a dependency that is used
// both by the crate and the compiler itself, we can't simply pass -L flags
// as we'll get a duplicate matching versions. Instead, disambiguate with
// `--extern dep=path`.
// See https://github.com/rust-lang/rust-clippy/issues/4015.
//
// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files.
//        Because it would force-rebuild if the options passed to `build` command is not the same
//        as what we manually pass to `cargo` invocation
fn third_party_crates() -> String {
    use std::collections::HashMap;
    static CRATES: &[&str] = &[
        "clippy_lints",
        "clippy_utils",
        "if_chain",
        "quote",
        "regex",
        "serde",
        "serde_derive",
        "syn",
    ];
    let dep_dir = cargo::TARGET_LIB.join("deps");
    let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
    for entry in fs::read_dir(dep_dir).unwrap() {
        let path = match entry {
            Ok(entry) => entry.path(),
            Err(_) => continue,
        };
        if let Some(name) = path.file_name().and_then(OsStr::to_str) {
            for dep in CRATES {
                if name.starts_with(&format!("lib{}-", dep))
                    && name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true)
                {
                    if let Some(old) = crates.insert(dep, path.clone()) {
                        // Check which action should be done in order to remove compiled deps.
                        // If pre-installed version of compiler is used, `cargo clean` will do.
                        // Otherwise (for bootstrapped compiler), the dependencies directory
                        // must be removed manually.
                        let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() {
                            "remove the stageN-tools directory"
                        } else {
                            "run `cargo clean`"
                        };

                        panic!(
                            "\n---------------------------------------------------\n\n \
                            Found multiple rlibs for crate `{}`: `{:?}` and `{:?}`.\n \
                            Probably, you need to {} before running tests again.\n \
                            \nFor details on that error see https://github.com/rust-lang/rust-clippy/issues/7343 \
                            \n---------------------------------------------------\n",
                            dep, old, path, suggested_action
                        );
                    }
                    break;
                }
            }
        }
    }

    let v: Vec<_> = crates
        .into_iter()
        .map(|(dep, path)| format!("--extern {}={}", dep, path.display()))
        .collect();
    v.join(" ")
}

fn default_config() -> compiletest::Config {
    let mut config = compiletest::Config::default();

    if let Ok(filters) = env::var("TESTNAME") {
        config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
    }

    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
        let path = PathBuf::from(path);
        config.run_lib_path = path.clone();
        config.compile_lib_path = path;
    }

    config.target_rustcflags = Some(format!(
        "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}",
        host_lib().join("deps").display(),
        cargo::TARGET_LIB.join("deps").display(),
        third_party_crates(),
    ));

    config.build_base = host_lib().join("test_build_base");
    config.rustc_path = clippy_driver_path();
    config
}

fn run_ui(cfg: &mut compiletest::Config) {
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui");
    // use tests/clippy.toml
    let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
    compiletest::run_tests(cfg);
}

fn run_internal_tests(cfg: &mut compiletest::Config) {
    // only run internal tests with the internal-tests feature
    if !RUN_INTERNAL_TESTS {
        return;
    }
    cfg.mode = TestMode::Ui;
    cfg.src_base = Path::new("tests").join("ui-internal");
    compiletest::run_tests(cfg);
}

fn run_ui_toml(config: &mut compiletest::Config) {
    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);
        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }
            let dir_path = dir.path();
            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
            for file in fs::read_dir(&dir_path)? {
                let file = file?;
                let file_path = file.path();
                if file.file_type()?.is_dir() {
                    continue;
                }
                if file_path.extension() != Some(OsStr::new("rs")) {
                    continue;
                }
                let paths = compiletest::common::TestPaths {
                    file: file_path,
                    base: config.src_base.clone(),
                    relative_dir: dir_path.file_name().unwrap().into(),
                };
                let test_name = compiletest::make_test_name(config, &paths);
                let index = tests
                    .iter()
                    .position(|test| test.desc.name == test_name)
                    .expect("The test should be in there");
                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
            }
        }
        Ok(result)
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let res = run_tests(config, tests);
    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn run_ui_cargo(config: &mut compiletest::Config) {
    fn run_tests(
        config: &compiletest::Config,
        filters: &[String],
        mut tests: Vec<tester::TestDescAndFn>,
    ) -> Result<bool, io::Error> {
        let mut result = true;
        let opts = compiletest::test_opts(config);

        for dir in fs::read_dir(&config.src_base)? {
            let dir = dir?;
            if !dir.file_type()?.is_dir() {
                continue;
            }

            // Use the filter if provided
            let dir_path = dir.path();
            for filter in filters {
                if !dir_path.ends_with(filter) {
                    continue;
                }
            }

            for case in fs::read_dir(&dir_path)? {
                let case = case?;
                if !case.file_type()?.is_dir() {
                    continue;
                }

                let src_path = case.path().join("src");

                // When switching between branches, if the previous branch had a test
                // that the current branch does not have, the directory is not removed
                // because an ignored Cargo.lock file exists.
                if !src_path.exists() {
                    continue;
                }

                env::set_current_dir(&src_path)?;
                for file in fs::read_dir(&src_path)? {
                    let file = file?;
                    if file.file_type()?.is_dir() {
                        continue;
                    }

                    // Search for the main file to avoid running a test for each file in the project
                    let file_path = file.path();
                    match file_path.file_name().and_then(OsStr::to_str) {
                        Some("main.rs") => {},
                        _ => continue,
                    }
                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
                    let paths = compiletest::common::TestPaths {
                        file: file_path,
                        base: config.src_base.clone(),
                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
                    };
                    let test_name = compiletest::make_test_name(config, &paths);
                    let index = tests
                        .iter()
                        .position(|test| test.desc.name == test_name)
                        .expect("The test should be in there");
                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
                }
            }
        }
        Ok(result)
    }

    if cargo::is_rustc_test_suite() {
        return;
    }

    config.mode = TestMode::Ui;
    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();

    let tests = compiletest::make_tests(config);

    let current_dir = env::current_dir().unwrap();
    let res = run_tests(config, &config.filters, tests);
    env::set_current_dir(current_dir).unwrap();

    match res {
        Ok(true) => {},
        Ok(false) => panic!("Some tests failed"),
        Err(e) => {
            panic!("I/O failure during tests: {:?}", e);
        },
    }
}

fn prepare_env() {
    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
    set_var("__CLIPPY_INTERNAL_TESTS", "true");
    //set_var("RUST_BACKTRACE", "0");
}

#[test]
fn compile_test() {
    prepare_env();
    let mut config = default_config();
    run_ui(&mut config);
    run_ui_toml(&mut config);
    run_ui_cargo(&mut config);
    run_internal_tests(&mut config);
}

/// Restores an env var on drop
#[must_use]
struct VarGuard {
    key: &'static str,
    value: Option<OsString>,
}

impl VarGuard {
    fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
        let value = var_os(key);
        set_var(key, val);
        Self { key, value }
    }
}

impl Drop for VarGuard {
    fn drop(&mut self) {
        match self.value.as_deref() {
            None => remove_var(self.key),
            Some(value) => set_var(self.key, value),
        }
    }
}
<|endoftext|>


<commit_before>#![feature(collections)]

extern crate regex;

use regex::Regex;

use std::process::{Command,Output};
use std::str::FromStr;

#[derive(Debug)]
struct CompileResult {
  success: bool,
  errors: Vec<CompileError>
}

impl CompileResult {
  fn new(output: Output) -> Self {
    let re = Regex::new(r"(?m)^(?P<file>.*):(?P<line>\d+):(?:\d+): (?:\d+):(?:\d+) error: (?P<message>.*)$").unwrap();
    let stderr = String::from_utf8(output.stderr).unwrap();

    let mut errors = Vec::<CompileError>::new();

    for capture in re.captures_iter(stderr.as_ref()) {
      let file = capture.name("file").unwrap();
      let line: usize = FromStr::from_str(capture.name("line").unwrap()).unwrap();
      let message = capture.name("message").unwrap();

      errors.push(CompileError::new(file, line, message));
    }

    CompileResult {
      success: output.status.success(),
      errors: errors
    }
  }

  fn success(&self) -> bool {
    self.errors.len() == 0
  }

  fn has_error(&self, message: &str, line: usize) -> bool {
    for error in self.errors.iter() {
      if error.line == line && error.message.find(message).is_some() {
        return true
      }
    }

    false
  }
}

#[derive(Debug)]
struct CompileError {
  file: String,
  line: usize,
  message: String
}

impl CompileError {
  fn new(file: &str, line: usize, message: &str) -> Self {
    CompileError {
      file: String::from_str(file),
      line: line,
      message: String::from_str(message)
    }
  }
}

const ERR_LIFETIME: &'static str = "does not live long enough";

macro_rules! assert_compile_success {
  ($file:expr) => ({
    let result = compile($file);
    assert!(result.success(), "expected compilation of {:?} to succeed, got: {:?}", $file, result);
    result
  })
}

macro_rules! assert_compile_fail {
  ($file:expr) => ({
    let result = compile($file);
    assert!(!result.success(), "expected compilation of {:?} to fail", $file);
    result
  });
  ($file:expr,$msg:expr,$line:expr) => ({
    let result = compile($file);
    assert!(!result.success(), "expected compilation of {:?} to fail", $file);
    assert_compile_error!(result, $msg, $line);
    result
  });
}

macro_rules! assert_compile_error {
  ($result:expr,$msg:expr,$line:expr) => ({
    if !$result.has_error($msg, $line) {
      panic!("assertion failed: expected error {:?} on line {}, found {:?}", $msg, $line, $result);
    }
  });
}

fn compile(file: &str) -> CompileResult {
  CompileResult::new(
    Command::new("rustc").
      arg("-L").arg("target/debug").
      arg("-L").arg("target/debug/deps").
      arg(&format!("tests/compile-tests/{}", file)[..]).
      arg("--out-dir").arg("target/debug/compile-tests").
      output().unwrap()
  )
}

#[test]
fn it_should_not_compile_device_list_that_outlives_context() {
  assert_compile_fail!("device_list_outlives_context.rs", ERR_LIFETIME, 6);
}

#[test]
fn it_should_not_compile_device_ref_that_outlives_context() {
  assert_compile_fail!("device_ref_outlives_context.rs", ERR_LIFETIME, 6);
}

#[test]
fn it_should_compile_device_ref_that_outlives_device_list() {
  assert_compile_success!("device_ref_outlives_device_list.rs");
}

#[test]
fn it_should_not_compile_device_iterator_that_outlives_device_list() {
  assert_compile_fail!("device_iterator_outlives_device_list.rs", ERR_LIFETIME, 8);
}

#[test]
fn it_should_not_compile_device_handle_that_outlives_context() {
  assert_compile_fail!("device_handle_outlives_context.rs", ERR_LIFETIME, 6);
}

#[test]
fn it_should_compile_device_handle_that_outlives_device_ref() {
  assert_compile_success!("device_handle_outlives_device_ref.rs");
}

#[test]
fn it_should_not_compile_interface_handle_that_outlives_device_handle() {
  assert_compile_fail!("interface_handle_outlives_device_handle.rs", ERR_LIFETIME, 9);
}
<commit_msg>replaces use of unstable from_str() with to_string()<commit_after>extern crate regex;

use regex::Regex;

use std::process::{Command,Output};
use std::str::FromStr;

#[derive(Debug)]
struct CompileResult {
  success: bool,
  errors: Vec<CompileError>
}

impl CompileResult {
  fn new(output: Output) -> Self {
    let re = Regex::new(r"(?m)^(?P<file>.*):(?P<line>\d+):(?:\d+): (?:\d+):(?:\d+) error: (?P<message>.*)$").unwrap();
    let stderr = String::from_utf8(output.stderr).unwrap();

    let mut errors = Vec::<CompileError>::new();

    for capture in re.captures_iter(stderr.as_ref()) {
      let file = capture.name("file").unwrap();
      let line: usize = FromStr::from_str(capture.name("line").unwrap()).unwrap();
      let message = capture.name("message").unwrap();

      errors.push(CompileError::new(file, line, message));
    }

    CompileResult {
      success: output.status.success(),
      errors: errors
    }
  }

  fn success(&self) -> bool {
    self.errors.len() == 0
  }

  fn has_error(&self, message: &str, line: usize) -> bool {
    for error in self.errors.iter() {
      if error.line == line && error.message.find(message).is_some() {
        return true
      }
    }

    false
  }
}

#[derive(Debug)]
struct CompileError {
  file: String,
  line: usize,
  message: String
}

impl CompileError {
  fn new(file: &str, line: usize, message: &str) -> Self {
    CompileError {
      file: file.to_string(),
      line: line,
      message: message.to_string()
    }
  }
}

const ERR_LIFETIME: &'static str = "does not live long enough";

macro_rules! assert_compile_success {
  ($file:expr) => ({
    let result = compile($file);
    assert!(result.success(), "expected compilation of {:?} to succeed, got: {:?}", $file, result);
    result
  })
}

macro_rules! assert_compile_fail {
  ($file:expr) => ({
    let result = compile($file);
    assert!(!result.success(), "expected compilation of {:?} to fail", $file);
    result
  });
  ($file:expr,$msg:expr,$line:expr) => ({
    let result = compile($file);
    assert!(!result.success(), "expected compilation of {:?} to fail", $file);
    assert_compile_error!(result, $msg, $line);
    result
  });
}

macro_rules! assert_compile_error {
  ($result:expr,$msg:expr,$line:expr) => ({
    if !$result.has_error($msg, $line) {
      panic!("assertion failed: expected error {:?} on line {}, found {:?}", $msg, $line, $result);
    }
  });
}

fn compile(file: &str) -> CompileResult {
  CompileResult::new(
    Command::new("rustc").
      arg("-L").arg("target/debug").
      arg("-L").arg("target/debug/deps").
      arg(&format!("tests/compile-tests/{}", file)[..]).
      arg("--out-dir").arg("target/debug/compile-tests").
      output().unwrap()
  )
}

#[test]
fn it_should_not_compile_device_list_that_outlives_context() {
  assert_compile_fail!("device_list_outlives_context.rs", ERR_LIFETIME, 6);
}

#[test]
fn it_should_not_compile_device_ref_that_outlives_context() {
  assert_compile_fail!("device_ref_outlives_context.rs", ERR_LIFETIME, 6);
}

#[test]
fn it_should_compile_device_ref_that_outlives_device_list() {
  assert_compile_success!("device_ref_outlives_device_list.rs");
}

#[test]
fn it_should_not_compile_device_iterator_that_outlives_device_list() {
  assert_compile_fail!("device_iterator_outlives_device_list.rs", ERR_LIFETIME, 8);
}

#[test]
fn it_should_not_compile_device_handle_that_outlives_context() {
  assert_compile_fail!("device_handle_outlives_context.rs", ERR_LIFETIME, 6);
}

#[test]
fn it_should_compile_device_handle_that_outlives_device_ref() {
  assert_compile_success!("device_handle_outlives_device_ref.rs");
}

#[test]
fn it_should_not_compile_interface_handle_that_outlives_device_handle() {
  assert_compile_fail!("interface_handle_outlives_device_handle.rs", ERR_LIFETIME, 9);
}
<|endoftext|>


<commit_before>extern crate rs_nes;

use rs_nes::memory::{Memory, MemoryError};

#[test]
fn store_and_load_from_ram_succeeds() {
  let mut mem = Memory::new();
  let stored = 0xB;
  let _ = mem.store(0x0, stored);
  let loaded = mem.load(0x0).unwrap();
  assert_eq!(stored, loaded);
}

#[test]
fn ram_should_be_mirrored() {
  let mut mem = Memory::new();
  let stored = 0xB;
  let _ = mem.store(0x0, stored);
  let mirror1 = mem.load(0x800).unwrap();
  let mirror2 = mem.load(0x1000).unwrap();
  let mirror3 = mem.load(0x1800).unwrap();
  assert_eq!(stored, mirror1);
  assert_eq!(stored, mirror2);
  assert_eq!(stored, mirror3);
}

#[test]
fn write_to_mirrored_ram_should_error() {
  let mut mem = Memory::new();
  match mem.store(0x1000, 0x0) {
    Err(MemoryError::WriteToMirror) => { /* expected */ },
    _ => assert!(false)
  }
}

#[test]
fn store_to_rom_should_error() {
  let mut mem = Memory::new();
  match mem.store(0x8000, 0x0) {
    Err(MemoryError::WriteToROM) => { /* expected */ },
    _ => assert!(false)
  }
}

#[test]
fn store_out_of_bounds_address_returns_error() {
  let mut mem = Memory::new();
  match mem.store(0xffffff, 0xb) {
    Err(MemoryError::AddressOutOfBounds) => { /* expected */ },
    _ => assert!(false)
  };
}
<commit_msg>Add memory test<commit_after>extern crate rs_nes;

use rs_nes::memory::{Memory, MemoryError};

#[test]
fn store_and_load_from_ram_succeeds() {
  let mut mem = Memory::new();
  let stored = 0xB;
  let _ = mem.store(0x0, stored);
  let loaded = mem.load(0x0).unwrap();
  assert_eq!(stored, loaded);
}

#[test]
fn ram_should_be_mirrored() {
  let mut mem = Memory::new();
  let stored = 0xB;
  let _ = mem.store(0x0, stored);
  let mirror1 = mem.load(0x800).unwrap();
  let mirror2 = mem.load(0x1000).unwrap();
  let mirror3 = mem.load(0x1800).unwrap();
  assert_eq!(stored, mirror1);
  assert_eq!(stored, mirror2);
  assert_eq!(stored, mirror3);
}

#[test]
fn write_to_mirrored_ram_should_error() {
  let mut mem = Memory::new();
  match mem.store(0x1000, 0x0) {
    Err(MemoryError::WriteToMirror) => { /* expected */ },
    _ => assert!(false)
  }
}

#[test]
fn store_to_rom_should_error() {
  let mut mem = Memory::new();
  match mem.store(0x8000, 0x0) {
    Err(MemoryError::WriteToROM) => { /* expected */ },
    _ => assert!(false)
  }
}

#[test]
fn store_out_of_bounds_address_returns_error() {
  let mut mem = Memory::new();
  match mem.store(0xffffff, 0xb) {
    Err(MemoryError::AddressOutOfBounds) => { /* expected */ },
    _ => assert!(false)
  };
}

#[test]
fn load_out_of_bounds_address_returns_error() {
  let mut mem = Memory::new();
  match mem.load(0xffffff) {
    Err(MemoryError::AddressOutOfBounds) => { /* expected */ },
    _ => assert!(false)
  };
}
<|endoftext|>


<commit_before>extern crate backtrace;

pub use backtrace::Backtrace;

mod quick_error;

#[derive(Debug)]
pub struct ForeignError {
    pub description: String,
    pub display: String,
}

impl ForeignError {
    pub fn new<E>(e: &E) -> ForeignError
        where E: ::std::error::Error
    {
        ForeignError {
            description: e.description().to_string(),
            display: format!("{}", e)
        }
    }
}

#[macro_export]
macro_rules! declare_errors {
    (
        types {
            $error_name:ident, $error_kind_name:ident,
            $chain_error_name:ident, $result_name:ident;
        }

        links {
            $( $link_error_path:path, $link_kind_path:path, $link_variant:ident;  ) *
        }

        foreign_links {
            $( $foreign_link_error_path:path, $foreign_link_variant:ident;  ) *
        }

        errors {
            $( $error_chunks:tt ) *
        }

    ) => {

        // The Error type
        // --------------

        #[derive(Debug)]
        pub struct $error_name(pub $error_kind_name,
                               pub Option<Box<::std::error::Error + Send>>,
                               pub $crate::Backtrace);

        #[allow(unused)]
        impl $error_name {
            pub fn inner(&self) -> &$error_kind_name {
                &self.0
            }

            pub fn into_inner(self) -> $error_kind_name {
                self.0
            }
        }

        impl ::std::error::Error for $error_name {
            fn description(&self) -> &str { self.0.description() }
            fn cause(&self) -> Option<&::std::error::Error> {
                match self.1 {
                    Some(ref c) => Some(&**c),
                    None => None
                }
            }
        }

        impl ::std::fmt::Display for $error_name {
            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                ::std::fmt::Display::fmt(&self.0, f)
            }
        }

        $(
            impl From<$link_error_path> for $error_name {
                fn from(e: $link_error_path) -> Self {
                    $error_name($error_kind_name::$link_variant(e.0), e.1, e.2)
                }
            }
        ) *

        $(
            impl From<$foreign_link_error_path> for $error_name {
                fn from(e: $foreign_link_error_path) -> Self {
                    $error_name(
                        $error_kind_name::$foreign_link_variant($crate::ForeignError::new(&e)),
                        Some(Box::new(e)),
                        $crate::Backtrace::new())
                }
            }
        ) *

        impl From<$error_kind_name> for $error_name {
            fn from(e: $error_kind_name) -> Self {
                $error_name(e, None, $crate::Backtrace::new())
            }
        }

        impl<'a> From<&'a str> for $error_name {
            fn from(s: &'a str) -> Self {
                $error_name(s.into(), None, $crate::Backtrace::new())
            }
        }

        impl From<String> for $error_name {
            fn from(s: String) -> Self {
                $error_name(s.into(), None, $crate::Backtrace::new())
            }
        }

        // The ErrorKind type
        // --------------

        quick_error! {
            #[derive(Debug)]
            pub enum $error_kind_name {

                Msg(s: String) {
                    description(&s)
                    display("{}", s)
                }

                $(
                    $link_variant(e: $link_kind_path) {
                        description(e.description())
                        display("{}", e)
                    }
                ) *

                $(
                    $foreign_link_variant(e: $crate::ForeignError) {
                        description(&e.description)
                        display("{}", e.display)
                    }
                ) *

                $($error_chunks)*
            }
        }

        $(
            impl From<$link_kind_path> for $error_kind_name {
                fn from(e: $link_kind_path) -> Self {
                    $error_kind_name::$link_variant(e)
                }
            }
        ) *

        impl<'a> From<&'a str> for $error_kind_name {
            fn from(s: &'a str) -> Self {
                $error_kind_name::Msg(s.to_string())
            }
        }

        impl From<String> for $error_kind_name {
            fn from(s: String) -> Self {
                $error_kind_name::Msg(s)
            }
        }

        // Lesser types
        // ------------

        pub trait $chain_error_name<T> {
            fn chain_err<F, EK>(self, callback: F) -> ::std::result::Result<T, $error_name>
                where F: FnOnce() -> EK,
                      EK: Into<$error_kind_name>;
        }

        impl<T, E> $chain_error_name<T> for ::std::result::Result<T, E>
            where E: ::std::error::Error + Send + 'static
        {
            fn chain_err<F, EK>(self, callback: F) -> ::std::result::Result<T, $error_name>
                where F: FnOnce() -> EK,
                      EK: Into<$error_kind_name>
            {
                self.map_err(move |e| {
                    // FIXME: Unfortunate backtrace if E already has one
                    $error_name(callback().into(), Some(Box::new(e)), $crate::Backtrace::new())
                })
            }
        }

        pub type $result_name<T> = ::std::result::Result<T, $error_name>;
    };
}
<commit_msg>Whitespace<commit_after>extern crate backtrace;

pub use backtrace::Backtrace;

mod quick_error;

#[derive(Debug)]
pub struct ForeignError {
    pub description: String,
    pub display: String,
}

impl ForeignError {
    pub fn new<E>(e: &E) -> ForeignError
        where E: ::std::error::Error
    {
        ForeignError {
            description: e.description().to_string(),
            display: format!("{}", e)
        }
    }
}

#[macro_export]
macro_rules! declare_errors {
    (
        types {
            $error_name:ident, $error_kind_name:ident,
            $chain_error_name:ident, $result_name:ident;
        }

        links {
            $( $link_error_path:path, $link_kind_path:path, $link_variant:ident;  ) *
        }

        foreign_links {
            $( $foreign_link_error_path:path, $foreign_link_variant:ident;  ) *
        }

        errors {
            $( $error_chunks:tt ) *
        }

    ) => {

        // The Error type
        // --------------

        #[derive(Debug)]
        pub struct $error_name(pub $error_kind_name,
                               pub Option<Box<::std::error::Error + Send>>,
                               pub $crate::Backtrace);

        #[allow(unused)]
        impl $error_name {
            pub fn inner(&self) -> &$error_kind_name {
                &self.0
            }

            pub fn into_inner(self) -> $error_kind_name {
                self.0
            }
        }

        impl ::std::error::Error for $error_name {
            fn description(&self) -> &str { self.0.description() }
            fn cause(&self) -> Option<&::std::error::Error> {
                match self.1 {
                    Some(ref c) => Some(&**c),
                    None => None
                }
            }
        }

        impl ::std::fmt::Display for $error_name {
            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                ::std::fmt::Display::fmt(&self.0, f)
            }
        }

        $(
            impl From<$link_error_path> for $error_name {
                fn from(e: $link_error_path) -> Self {
                    $error_name($error_kind_name::$link_variant(e.0), e.1, e.2)
                }
            }
        ) *

        $(
            impl From<$foreign_link_error_path> for $error_name {
                fn from(e: $foreign_link_error_path) -> Self {
                    $error_name(
                        $error_kind_name::$foreign_link_variant($crate::ForeignError::new(&e)),
                        Some(Box::new(e)),
                        $crate::Backtrace::new())
                }
            }
        ) *

        impl From<$error_kind_name> for $error_name {
            fn from(e: $error_kind_name) -> Self {
                $error_name(e, None, $crate::Backtrace::new())
            }
        }

        impl<'a> From<&'a str> for $error_name {
            fn from(s: &'a str) -> Self {
                $error_name(s.into(), None, $crate::Backtrace::new())
            }
        }

        impl From<String> for $error_name {
            fn from(s: String) -> Self {
                $error_name(s.into(), None, $crate::Backtrace::new())
            }
        }

        // The ErrorKind type
        // --------------

        quick_error! {
            #[derive(Debug)]
            pub enum $error_kind_name {

                Msg(s: String) {
                    description(&s)
                    display("{}", s)
                }

                $(
                    $link_variant(e: $link_kind_path) {
                        description(e.description())
                        display("{}", e)
                    }
                ) *

                $(
                    $foreign_link_variant(e: $crate::ForeignError) {
                        description(&e.description)
                        display("{}", e.display)
                    }
                ) *

                $($error_chunks)*
            }
        }

        $(
            impl From<$link_kind_path> for $error_kind_name {
                fn from(e: $link_kind_path) -> Self {
                    $error_kind_name::$link_variant(e)
                }
            }
        ) *

        impl<'a> From<&'a str> for $error_kind_name {
            fn from(s: &'a str) -> Self {
                $error_kind_name::Msg(s.to_string())
            }
        }

        impl From<String> for $error_kind_name {
            fn from(s: String) -> Self {
                $error_kind_name::Msg(s)
            }
        }

        // The ChainErr trait
        // ------------------

        pub trait $chain_error_name<T> {
            fn chain_err<F, EK>(self, callback: F) -> ::std::result::Result<T, $error_name>
                where F: FnOnce() -> EK,
                      EK: Into<$error_kind_name>;
        }

        impl<T, E> $chain_error_name<T> for ::std::result::Result<T, E>
            where E: ::std::error::Error + Send + 'static
        {
            fn chain_err<F, EK>(self, callback: F) -> ::std::result::Result<T, $error_name>
                where F: FnOnce() -> EK,
                      EK: Into<$error_kind_name>
            {
                self.map_err(move |e| {
                    // FIXME: Unfortunate backtrace if E already has one
                    $error_name(callback().into(), Some(Box::new(e)), $crate::Backtrace::new())
                })
            }
        }

        // The Result type
        // ---------------

        pub type $result_name<T> = ::std::result::Result<T, $error_name>;
    };
}
<|endoftext|>


<commit_before>/*!
Library for reading/writing Labeled Tab-Separated Values

# Example

~~~~~~~~~~~~~~~~~~~~~~
extern mod ltsv;
use ltsv::LTSVWriter;
use ltsv::LTSVReader;

fn main() {
    let infile = io::file_reader(&Path("path/fo/file.tlsv")).get();
    for infile.read_ltsv().each |record| {
        for record.each |&(label, value)| {
            io::println(fmt!("%s: %s", *label, *value));
        }
    }
}
~~~~~~~~~~~~~~~~~~~~~~
*/
#[link(name = "ltsv",
       vers = "0.2",
       uuid = "E0EA0251-E165-4612-919F-38E89ACECBE9",
       url  = "https://github.com/tychosci/rust-ltsv/")];

#[comment = "Library for reading/writing Labeled Tab-Separated Values"];
#[license = "MIT license"];
#[crate_type = "lib"];

use core::container::Map;
use core::hashmap::linear::LinearMap;
use io::WriterUtil;

pub type Record = LinearMap<~str, ~str>;

enum ParseType {
    FieldLabel,
    FieldValue,
    Field,
    Record,
    Ltsv
}

enum ParseDelimiter {
    EOF, TAB, NL, MISC
}

enum ParseResult<T> {
    ParseError(~str),
    ParseOk(ParseType, ParseDelimiter, T)
}

pub trait LTSVWriter {
    fn write_ltsv(&self, ltsv: &[Record]);
    fn write_ltsv_record(&self, record: &Record);
}

pub trait LTSVReader {
    fn read_ltsv(&self) -> ~[Record];
    fn each_ltsv_record(&self, f: &fn(&Record) -> bool);
    fn each_ltsv_field(&self, f: &fn(&(~str, ~str)) -> bool);
}

pub impl<T: io::Writer> LTSVWriter for T {
    fn write_ltsv(&self, ltsv: &[Record]) {
        for ltsv.each |record| {
            self.write_ltsv_record(record);
            self.write_char('\n');
        }
    }
    fn write_ltsv_record(&self, record: &Record) {
        let mut is_first = true;
        for record.each |&(k, v)| {
            if !is_first { self.write_char('\t'); }
            self.write_str(fmt!("%s:%s", *k, *v));
            if is_first { is_first = false; }
        }
    }
}

pub impl<T: io::Reader> LTSVReader for T {
    fn read_ltsv(&self) -> ~[Record] {
        let mut parser = LTSVParser::new(self);
        match parser.parse_ltsv() {
            ParseError(reason) => die!(reason),
            ParseOk(_, _, records) => records
        }
    }
    fn each_ltsv_record(&self, f: &fn(&Record) -> bool) {
        let mut parser = LTSVParser::new(self);
        while !parser.eof() { // FIXME: This emits odd loaning errors.
            match parser.parse_record() {
                ParseError(reason) => die!(reason),
                ParseOk(_, _, record) => if !f(&record) { break; }
            }
        }
    }
    fn each_ltsv_field(&self, f: &fn(&(~str, ~str)) -> bool) {
        let mut parser = LTSVParser::new(self);
        while !parser.eof() { // FIXME: This emits odd loaning errors.
            match parser.parse_field() {
                ParseError(reason) => die!(reason),
                ParseOk(_, _, field) => if !f(&field) { break; }
            }
        }
    }
}

struct LTSVParser<T> {
    priv rd: &T,
    priv cur: int
}

impl<T: io::Reader> LTSVParser<T> {
    static fn new(rd: &r/T) -> LTSVParser/&r<T> {
        let cur = rd.read_byte();
        LTSVParser { rd: rd, cur: cur }
    }

    fn eof(&self) -> bool { self.cur == -1 }

    fn bump(&mut self) {
        if !self.eof() {
            self.cur = self.rd.read_byte();
        }
    }

    fn parse_ltsv(&mut self) -> ParseResult<~[Record]> {
        let mut records = ~[];
        loop {
            match self.parse_record() {
                ParseError(reason) => {
                    return ParseError(reason);
                }
                ParseOk(_, EOF, record) => {
                    records.push(record);
                    break;
                }
                ParseOk(_, _, record) => {
                    records.push(record);
                }
            }
        }
        ParseOk(Ltsv, EOF, records)
    }

    fn parse_record(&mut self) -> ParseResult<Record> {
        let mut record = LinearMap::new();
        self.skip_whitespaces();
        loop {
            match self.parse_field() {
                ParseError(reason) => {
                    return ParseError(reason);
                }
                ParseOk(_, TAB, (label, value)) => {
                    record.insert(label, value);
                }
                ParseOk(_, delim, (label, value)) => {
                    record.insert(label, value);
                    return ParseOk(Record, delim, record);
                }
            }
        }
    }

    fn parse_field(&mut self) -> ParseResult<(~str, ~str)> {
        let label = match self.parse_field_label() {
            ParseError(reason) => return ParseError(reason),
            ParseOk(_, _, label) => { self.bump(); label }
        };
        match self.parse_field_value() {
            ParseError(reason) => {
                ParseError(reason)
            }
            ParseOk(_, delim, value) => {
                self.bump();
                ParseOk(Field, delim, (label, value))
            }
        }
    }

    fn parse_field_label(&mut self) -> ParseResult<~str> {
        let mut bytes = ~[];
        loop {
            match self.cur {
                0x30..0x39 | 0x41..0x5a | 0x61..0x7a | 0x5f |
                0x2e | 0x2d => bytes.push(self.cur as u8),
                0x3a if bytes.len() == 0 => return ParseError(~"label is empty"),
                0x3a => return ParseOk(FieldLabel, MISC, str::from_bytes(bytes)),
                -1   => return ParseError(~"EOF while parsing field label"),
                _    => return ParseError(~"invalid byte detected")
            }
            self.bump();
        }
    }

    fn parse_field_value(&mut self) -> ParseResult<~str> {
        let mut bytes = ~[];
        loop {
            match self.cur {
                0x01..0x08 | 0x0b | 0x0c |
                0x0e..0xff => bytes.push(self.cur as u8),
                0x0d => return self.consume_forward_LF(str::from_bytes(bytes)),
                0x0a => return ParseOk(FieldValue, NL, str::from_bytes(bytes)),
                0x09 => return ParseOk(FieldValue, TAB, str::from_bytes(bytes)),
                -1   => return ParseOk(FieldValue, EOF, str::from_bytes(bytes)),
                _    => return ParseError(~"invalid byte detected")
            }
            self.bump();
        }
    }

    fn consume_forward_LF(&mut self, rv: ~str) -> ParseResult<~str> {
        self.bump();
        if self.cur != 0x0a {
            ParseError(~"CR detected, but not provided with LF")
        } else {
            ParseOk(FieldValue, NL, rv)
        }
    }

    fn skip_whitespaces(&mut self) {
        while char::is_whitespace(self.cur as char) {
            self.bump();
        }
    }
}

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

    #[test]
    fn test_parse_simple() {
        let records = io::with_str_reader(~"a:1\tb:2", |rd| rd.read_ltsv());
        assert records.len() == 1;
    }

    #[test]
    fn test_parse_full() {
        let s = io::with_str_writer(|wr| {
            // genzairyou
            wr.write_str(fmt!("%s:%s\t", "tofu", "豆"));
            wr.write_str(fmt!("%s:%s\t", "kamaboko", "魚"));
            wr.write_str(fmt!("%s:%s\n", "sukonbu", "海藻"));
            // konomi
            wr.write_str(fmt!("%s:%s\t", "tofu", "好き"));
            wr.write_str(fmt!("%s:%s\t", "kamaboko", "普通"));
            wr.write_str(fmt!("%s:%s\n", "sukonbu", "苦手"));
        });
        let records = io::with_str_reader(s, |rd| rd.read_ltsv());
        assert records.len() == 2;
    }

    #[test]
    fn test_parse_ltsv_trailing_nl_and_write() {
        let s = io::with_str_writer(|wr| {
            wr.write_str(fmt!("%s:%s\t", "neko", "yes"));
            wr.write_str(fmt!("%s:%s\t", "inu", "yes"));
            wr.write_str(fmt!("%s:%s\n", "tori", "yes"));
        });
        let records_1 = io::with_str_reader(s, |rd| rd.read_ltsv());
        let s2 = io::with_str_writer(|wr| wr.write_ltsv(records_1));
        let records_2 = io::with_str_reader(s2, |rd| rd.read_ltsv());
        assert records_1 == records_2;
    }
}
<commit_msg>Use mutable field to avoid loan errors for now<commit_after>/*!
Library for reading/writing Labeled Tab-Separated Values

# Example

~~~~~~~~~~~~~~~~~~~~~~
extern mod ltsv;
use ltsv::LTSVWriter;
use ltsv::LTSVReader;

fn main() {
    let infile = io::file_reader(&Path("path/fo/file.tlsv")).get();
    for infile.read_ltsv().each |record| {
        for record.each |&(label, value)| {
            io::println(fmt!("%s: %s", *label, *value));
        }
    }
}
~~~~~~~~~~~~~~~~~~~~~~
*/
#[link(name = "ltsv",
       vers = "0.2",
       uuid = "E0EA0251-E165-4612-919F-38E89ACECBE9",
       url  = "https://github.com/tychosci/rust-ltsv/")];

#[comment = "Library for reading/writing Labeled Tab-Separated Values"];
#[license = "MIT license"];
#[crate_type = "lib"];

use core::container::Map;
use core::hashmap::linear::LinearMap;
use io::WriterUtil;

pub type Record = LinearMap<~str, ~str>;

enum ParseType {
    FieldLabel,
    FieldValue,
    Field,
    Record,
    Ltsv
}

enum ParseDelimiter {
    EOF, TAB, NL, MISC
}

enum ParseResult<T> {
    ParseError(~str),
    ParseOk(ParseType, ParseDelimiter, T)
}

pub trait LTSVWriter {
    fn write_ltsv(&self, ltsv: &[Record]);
    fn write_ltsv_record(&self, record: &Record);
}

pub trait LTSVReader {
    fn read_ltsv(&self) -> ~[Record];
    fn each_ltsv_record(&self, f: &fn(&Record) -> bool);
    fn each_ltsv_field(&self, f: &fn(&(~str, ~str)) -> bool);
}

pub impl<T: io::Writer> LTSVWriter for T {
    fn write_ltsv(&self, ltsv: &[Record]) {
        for ltsv.each |record| {
            self.write_ltsv_record(record);
            self.write_char('\n');
        }
    }
    fn write_ltsv_record(&self, record: &Record) {
        let mut is_first = true;
        for record.each |&(k, v)| {
            if !is_first { self.write_char('\t'); }
            self.write_str(fmt!("%s:%s", *k, *v));
            if is_first { is_first = false; }
        }
    }
}

pub impl<T: io::Reader> LTSVReader for T {
    fn read_ltsv(&self) -> ~[Record] {
        let mut parser = LTSVParser::new(self);
        match parser.parse_ltsv() {
            ParseError(reason) => die!(reason),
            ParseOk(_, _, records) => records
        }
    }
    fn each_ltsv_record(&self, f: &fn(&Record) -> bool) {
        let mut parser = LTSVParser::new(self);
        while !parser.eof() { // FIXME: This emits odd loaning errors.
            match parser.parse_record() {
                ParseError(reason) => die!(reason),
                ParseOk(_, _, record) => if !f(&record) { break; }
            }
        }
    }
    fn each_ltsv_field(&self, f: &fn(&(~str, ~str)) -> bool) {
        let mut parser = LTSVParser::new(self);
        while !parser.eof() { // FIXME: This emits odd loaning errors.
            match parser.parse_field() {
                ParseError(reason) => die!(reason),
                ParseOk(_, _, field) => if !f(&field) { break; }
            }
        }
    }
}

struct LTSVParser<T> {
    priv rd: &T,
    priv mut cur: int
}

impl<T: io::Reader> LTSVParser<T> {
    static fn new(rd: &r/T) -> LTSVParser/&r<T> {
        let cur = rd.read_byte();
        LTSVParser { rd: rd, cur: cur }
    }

    fn eof(&self) -> bool { self.cur == -1 }

    fn bump(&self) {
        if !self.eof() {
            self.cur = self.rd.read_byte();
        }
    }

    fn parse_ltsv(&self) -> ParseResult<~[Record]> {
        let mut records = ~[];
        loop {
            match self.parse_record() {
                ParseError(reason) => {
                    return ParseError(reason);
                }
                ParseOk(_, EOF, record) => {
                    records.push(record);
                    break;
                }
                ParseOk(_, _, record) => {
                    records.push(record);
                }
            }
        }
        ParseOk(Ltsv, EOF, records)
    }

    fn parse_record(&self) -> ParseResult<Record> {
        let mut record = LinearMap::new();
        loop {
            match self.parse_field() {
                ParseError(reason) => {
                    return ParseError(reason);
                }
                ParseOk(_, TAB, (label, value)) => {
                    record.insert(label, value);
                }
                ParseOk(_, delim, (label, value)) => {
                    record.insert(label, value);
                    return ParseOk(Record, delim, record);
                }
            }
        }
    }

    fn parse_field(&self) -> ParseResult<(~str, ~str)> {
        self.skip_whitespaces();
        let label = match self.parse_field_label() {
            ParseError(reason) => return ParseError(reason),
            ParseOk(_, _, label) => { self.bump(); label }
        };
        match self.parse_field_value() {
            ParseError(reason) => {
                ParseError(reason)
            }
            ParseOk(_, delim, value) => {
                self.bump();
                self.skip_whitespaces();
                // re-check EOF
                let delim = if self.eof() { EOF } else { delim };
                ParseOk(Field, delim, (label, value))
            }
        }
    }

    priv fn parse_field_label(&self) -> ParseResult<~str> {
        let mut bytes = ~[];
        loop {
            match self.cur {
                0x30..0x39 | 0x41..0x5a | 0x61..0x7a | 0x5f |
                0x2e | 0x2d => bytes.push(self.cur as u8),
                0x3a if bytes.len() == 0 => return ParseError(~"label is empty"),
                0x3a => return ParseOk(FieldLabel, MISC, str::from_bytes(bytes)),
                -1   => return ParseError(~"EOF while parsing field label"),
                _    => return ParseError(~"invalid byte detected")
            }
            self.bump();
        }
    }

    priv fn parse_field_value(&self) -> ParseResult<~str> {
        let mut bytes = ~[];
        loop {
            match self.cur {
                0x01..0x08 | 0x0b | 0x0c |
                0x0e..0xff => bytes.push(self.cur as u8),
                0x0d => return self.consume_forward_LF(str::from_bytes(bytes)),
                0x0a => return ParseOk(FieldValue, NL, str::from_bytes(bytes)),
                0x09 => return ParseOk(FieldValue, TAB, str::from_bytes(bytes)),
                -1   => return ParseOk(FieldValue, EOF, str::from_bytes(bytes)),
                _    => return ParseError(~"invalid byte detected")
            }
            self.bump();
        }
    }

    priv fn consume_forward_LF(&self, rv: ~str) -> ParseResult<~str> {
        self.bump();
        if self.cur != 0x0a {
            ParseError(~"CR detected, but not provided with LF")
        } else {
            ParseOk(FieldValue, NL, rv)
        }
    }

    priv fn skip_whitespaces(&self) {
        while char::is_whitespace(self.cur as char) {
            self.bump();
        }
    }
}

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

    #[test]
    fn test_parse_simple() {
        let records = io::with_str_reader(~"a:1\tb:2", |rd| rd.read_ltsv());
        assert records.len() == 1;
    }

    #[test]
    fn test_parse_full() {
        let s = io::with_str_writer(|wr| {
            // genzairyou
            wr.write_str(fmt!("%s:%s\t", "tofu", "豆"));
            wr.write_str(fmt!("%s:%s\t", "kamaboko", "魚"));
            wr.write_str(fmt!("%s:%s\n", "sukonbu", "海藻"));
            // konomi
            wr.write_str(fmt!("%s:%s\t", "tofu", "好き"));
            wr.write_str(fmt!("%s:%s\t", "kamaboko", "普通"));
            wr.write_str(fmt!("%s:%s\n", "sukonbu", "苦手"));
        });
        let records = io::with_str_reader(s, |rd| rd.read_ltsv());
        assert records.len() == 2;
    }

    #[test]
    fn test_parse_ltsv_trailing_nl_and_write() {
        let s = io::with_str_writer(|wr| {
            wr.write_str(fmt!("%s:%s\t", "neko", "yes"));
            wr.write_str(fmt!("%s:%s\t", "inu", "yes"));
            wr.write_str(fmt!("%s:%s\n", "tori", "yes"));
        });
        let records_1 = io::with_str_reader(s, |rd| rd.read_ltsv());
        let s2 = io::with_str_writer(|wr| wr.write_ltsv(records_1));
        let records_2 = io::with_str_reader(s2, |rd| rd.read_ltsv());
        assert records_1 == records_2;
    }
}
<|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.

//! This module provides linkage between libgraphviz traits and
//! `rustc::middle::typeck::infer::region_constraints`, generating a
//! rendering of the graph represented by the list of `Constraint`
//! instances (which make up the edges of the graph), as well as the
//! origin for each constraint (which are attached to the labels on
//! each edge).

/// For clarity, rename the graphviz crate locally to dot.
use graphviz as dot;

use hir::def_id::DefIndex;
use ty;
use middle::free_region::RegionRelations;
use middle::region;
use super::Constraint;
use infer::SubregionOrigin;
use infer::region_constraints::RegionConstraintData;
use util::nodemap::{FxHashMap, FxHashSet};

use std::borrow::Cow;
use std::collections::hash_map::Entry::Vacant;
use std::collections::btree_map::BTreeMap;
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::sync::atomic::{AtomicBool, Ordering};

fn print_help_message() {
    println!("\
-Z print-region-graph by default prints a region constraint graph for every \n\
function body, to the path `/tmp/constraints.nodeXXX.dot`, where the XXX is \n\
replaced with the node id of the function under analysis.                   \n\
                                                                            \n\
To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`,   \n\
where XXX is the node id desired.                                           \n\
                                                                            \n\
To generate output to some path other than the default                      \n\
`/tmp/constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`;  \n\
occurrences of the character `%` in the requested path will be replaced with\n\
the node id of the function under analysis.                                 \n\
                                                                            \n\
(Since you requested help via RUST_REGION_GRAPH=help, no region constraint  \n\
graphs will be printed.                                                     \n\
");
}

pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>(
    region_data: &RegionConstraintData<'tcx>,
    region_rels: &RegionRelations<'a, 'gcx, 'tcx>)
{
    let tcx = region_rels.tcx;
    let context = region_rels.context;

    if !tcx.sess.opts.debugging_opts.print_region_graph {
        return;
    }

    let requested_node = env::var("RUST_REGION_GRAPH_NODE")
        .ok().and_then(|s| s.parse().map(DefIndex::from_raw_u32).ok());

    if requested_node.is_some() && requested_node != Some(context.index) {
        return;
    }

    let requested_output = env::var("RUST_REGION_GRAPH");
    debug!("requested_output: {:?} requested_node: {:?}",
           requested_output,
           requested_node);

    let output_path = {
        let output_template = match requested_output {
            Ok(ref s) if s == "help" => {
                static PRINTED_YET: AtomicBool = AtomicBool::new(false);
                if !PRINTED_YET.load(Ordering::SeqCst) {
                    print_help_message();
                    PRINTED_YET.store(true, Ordering::SeqCst);
                }
                return;
            }

            Ok(other_path) => other_path,
            Err(_) => "/tmp/constraints.node%.dot".to_string(),
        };

        if output_template.is_empty() {
            panic!("empty string provided as RUST_REGION_GRAPH");
        }

        if output_template.contains('%') {
            let mut new_str = String::new();
            for c in output_template.chars() {
                if c == '%' {
                    new_str.push_str(&context.index.as_raw_u32().to_string());
                } else {
                    new_str.push(c);
                }
            }
            new_str
        } else {
            output_template
        }
    };

    match dump_region_data_to(region_rels, &region_data.constraints, &output_path) {
        Ok(()) => {}
        Err(e) => {
            let msg = format!("io error dumping region constraints: {}", e);
            tcx.sess.err(&msg)
        }
    }
}

struct ConstraintGraph<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
    graph_name: String,
    region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>,
    map: &'a BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>,
    node_ids: FxHashMap<Node, usize>,
}

#[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)]
enum Node {
    RegionVid(ty::RegionVid),
    Region(ty::RegionKind),
}

#[derive(Clone, PartialEq, Eq, Debug, Copy)]
enum Edge<'tcx> {
    Constraint(Constraint<'tcx>),
    EnclScope(region::Scope, region::Scope),
}

impl<'a, 'gcx, 'tcx> ConstraintGraph<'a, 'gcx, 'tcx> {
    fn new(name: String,
           region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>,
           map: &'a ConstraintMap<'tcx>)
           -> ConstraintGraph<'a, 'gcx, 'tcx> {
        let mut i = 0;
        let mut node_ids = FxHashMap();
        {
            let mut add_node = |node| {
                if let Vacant(e) = node_ids.entry(node) {
                    e.insert(i);
                    i += 1;
                }
            };

            for (n1, n2) in map.keys().map(|c| constraint_to_nodes(c)) {
                add_node(n1);
                add_node(n2);
            }

            region_rels.region_scope_tree.each_encl_scope(|sub, sup| {
                add_node(Node::Region(ty::ReScope(sub)));
                add_node(Node::Region(ty::ReScope(sup)));
            });
        }

        ConstraintGraph {
            map,
            node_ids,
            region_rels,
            graph_name: name,
        }
    }
}

impl<'a, 'gcx, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
    type Node = Node;
    type Edge = Edge<'tcx>;
    fn graph_id(&self) -> dot::Id {
        dot::Id::new(&*self.graph_name).unwrap()
    }
    fn node_id(&self, n: &Node) -> dot::Id {
        let node_id = match self.node_ids.get(n) {
            Some(node_id) => node_id,
            None => bug!("no node_id found for node: {:?}", n),
        };
        let name = || format!("node_{}", node_id);
        match dot::Id::new(name()) {
            Ok(id) => id,
            Err(_) => {
                bug!("failed to create graphviz node identified by {}", name());
            }
        }
    }
    fn node_label(&self, n: &Node) -> dot::LabelText {
        match *n {
            Node::RegionVid(n_vid) => dot::LabelText::label(format!("{:?}", n_vid)),
            Node::Region(n_rgn) => dot::LabelText::label(format!("{:?}", n_rgn)),
        }
    }
    fn edge_label(&self, e: &Edge) -> dot::LabelText {
        match *e {
            Edge::Constraint(ref c) =>
                dot::LabelText::label(format!("{:?}", self.map.get(c).unwrap())),
            Edge::EnclScope(..) => dot::LabelText::label(format!("(enclosed)")),
        }
    }
}

fn constraint_to_nodes(c: &Constraint) -> (Node, Node) {
    match *c {
        Constraint::VarSubVar(rv_1, rv_2) =>
            (Node::RegionVid(rv_1), Node::RegionVid(rv_2)),
        Constraint::RegSubVar(r_1, rv_2) =>
            (Node::Region(*r_1), Node::RegionVid(rv_2)),
        Constraint::VarSubReg(rv_1, r_2) =>
            (Node::RegionVid(rv_1), Node::Region(*r_2)),
        Constraint::RegSubReg(r_1, r_2) =>
            (Node::Region(*r_1), Node::Region(*r_2)),
    }
}

fn edge_to_nodes(e: &Edge) -> (Node, Node) {
    match *e {
        Edge::Constraint(ref c) => constraint_to_nodes(c),
        Edge::EnclScope(sub, sup) => {
            (Node::Region(ty::ReScope(sub)),
             Node::Region(ty::ReScope(sup)))
        }
    }
}

impl<'a, 'gcx, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
    type Node = Node;
    type Edge = Edge<'tcx>;
    fn nodes(&self) -> dot::Nodes<Node> {
        let mut set = FxHashSet();
        for node in self.node_ids.keys() {
            set.insert(*node);
        }
        debug!("constraint graph has {} nodes", set.len());
        set.into_iter().collect()
    }
    fn edges(&self) -> dot::Edges<Edge<'tcx>> {
        debug!("constraint graph has {} edges", self.map.len());
        let mut v: Vec<_> = self.map.keys().map(|e| Edge::Constraint(*e)).collect();
        self.region_rels.region_scope_tree.each_encl_scope(|sub, sup| {
            v.push(Edge::EnclScope(sub, sup))
        });
        debug!("region graph has {} edges", v.len());
        Cow::Owned(v)
    }
    fn source(&self, edge: &Edge<'tcx>) -> Node {
        let (n1, _) = edge_to_nodes(edge);
        debug!("edge {:?} has source {:?}", edge, n1);
        n1
    }
    fn target(&self, edge: &Edge<'tcx>) -> Node {
        let (_, n2) = edge_to_nodes(edge);
        debug!("edge {:?} has target {:?}", edge, n2);
        n2
    }
}

pub type ConstraintMap<'tcx> = BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>;

fn dump_region_data_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>,
                                       map: &ConstraintMap<'tcx>,
                                       path: &str)
                                       -> io::Result<()> {
    debug!("dump_region_data map (len: {}) path: {}",
           map.len(),
           path);
    let g = ConstraintGraph::new(format!("region_data"), region_rels, map);
    debug!("dump_region_data calling render");
    let mut v = Vec::new();
    dot::render(&g, &mut v).unwrap();
    File::create(path).and_then(|mut f| f.write_all(&v))
}
<commit_msg>Rollup merge of #52195 - alexcrichton:no-tmp, r=pnkfelix<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.

//! This module provides linkage between libgraphviz traits and
//! `rustc::middle::typeck::infer::region_constraints`, generating a
//! rendering of the graph represented by the list of `Constraint`
//! instances (which make up the edges of the graph), as well as the
//! origin for each constraint (which are attached to the labels on
//! each edge).

/// For clarity, rename the graphviz crate locally to dot.
use graphviz as dot;

use hir::def_id::DefIndex;
use ty;
use middle::free_region::RegionRelations;
use middle::region;
use super::Constraint;
use infer::SubregionOrigin;
use infer::region_constraints::RegionConstraintData;
use util::nodemap::{FxHashMap, FxHashSet};

use std::borrow::Cow;
use std::collections::hash_map::Entry::Vacant;
use std::collections::btree_map::BTreeMap;
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::sync::atomic::{AtomicBool, Ordering};

fn print_help_message() {
    println!("\
-Z print-region-graph by default prints a region constraint graph for every \n\
function body, to the path `constraints.nodeXXX.dot`, where the XXX is \n\
replaced with the node id of the function under analysis.                   \n\
                                                                            \n\
To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`,   \n\
where XXX is the node id desired.                                           \n\
                                                                            \n\
To generate output to some path other than the default                      \n\
`constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`;  \n\
occurrences of the character `%` in the requested path will be replaced with\n\
the node id of the function under analysis.                                 \n\
                                                                            \n\
(Since you requested help via RUST_REGION_GRAPH=help, no region constraint  \n\
graphs will be printed.                                                     \n\
");
}

pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>(
    region_data: &RegionConstraintData<'tcx>,
    region_rels: &RegionRelations<'a, 'gcx, 'tcx>)
{
    let tcx = region_rels.tcx;
    let context = region_rels.context;

    if !tcx.sess.opts.debugging_opts.print_region_graph {
        return;
    }

    let requested_node = env::var("RUST_REGION_GRAPH_NODE")
        .ok().and_then(|s| s.parse().map(DefIndex::from_raw_u32).ok());

    if requested_node.is_some() && requested_node != Some(context.index) {
        return;
    }

    let requested_output = env::var("RUST_REGION_GRAPH");
    debug!("requested_output: {:?} requested_node: {:?}",
           requested_output,
           requested_node);

    let output_path = {
        let output_template = match requested_output {
            Ok(ref s) if s == "help" => {
                static PRINTED_YET: AtomicBool = AtomicBool::new(false);
                if !PRINTED_YET.load(Ordering::SeqCst) {
                    print_help_message();
                    PRINTED_YET.store(true, Ordering::SeqCst);
                }
                return;
            }

            Ok(other_path) => other_path,
            Err(_) => "constraints.node%.dot".to_string(),
        };

        if output_template.is_empty() {
            panic!("empty string provided as RUST_REGION_GRAPH");
        }

        if output_template.contains('%') {
            let mut new_str = String::new();
            for c in output_template.chars() {
                if c == '%' {
                    new_str.push_str(&context.index.as_raw_u32().to_string());
                } else {
                    new_str.push(c);
                }
            }
            new_str
        } else {
            output_template
        }
    };

    match dump_region_data_to(region_rels, &region_data.constraints, &output_path) {
        Ok(()) => {}
        Err(e) => {
            let msg = format!("io error dumping region constraints: {}", e);
            tcx.sess.err(&msg)
        }
    }
}

struct ConstraintGraph<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
    graph_name: String,
    region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>,
    map: &'a BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>,
    node_ids: FxHashMap<Node, usize>,
}

#[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)]
enum Node {
    RegionVid(ty::RegionVid),
    Region(ty::RegionKind),
}

#[derive(Clone, PartialEq, Eq, Debug, Copy)]
enum Edge<'tcx> {
    Constraint(Constraint<'tcx>),
    EnclScope(region::Scope, region::Scope),
}

impl<'a, 'gcx, 'tcx> ConstraintGraph<'a, 'gcx, 'tcx> {
    fn new(name: String,
           region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>,
           map: &'a ConstraintMap<'tcx>)
           -> ConstraintGraph<'a, 'gcx, 'tcx> {
        let mut i = 0;
        let mut node_ids = FxHashMap();
        {
            let mut add_node = |node| {
                if let Vacant(e) = node_ids.entry(node) {
                    e.insert(i);
                    i += 1;
                }
            };

            for (n1, n2) in map.keys().map(|c| constraint_to_nodes(c)) {
                add_node(n1);
                add_node(n2);
            }

            region_rels.region_scope_tree.each_encl_scope(|sub, sup| {
                add_node(Node::Region(ty::ReScope(sub)));
                add_node(Node::Region(ty::ReScope(sup)));
            });
        }

        ConstraintGraph {
            map,
            node_ids,
            region_rels,
            graph_name: name,
        }
    }
}

impl<'a, 'gcx, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
    type Node = Node;
    type Edge = Edge<'tcx>;
    fn graph_id(&self) -> dot::Id {
        dot::Id::new(&*self.graph_name).unwrap()
    }
    fn node_id(&self, n: &Node) -> dot::Id {
        let node_id = match self.node_ids.get(n) {
            Some(node_id) => node_id,
            None => bug!("no node_id found for node: {:?}", n),
        };
        let name = || format!("node_{}", node_id);
        match dot::Id::new(name()) {
            Ok(id) => id,
            Err(_) => {
                bug!("failed to create graphviz node identified by {}", name());
            }
        }
    }
    fn node_label(&self, n: &Node) -> dot::LabelText {
        match *n {
            Node::RegionVid(n_vid) => dot::LabelText::label(format!("{:?}", n_vid)),
            Node::Region(n_rgn) => dot::LabelText::label(format!("{:?}", n_rgn)),
        }
    }
    fn edge_label(&self, e: &Edge) -> dot::LabelText {
        match *e {
            Edge::Constraint(ref c) =>
                dot::LabelText::label(format!("{:?}", self.map.get(c).unwrap())),
            Edge::EnclScope(..) => dot::LabelText::label(format!("(enclosed)")),
        }
    }
}

fn constraint_to_nodes(c: &Constraint) -> (Node, Node) {
    match *c {
        Constraint::VarSubVar(rv_1, rv_2) =>
            (Node::RegionVid(rv_1), Node::RegionVid(rv_2)),
        Constraint::RegSubVar(r_1, rv_2) =>
            (Node::Region(*r_1), Node::RegionVid(rv_2)),
        Constraint::VarSubReg(rv_1, r_2) =>
            (Node::RegionVid(rv_1), Node::Region(*r_2)),
        Constraint::RegSubReg(r_1, r_2) =>
            (Node::Region(*r_1), Node::Region(*r_2)),
    }
}

fn edge_to_nodes(e: &Edge) -> (Node, Node) {
    match *e {
        Edge::Constraint(ref c) => constraint_to_nodes(c),
        Edge::EnclScope(sub, sup) => {
            (Node::Region(ty::ReScope(sub)),
             Node::Region(ty::ReScope(sup)))
        }
    }
}

impl<'a, 'gcx, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
    type Node = Node;
    type Edge = Edge<'tcx>;
    fn nodes(&self) -> dot::Nodes<Node> {
        let mut set = FxHashSet();
        for node in self.node_ids.keys() {
            set.insert(*node);
        }
        debug!("constraint graph has {} nodes", set.len());
        set.into_iter().collect()
    }
    fn edges(&self) -> dot::Edges<Edge<'tcx>> {
        debug!("constraint graph has {} edges", self.map.len());
        let mut v: Vec<_> = self.map.keys().map(|e| Edge::Constraint(*e)).collect();
        self.region_rels.region_scope_tree.each_encl_scope(|sub, sup| {
            v.push(Edge::EnclScope(sub, sup))
        });
        debug!("region graph has {} edges", v.len());
        Cow::Owned(v)
    }
    fn source(&self, edge: &Edge<'tcx>) -> Node {
        let (n1, _) = edge_to_nodes(edge);
        debug!("edge {:?} has source {:?}", edge, n1);
        n1
    }
    fn target(&self, edge: &Edge<'tcx>) -> Node {
        let (_, n2) = edge_to_nodes(edge);
        debug!("edge {:?} has target {:?}", edge, n2);
        n2
    }
}

pub type ConstraintMap<'tcx> = BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>;

fn dump_region_data_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>,
                                       map: &ConstraintMap<'tcx>,
                                       path: &str)
                                       -> io::Result<()> {
    debug!("dump_region_data map (len: {}) path: {}",
           map.len(),
           path);
    let g = ConstraintGraph::new(format!("region_data"), region_rels, map);
    debug!("dump_region_data calling render");
    let mut v = Vec::new();
    dot::render(&g, &mut v).unwrap();
    File::create(path).and_then(|mut f| f.write_all(&v))
}
<|endoftext|>


<commit_before>extern crate llvm_tools;
extern crate target_build_utils;
extern crate walkdir;

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

use target_build_utils::TargetInfo;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	let target = TargetInfo::new().expect("Could not get target info");
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	if target.target_arch() == "x86_64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("x86_64-unknown-hermit-kernel");
	} else if target.target_arch() == "aarch64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("aarch64-unknown-hermit");
	} else {
		panic!("Try to build for an unsupported platform");
	}

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

	if profile == "release" {
		cmd.arg("--release");
	}

	#[cfg(feature = "instrument")]
	cmd.env("RUSTFLAGS", "-Z instrument-mcount");
	// if instrument is not set, ensure that instrument is not in environment variables!
	#[cfg(not(feature = "instrument"))]
	cmd.env(
		"RUSTFLAGS",
		env::var("RUSTFLAGS")
			.unwrap_or_else(|_| "".into())
			.replace("-Z instrument-mcount", ""),
	);

	let output = cmd.output().expect("Unable to build kernel");
	let stdout = std::string::String::from_utf8(output.stdout);
	let stderr = std::string::String::from_utf8(output.stderr);

	println!("Build libhermit-rs output-status: {}", output.status);
	println!("Build libhermit-rs output-stdout: {}", stdout.unwrap());
	println!("Build libhermit-rs output-stderr: {}", stderr.unwrap());
	assert!(output.status.success());

	let lib_location = if target.target_arch() == "x86_64" {
		target_dir
			.join("x86_64-unknown-hermit-kernel")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else if target.target_arch() == "aarch64" {
		target_dir
			.join("aarch64-unknown-hermit")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else {
		panic!("Try to build for an unsupported platform");
	};
	println!("Lib location: {}", lib_location.display());

	// get access to llvm tools shipped in the llvm-tools-preview rustup component
	let llvm_tools = match llvm_tools::LlvmTools::new() {
		Ok(tools) => tools,
		Err(llvm_tools::Error::NotFound) => {
			eprintln!("Error: llvm-tools not found");
			eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?");
			eprintln!("  Install it through: `rustup component add llvm-tools-preview`");
			process::exit(1);
		}
		Err(err) => {
			eprintln!("Failed to retrieve llvm-tools component: {:?}", err);
			process::exit(1);
		}
	};

	// determine llvm_objcopy
	let llvm_objcopy = llvm_tools
		.tool(&llvm_tools::exe("llvm-objcopy"))
		.expect("llvm_objcopy not found in llvm-tools");

	// rename symbols
	let mut cmd = Command::new(llvm_objcopy);
	cmd.arg("--redefine-sym")
		.arg("memcpy=kernel_memcpy")
		.arg("--redefine-sym")
		.arg("memmove=kernel_memmove")
		.arg("--redefine-sym")
		.arg("memset=kernel_memset")
		.arg("--redefine-sym")
		.arg("memcmp=kernel_memcmp")
		.arg("--redefine-sym")
		.arg("bcmp=kernel_bcmp")
		.arg(lib_location.display().to_string() + "/libhermit.a");

	println!("cmd {:?}", cmd);
	let output = cmd.output().expect("Unable to rename symbols");
	let stdout = std::string::String::from_utf8(output.stdout);
	let stderr = std::string::String::from_utf8(output.stderr);
	println!("Rename symbols output-status: {}", output.status);
	println!("Rename symbols output-stdout: {}", stdout.unwrap());
	println!("Rename symbols output-stderr: {}", stderr.unwrap());
	assert!(output.status.success());

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	//HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	// Doesn't actually rebuild atm - see: https://github.com/rust-lang/cargo/issues/8306
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	let _output = Command::new("cargo")
		.current_dir(out_dir)
		.arg("download")
		.arg("--output")
		.arg(src_dir.clone().into_os_string())
		.arg("--extract")
		.arg("rusty-hermit")
		.output()
		.expect("Unable to download rusty-hermit. Please install `cargo-download`.");

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.clone().join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(|e| is_not_ignored(e))
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<commit_msg>rename symbols only in the debug version of libhermit<commit_after>extern crate llvm_tools;
extern crate target_build_utils;
extern crate walkdir;

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

use target_build_utils::TargetInfo;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	let target = TargetInfo::new().expect("Could not get target info");
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	if target.target_arch() == "x86_64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("x86_64-unknown-hermit-kernel");
	} else if target.target_arch() == "aarch64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("aarch64-unknown-hermit");
	} else {
		panic!("Try to build for an unsupported platform");
	}

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

	if profile == "release" {
		cmd.arg("--release");
	}

	#[cfg(feature = "instrument")]
	cmd.env("RUSTFLAGS", "-Z instrument-mcount");
	// if instrument is not set, ensure that instrument is not in environment variables!
	#[cfg(not(feature = "instrument"))]
	cmd.env(
		"RUSTFLAGS",
		env::var("RUSTFLAGS")
			.unwrap_or_else(|_| "".into())
			.replace("-Z instrument-mcount", ""),
	);

	let output = cmd.output().expect("Unable to build kernel");
	let stdout = std::string::String::from_utf8(output.stdout);
	let stderr = std::string::String::from_utf8(output.stderr);

	println!("Build libhermit-rs output-status: {}", output.status);
	println!("Build libhermit-rs output-stdout: {}", stdout.unwrap());
	println!("Build libhermit-rs output-stderr: {}", stderr.unwrap());
	assert!(output.status.success());

	let lib_location = if target.target_arch() == "x86_64" {
		target_dir
			.join("x86_64-unknown-hermit-kernel")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else if target.target_arch() == "aarch64" {
		target_dir
			.join("aarch64-unknown-hermit")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else {
		panic!("Try to build for an unsupported platform");
	};
	println!("Lib location: {}", lib_location.display());

	if profile == "debug" {
		// get access to llvm tools shipped in the llvm-tools-preview rustup component
		let llvm_tools = match llvm_tools::LlvmTools::new() {
			Ok(tools) => tools,
			Err(llvm_tools::Error::NotFound) => {
				eprintln!("Error: llvm-tools not found");
				eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?");
				eprintln!("  Install it through: `rustup component add llvm-tools-preview`");
				process::exit(1);
			}
			Err(err) => {
				eprintln!("Failed to retrieve llvm-tools component: {:?}", err);
				process::exit(1);
			}
		};

		// determine llvm_objcopy
		let llvm_objcopy = llvm_tools
			.tool(&llvm_tools::exe("llvm-objcopy"))
			.expect("llvm_objcopy not found in llvm-tools");

		// rename symbols
		let mut cmd = Command::new(llvm_objcopy);
		cmd.arg("--redefine-sym")
			.arg("memcpy=kernel_memcpy")
			.arg("--redefine-sym")
			.arg("memmove=kernel_memmove")
			.arg("--redefine-sym")
			.arg("memset=kernel_memset")
			.arg("--redefine-sym")
			.arg("memcmp=kernel_memcmp")
			.arg("--redefine-sym")
			.arg("bcmp=kernel_bcmp")
			.arg(lib_location.display().to_string() + "/libhermit.a");

		println!("cmd {:?}", cmd);
		let output = cmd.output().expect("Unable to rename symbols");
		let stdout = std::string::String::from_utf8(output.stdout);
		let stderr = std::string::String::from_utf8(output.stderr);
		println!("Rename symbols output-status: {}", output.status);
		println!("Rename symbols output-stdout: {}", stdout.unwrap());
		println!("Rename symbols output-stderr: {}", stderr.unwrap());
		assert!(output.status.success());
	}

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	//HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	// Doesn't actually rebuild atm - see: https://github.com/rust-lang/cargo/issues/8306
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	let _output = Command::new("cargo")
		.current_dir(out_dir)
		.arg("download")
		.arg("--output")
		.arg(src_dir.clone().into_os_string())
		.arg("--extract")
		.arg("rusty-hermit")
		.output()
		.expect("Unable to download rusty-hermit. Please install `cargo-download`.");

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.clone().join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(|e| is_not_ignored(e))
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/Synchronous_concurrency
// not_tested
//
// Reader unit reads lines from input.txt, send lines one at a time to printer
// unit, which then prints lines, keeping track of lines printed. At the end of
// the file, the reader unit requests number of lines printed from the printer
// unit, and then prints them.

use std::io::File;
use std::io::BufferedReader;
use std::comm::{DuplexStream, duplex};

static FILENAME: &'static str = "resources/input.txt";

enum Message {
    Line(String),
    End
}

fn printer(chan: DuplexStream<int, Message>) {
    let mut count = 0;
    loop {
        match chan.recv() {
            Line(line) => {
                print!("{}", line);
                count += 1;
            }
            End => {break;}
        }
    }
    chan.send(count);
}

fn reader(chan: DuplexStream<Message, int>) {
    let mut file = BufferedReader::new(File::open(&Path::new(FILENAME)));
    for line in file.lines() {
        chan.send(Line(line.unwrap()));
    }
    chan.send(End);
    println!("Total Lines: {}", chan.recv());
}

fn main() {
    let (to_reader, to_printer) = duplex();
    spawn(proc() printer(to_reader));
    spawn(proc() reader(to_printer));
}
<commit_msg>removed deprecated Duplex from synchronous concurrency<commit_after>// http://rosettacode.org/wiki/Synchronous_concurrency
// not_tested
//
// Reader unit reads lines from input.txt, send lines one at a time to printer
// unit, which then prints lines, keeping track of lines printed. At the end of
// the file, the reader unit requests number of lines printed from the printer
// unit, and then prints them.

use std::io::File;
use std::io::BufferedReader;
use std::comm::{channel, Sender, Receiver};

static FILENAME: &'static str = "resources/input.txt";

enum Message {
    Line(String),
    End
}

fn printer(i_snd: Sender<int>, msg_rcv: Receiver<Message>) {
    let mut count = 0;
    loop {
        match msg_rcv.recv() {
            Line(line) => {
                print!("{}", line);
                count += 1;
            }
            End => {break;}
        }
    }
    i_snd.send(count);
}

fn reader(msg_snd: Sender<Message>, i_rcv: Receiver<int>) {
    let mut file = BufferedReader::new(File::open(&Path::new(FILENAME)));
    for line in file.lines() {
        msg_snd.send(Line(line.unwrap()));
    }
    msg_snd.send(End);
    println!("Total Lines: {}", i_rcv.recv());
}

fn main() {
    let (msg_snd, msg_rcv) = channel();
    let (i_snd, i_rcv) = channel();

    spawn(proc() printer(i_snd, msg_rcv));
    spawn(proc() reader(msg_snd, i_rcv));
}
<|endoftext|>