<commit_before>#![cfg_attr(not(feature = "net"), allow(dead_code))]

use crate::loom::sync::atomic::{AtomicU64, Ordering::Relaxed};

#[derive(Default)]
pub(crate) struct IoDriverMetrics {
    pub(super) fd_registered_count: AtomicU64,
    pub(super) fd_deregistered_count: AtomicU64,
    pub(super) ready_count: AtomicU64,
}

impl IoDriverMetrics {
    pub(crate) fn incr_fd_count(&self) {
        let prev = self.fd_registered_count.load(Relaxed);
        let new = prev.wrapping_add(1);
        self.fd_registered_count.store(new, Relaxed);
    }

    pub(crate) fn dec_fd_count(&self) {
        let prev = self.fd_deregistered_count.load(Relaxed);
        let new = prev.wrapping_add(1);
        self.fd_deregistered_count.store(new, Relaxed);
    }

    pub(crate) fn incr_ready_count_by(&self, amt: u64) {
        let prev = self.ready_count.load(Relaxed);
        let new = prev.wrapping_add(amt);
        self.ready_count.store(new, Relaxed);
    }
}
<commit_msg>metrics: correctly update atomics in IoDriverMetrics (#4725)<commit_after>#![cfg_attr(not(feature = "net"), allow(dead_code))]

use crate::loom::sync::atomic::{AtomicU64, Ordering::Relaxed};

#[derive(Default)]
pub(crate) struct IoDriverMetrics {
    pub(super) fd_registered_count: AtomicU64,
    pub(super) fd_deregistered_count: AtomicU64,
    pub(super) ready_count: AtomicU64,
}

impl IoDriverMetrics {
    pub(crate) fn incr_fd_count(&self) {
        self.fd_registered_count.fetch_add(1, Relaxed);
    }

    pub(crate) fn dec_fd_count(&self) {
        self.fd_deregistered_count.fetch_add(1, Relaxed);
    }

    pub(crate) fn incr_ready_count_by(&self, amt: u64) {
        self.ready_count.fetch_add(amt, Relaxed);
    }
}
<|endoftext|>


<commit_before>extern crate polydraw;
extern crate toml;

use polydraw::{Renderer, Application, Frame};
use polydraw::devel::{Scene, Poly, DevelRenderer};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

const NODE_DEFS: &'static str = r#"

   [frame]
   type = "frame"

   [poly-points]
   type = "[(i64, i64)]"
   value = [ [0, 0], [90, 1200], [261, 1735], [1443, 410] ]

   [translate-point]
   type = "pair"
   in-1 = "frame"
   in-2 = { type = "i64", value = 0 }

   [add-operator]
   type = "add"
   in-1 = "poly-points"
   in-2 = "translate-point"

   [poly]
   type = "poly"
   in-1 = "add-operator"
   in-2 = { type = "(u8, u8, u8)", value = [0, 127, 255] }

   [layer]
   type = "layer"
   in-1 = "poly"

   [doc]
   type = "doc"
   in-1 = "layer"

"#;

type I64I64 = (i64, i64);
type VVI64I64 = Vec<Vec<I64I64>>;

#[derive(Debug)]
#[allow(dead_code)]
enum Data {
   None,
   I64(i64),
   F64(f64),
   I64I64(I64I64),
   VVI64I64(VVI64I64),
}

const NONE: Data = Data::None;

trait Node {
   fn new(init1: Data, init2: Data, init3: Data, init4: Data) -> Self;

   fn process(&self, arg1: &Data, arg2: &Data, arg3: &Data, arg4: &Data) -> Data;
}

struct AddNode {
   init1: Data,
   init2: Data,
}

impl Node for AddNode {
   #[inline]
   fn new(init1: Data, init2: Data, _: Data, _: Data) -> Self {
      AddNode {
         init1: init1,
         init2: init2,
      }
   }

   #[inline]
   fn process(&self, arg1: &Data, arg2: &Data, _: &Data, _: &Data) -> Data {
      let in1 = actual(arg1, &self.init1);
      let in2 = actual(arg2, &self.init2);

      match (in1, in2) {
         (&Data::I64(ref v1), &Data::I64(ref v2)) => <(i64, i64)>::add(v1, v2),

         (&Data::F64(ref v1), &Data::I64(ref v2)) => <(f64, i64)>::add(v1, v2),
         (&Data::I64(ref v1), &Data::F64(ref v2)) => <(f64, i64)>::add(v2, v1),

         (&Data::I64I64(ref v1), &Data::I64(ref v2)) => <(I64I64, i64)>::add(v1, v2),
         (&Data::I64(ref v1), &Data::I64I64(ref v2)) => <(I64I64, i64)>::add(v2, v1),

         (&Data::VVI64I64(ref v1), &Data::I64I64(ref v2)) => <(VVI64I64, I64I64)>::add(v1, v2),
         (&Data::I64I64(ref v1), &Data::VVI64I64(ref v2)) => <(VVI64I64, I64I64)>::add(v2, v1),

         _ => NONE
      }
   }
}

trait Add<T1, T2> {
   fn add(v1: &T1, v2: &T2) -> Data;
}

impl Add<i64, i64> for (i64, i64) {
   #[inline]
   fn add(v1: &i64, v2: &i64) -> Data {
      Data::I64(*v1 + *v2)
   }
}

impl Add<f64, i64> for (f64, i64) {
   #[inline]
   fn add(v1: &f64, v2: &i64) -> Data {
      Data::F64(*v1 + *v2 as f64)
   }
}

impl Add<I64I64, i64> for (I64I64, i64) {
   #[inline]
   fn add(v1: &I64I64, v2: &i64) -> Data {
      Data::I64I64((v1.0 + *v2, v1.1 + *v2))
   }
}

impl Add<VVI64I64, I64I64> for (VVI64I64, I64I64) {
   #[inline]
   fn add(v1: &VVI64I64, v2: &I64I64) -> Data {
      let mut outer = Vec::with_capacity(v1.len());

      for src in v1 {
         let mut inner = Vec::with_capacity(src.len());

         for tuple in src {
            inner.push((tuple.0 + v2.0, tuple.1 + v2.1));
         }

         outer.push(inner);
      }

      Data::VVI64I64(outer)
   }
}

#[inline]
fn actual<'a>(passed: &'a Data, initial: &'a Data) -> &'a Data {
   match passed {
      &Data::None => initial,
      _ => passed
   }
}

#[inline]
fn _poly_from_data(data: &VVI64I64) -> Poly {
   let outer = _points_from_coords(&data[0]);

   let mut inner = Vec::new();

   for inner_data in &data[1..] {
      inner.push(
         _points_from_coords(inner_data)
      );
   }

   let poly = Poly::new_with_holes(
      outer, inner, RGB::new(81, 180, 200),
   );

   poly
}

#[inline]
fn _points_from_coords(coords: &[(i64, i64)]) -> Vec<Point> {
   let mut points = Vec::new();

   for &(x, y) in coords.iter() {
      points.push(Point::new(x + 120, y + 120))
   }

   points
}

struct NodeRenderer {
   renderer: DevelRenderer,
   frame: i64,
}

impl NodeRenderer {
   #[inline]
   pub fn new() -> Self {
      NodeRenderer {
         renderer: DevelRenderer::new(Scene::new()),
         frame: 0,
      }
   }

   #[inline]
   pub fn parse_nodes(&mut self) {
      let mut parser = toml::Parser::new(NODE_DEFS);

      match parser.parse() {
          Some(value) => println!("toml parsed: {:?}", value),
          None => {
              println!("parse errors: {:?}", parser.errors);
          }
      }
   }
}

impl Renderer for NodeRenderer {
   #[inline]
   fn init(&mut self, width: u32, height: u32) {
      self.parse_nodes();

      self.renderer.init(width, height);
   }

   #[inline]
   fn render(&mut self, frame: &mut Frame) {
      let mut scene = Scene::new();

      let source = vec![vec![
         (90, 1200),
         (261, 1735),
         (1443, 410),
         (493, 174),
      ]];

      let add = AddNode::new(NONE, NONE, NONE, NONE);

      let destination = add.process(
         &Data::VVI64I64(source), &Data::I64I64((self.frame, 0)), &NONE, &NONE
      );

      match destination {
         Data::VVI64I64(data) => scene.push(_poly_from_data(&data)),
         _ => {}
      }

      self.renderer.set_scene(scene);

      self.renderer.render(frame);

      self.frame += 1;
   }
}

fn main() {
   let mut renderer = NodeRenderer::new();

   Application::new()
      .renderer(&mut renderer)
      .title("Nodes")
      .size(1200, 800)
      .run();
}

<commit_msg>Move the method `new` out of the Node trait<commit_after>extern crate polydraw;
extern crate toml;

use polydraw::{Renderer, Application, Frame};
use polydraw::devel::{Scene, Poly, DevelRenderer};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

const NODE_DEFS: &'static str = r#"

   [frame]
   type = "frame"

   [poly-points]
   type = "[(i64, i64)]"
   value = [ [0, 0], [90, 1200], [261, 1735], [1443, 410] ]

   [translate-point]
   type = "pair"
   in-1 = "frame"
   in-2 = { type = "i64", value = 0 }

   [add-operator]
   type = "add"
   in-1 = "poly-points"
   in-2 = "translate-point"

   [poly]
   type = "poly"
   in-1 = "add-operator"
   in-2 = { type = "(u8, u8, u8)", value = [0, 127, 255] }

   [layer]
   type = "layer"
   in-1 = "poly"

   [doc]
   type = "doc"
   in-1 = "layer"

"#;

type I64I64 = (i64, i64);
type U8U8U8 = (u8, u8, u8);
type VVI64I64 = Vec<Vec<I64I64>>;

#[derive(Debug)]
#[allow(dead_code)]
enum Data {
   None,
   I64(i64),
   F64(f64),
   I64I64(I64I64),
   U8U8U8(U8U8U8),
   VVI64I64(VVI64I64),
}

const NONE: Data = Data::None;

trait Node {
   fn process(&self, arg1: &Data, arg2: &Data, arg3: &Data, arg4: &Data) -> Data;
}

struct AddNode {
   first: Data,
   second: Data,
}

impl AddNode {
   #[inline]
   fn new(first: Data, second: Data) -> Self {
      AddNode {
         first: first,
         second: second,
      }
   }
}

impl Node for AddNode {
   #[inline]
   fn process(&self, arg1: &Data, arg2: &Data, _: &Data, _: &Data) -> Data {
      let in1 = actual(arg1, &self.first);
      let in2 = actual(arg2, &self.second);

      match (in1, in2) {
         (&Data::I64(ref v1), &Data::I64(ref v2)) => <(i64, i64)>::add(v1, v2),

         (&Data::F64(ref v1), &Data::I64(ref v2)) => <(f64, i64)>::add(v1, v2),
         (&Data::I64(ref v1), &Data::F64(ref v2)) => <(f64, i64)>::add(v2, v1),

         (&Data::I64I64(ref v1), &Data::I64(ref v2)) => <(I64I64, i64)>::add(v1, v2),
         (&Data::I64(ref v1), &Data::I64I64(ref v2)) => <(I64I64, i64)>::add(v2, v1),

         (&Data::VVI64I64(ref v1), &Data::I64I64(ref v2)) => <(VVI64I64, I64I64)>::add(v1, v2),
         (&Data::I64I64(ref v1), &Data::VVI64I64(ref v2)) => <(VVI64I64, I64I64)>::add(v2, v1),

         _ => NONE
      }
   }
}

trait Add<T1, T2> {
   fn add(v1: &T1, v2: &T2) -> Data;
}

impl Add<i64, i64> for (i64, i64) {
   #[inline]
   fn add(v1: &i64, v2: &i64) -> Data {
      Data::I64(*v1 + *v2)
   }
}

impl Add<f64, i64> for (f64, i64) {
   #[inline]
   fn add(v1: &f64, v2: &i64) -> Data {
      Data::F64(*v1 + *v2 as f64)
   }
}

impl Add<I64I64, i64> for (I64I64, i64) {
   #[inline]
   fn add(v1: &I64I64, v2: &i64) -> Data {
      Data::I64I64((v1.0 + *v2, v1.1 + *v2))
   }
}

impl Add<VVI64I64, I64I64> for (VVI64I64, I64I64) {
   #[inline]
   fn add(v1: &VVI64I64, v2: &I64I64) -> Data {
      let mut outer = Vec::with_capacity(v1.len());

      for src in v1 {
         let mut inner = Vec::with_capacity(src.len());

         for tuple in src {
            inner.push((tuple.0 + v2.0, tuple.1 + v2.1));
         }

         outer.push(inner);
      }

      Data::VVI64I64(outer)
   }
}

#[inline]
fn actual<'a>(passed: &'a Data, initial: &'a Data) -> &'a Data {
   match passed {
      &Data::None => initial,
      _ => passed
   }
}

#[inline]
fn _poly_from_data(data: &VVI64I64) -> Poly {
   let outer = _points_from_coords(&data[0]);

   let mut inner = Vec::new();

   for inner_data in &data[1..] {
      inner.push(
         _points_from_coords(inner_data)
      );
   }

   let poly = Poly::new_with_holes(
      outer, inner, RGB::new(81, 180, 200),
   );

   poly
}

#[inline]
fn _points_from_coords(coords: &[(i64, i64)]) -> Vec<Point> {
   let mut points = Vec::new();

   for &(x, y) in coords.iter() {
      points.push(Point::new(x + 120, y + 120))
   }

   points
}

struct NodeRenderer {
   renderer: DevelRenderer,
   frame: i64,
}

impl NodeRenderer {
   #[inline]
   pub fn new() -> Self {
      NodeRenderer {
         renderer: DevelRenderer::new(Scene::new()),
         frame: 0,
      }
   }

   #[inline]
   pub fn parse_nodes(&mut self) {
      let mut parser = toml::Parser::new(NODE_DEFS);

      match parser.parse() {
         Some(value) => {
            println!("toml parsed: {:?}", value);
         },
         None => {
            println!("parse errors: {:?}", parser.errors);
         }
      }
   }
}

impl Renderer for NodeRenderer {
   #[inline]
   fn init(&mut self, width: u32, height: u32) {
      self.parse_nodes();

      self.renderer.init(width, height);
   }

   #[inline]
   fn render(&mut self, frame: &mut Frame) {
      let mut scene = Scene::new();

      let source = vec![vec![
         (90, 1200),
         (261, 1735),
         (1443, 410),
         (493, 174),
      ]];

      let add = AddNode::new(NONE, NONE);

      let destination = add.process(
         &Data::VVI64I64(source), &Data::I64I64((self.frame, 0)), &NONE, &NONE
      );

      match destination {
         Data::VVI64I64(data) => scene.push(_poly_from_data(&data)),
         _ => {}
      }

      self.renderer.set_scene(scene);

      self.renderer.render(frame);

      self.frame += 1;
   }
}

fn main() {
   let mut renderer = NodeRenderer::new();

   Application::new()
      .renderer(&mut renderer)
      .title("Nodes")
      .size(1200, 800)
      .run();
}

<|endoftext|>


<commit_before>extern crate cc;
extern crate regex;

fn main() {
    build::main()
}

#[cfg(feature = "docs-only")]
mod build {
    pub fn main() {}
}

#[cfg(not(feature = "docs-only"))]
mod build {
    use regex::Regex;
    use std::env;
    use std::fs;
    use std::path::Path;
    use std::process::Command;

    pub fn main() {
        let out_dir = env::var("OUT_DIR").unwrap();
        let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();

        let out_dir = Path::new(&out_dir);
        let native_from = Path::new(&crate_dir).join("native");
        let native_to = Path::new(&out_dir).join("native");

        // 1. Copy the native runtime library into the build directory.
        copy_native_library(&native_from, &native_to);

        // 2. Build the object file from source using node-gyp.
        build_object_file(&native_to);

        // 3. Link the library from the object file using gcc.
        link_library(&native_to);

        // 4. Copy native build artifacts
        copy_build_artifacts(&native_to, &out_dir);

        // Rebuild if any of these environment variables change
        println!("cargo:rerun-if-env-changed=npm_config_target");
        println!("cargo:rerun-if-env-changed=npm_config_arch");
        println!("cargo:rerun-if-env-changed=npm_config_target_arch");
    }

    fn copy_files(dir_from: impl AsRef<Path>, dir_to: impl AsRef<Path>) {
        for entry in fs::read_dir(dir_from.as_ref()).unwrap() {
            let entry = entry.unwrap();

            if entry.file_type().unwrap().is_dir() {
                continue;
            }

            let file_name = entry.file_name();

            let from = dir_from.as_ref().join(&file_name);
            let to = dir_to.as_ref().join(&file_name);

            fs::copy(from, to).unwrap();
        }
    }

    fn copy_native_library(native_from: impl AsRef<Path>, native_to: impl AsRef<Path>) {
        let native_from = native_from.as_ref();
        let native_to = native_to.as_ref();

        let src_from = native_from.join("src");
        let src_to = native_to.join("src");

        fs::create_dir_all(&src_to).unwrap();

        copy_files(&native_from, &native_to);
        copy_files(&src_from, &src_to);
    }

    #[cfg(unix)]
    fn npm(cwd: &Path) -> Command {
        let mut cmd = Command::new("npm");
        cmd.current_dir(cwd);
        cmd
    }

    #[cfg(windows)]
    fn npm(cwd: &Path) -> Command {
        let mut cmd = Command::new("cmd.exe");
        cmd.args(&["/C", "npm"]);
        cmd.current_dir(cwd);
        cmd
    }

    // The node-gyp build output includes platform information in a string
    // that looks like:
    //
    //     gyp verb architecture ia32
    fn parse_node_arch(node_gyp_output: &str) -> String {
        let version_regex =
            Regex::new(r"gyp verb architecture (?P<arch>ia32|x64|arm|arm64)").unwrap();
        let captures = version_regex.captures(&node_gyp_output).unwrap();
        String::from(&captures["arch"])
    }

    // The node-gyp output includes the root directory of shared resources
    // for the Node installation in a string that looks like:
    //
    //     '-Dnode_root_dir=C:\\Users\\dherman\\.node-gyp\\8.3.0'
    fn parse_node_root_dir(node_gyp_output: &str) -> &str {
        let node_root_dir_flag_pattern = "'-Dnode_root_dir=";
        let node_root_dir_start_index = node_gyp_output
            .find(node_root_dir_flag_pattern)
            .map(|i| i + node_root_dir_flag_pattern.len())
            .expect("Couldn't find node_root_dir in node-gyp output.");
        let node_root_dir_end_index = node_gyp_output[node_root_dir_start_index..]
            .find('\'')
            .unwrap()
            + node_root_dir_start_index;
        &node_gyp_output[node_root_dir_start_index..node_root_dir_end_index]
    }

    // The node-gyp output includes the name of the shared Node library file.
    // In NPM versions <= v5.0.3, this was just the filename by itself, e.g.:
    //
    //     '-Dnode_lib_file=node.lib'
    //
    // In NPM versions >= v5.3.0, this was a templated absolute path with a
    // reference to a gyp variable, e.g.:
    //
    //     '-Dnode_lib_file=C:\\Users\\dherman\\.node-gyp\\8.3.0\\<(target_arch)\\node.lib'
    //
    // Either way, we simply extract the value here. The `neon-build` crate
    // processes it further.
    fn parse_node_lib_file(node_gyp_output: &str) -> &str {
        let node_lib_file_flag_pattern = "'-Dnode_lib_file=";
        let node_lib_file_start_index = node_gyp_output
            .find(node_lib_file_flag_pattern)
            .map(|i| i + node_lib_file_flag_pattern.len())
            .expect("Couldn't find node_lib_file in node-gyp output.");
        let node_lib_file_end_index = node_gyp_output[node_lib_file_start_index..]
            .find('\'')
            .unwrap()
            + node_lib_file_start_index;
        &node_gyp_output[node_lib_file_start_index..node_lib_file_end_index]
    }

    fn build_object_file(native_dir: &Path) {
        if cfg!(windows) {
            // Downcase all the npm environment variables to ensure they are read by node-gyp.
            for (key, value) in env::vars() {
                if key.starts_with("NPM_CONFIG") {
                    env::remove_var(&key);
                    env::set_var(key.to_lowercase(), value);
                }
            }
        }

        // Ensure that all package.json dependencies and dev dependencies are installed.
        npm(native_dir)
            .args(&["install", "--silent"])
            .status()
            .expect("Failed to run \"npm install\" for neon-sys!");

        // Run `node-gyp configure` in verbose mode to read node_root_dir on Windows.
        let output = npm(native_dir)
            .args(&[
                "run",
                if debug() {
                    "configure-debug"
                } else {
                    "configure-release"
                },
            ])
            .output()
            .expect("Failed to run \"node-gyp configure\" for neon-sys!");

        if !output.status.success() {
            panic!(
                "Failed to run \"node-gyp configure\" for neon-sys!\n Out: {}\n Err: {}",
                String::from_utf8_lossy(&output.stdout),
                String::from_utf8_lossy(&output.stderr)
            );
        }

        if cfg!(windows) {
            let node_gyp_output = String::from_utf8_lossy(&output.stderr);
            println!(
                "cargo:node_root_dir={}",
                parse_node_root_dir(&node_gyp_output)
            );
            println!(
                "cargo:node_lib_file={}",
                parse_node_lib_file(&node_gyp_output)
            );
        }

        // Run `node-gyp build`.
        let build_output = npm(native_dir)
            .args(&[
                "run",
                if debug() {
                    "build-debug"
                } else {
                    "build-release"
                },
            ])
            .output()
            .expect("Failed to run \"node-gyp build\" for neon-sys!");

        let node_gyp_build_output = String::from_utf8_lossy(&build_output.stderr);
        println!(
            "cargo:node_arch={}",
            parse_node_arch(&node_gyp_build_output)
        );
    }

    // Link the built object file into a static library.
    fn link_library(native_dir: &Path) {
        let configuration = if debug() { "Debug" } else { "Release" };

        let object_path = if cfg!(unix) {
            native_dir
                .join("build")
                .join(configuration)
                .join("obj.target")
                .join("neon")
                .join("src")
                .join("neon.o")
        } else {
            let neon_dir = native_dir
                .join("build")
                .join(configuration)
                .join("obj")
                .join("neon");
            let object_path = neon_dir.join("src").join("neon.obj");
            if object_path.exists() {
                object_path
            } else {
                neon_dir.join("neon.obj")
            }
        };

        cc::Build::new()
            .cpp(true)
            .object(object_path)
            .compile("libneon.a");
    }

    #[cfg(unix)]
    fn copy_build_artifacts(_native_dir: &Path, _out_dir: &Path) {}

    #[cfg(windows)]
    fn copy_build_artifacts(native_dir: &Path, out_dir: &Path) {
        let configuration = if debug() { "Debug" } else { "Release" };
        let win_delay_file = "win_delay_load_hook.obj";
        let win_delay_dest = out_dir.join(win_delay_file);
        let win_delay_source = native_dir
            .join("build")
            .join(configuration)
            .join("obj")
            .join("neon")
            .join(win_delay_file);

        // Win delay hook is only needed for electron, warn instead of crash if not found
        if let Err(err) = fs::copy(win_delay_source, win_delay_dest) {
            if err.kind() == std::io::ErrorKind::NotFound {
                eprintln!("warning: {} could not be found", win_delay_file);
            } else {
                panic!("{:?}", err);
            }
        }
    }

    fn debug() -> bool {
        match env::var("DEBUG") {
            Ok(s) => s == "true",
            Err(_) => false,
        }
    }
}
<commit_msg>Show the compilation error in the console and abort the build<commit_after>extern crate cc;
extern crate regex;

fn main() {
    build::main()
}

#[cfg(feature = "docs-only")]
mod build {
    pub fn main() {}
}

#[cfg(not(feature = "docs-only"))]
mod build {
    use regex::Regex;
    use std::env;
    use std::fs;
    use std::path::Path;
    use std::process::Command;

    pub fn main() {
        let out_dir = env::var("OUT_DIR").unwrap();
        let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();

        let out_dir = Path::new(&out_dir);
        let native_from = Path::new(&crate_dir).join("native");
        let native_to = Path::new(&out_dir).join("native");

        // 1. Copy the native runtime library into the build directory.
        copy_native_library(&native_from, &native_to);

        // 2. Build the object file from source using node-gyp.
        build_object_file(&native_to);

        // 3. Link the library from the object file using gcc.
        link_library(&native_to);

        // 4. Copy native build artifacts
        copy_build_artifacts(&native_to, &out_dir);

        // Rebuild if any of these environment variables change
        println!("cargo:rerun-if-env-changed=npm_config_target");
        println!("cargo:rerun-if-env-changed=npm_config_arch");
        println!("cargo:rerun-if-env-changed=npm_config_target_arch");
    }

    fn copy_files(dir_from: impl AsRef<Path>, dir_to: impl AsRef<Path>) {
        for entry in fs::read_dir(dir_from.as_ref()).unwrap() {
            let entry = entry.unwrap();

            if entry.file_type().unwrap().is_dir() {
                continue;
            }

            let file_name = entry.file_name();

            let from = dir_from.as_ref().join(&file_name);
            let to = dir_to.as_ref().join(&file_name);

            fs::copy(from, to).unwrap();
        }
    }

    fn copy_native_library(native_from: impl AsRef<Path>, native_to: impl AsRef<Path>) {
        let native_from = native_from.as_ref();
        let native_to = native_to.as_ref();

        let src_from = native_from.join("src");
        let src_to = native_to.join("src");

        fs::create_dir_all(&src_to).unwrap();

        copy_files(&native_from, &native_to);
        copy_files(&src_from, &src_to);
    }

    #[cfg(unix)]
    fn npm(cwd: &Path) -> Command {
        let mut cmd = Command::new("npm");
        cmd.current_dir(cwd);
        cmd
    }

    #[cfg(windows)]
    fn npm(cwd: &Path) -> Command {
        let mut cmd = Command::new("cmd.exe");
        cmd.args(&["/C", "npm"]);
        cmd.current_dir(cwd);
        cmd
    }

    // The node-gyp build output includes platform information in a string
    // that looks like:
    //
    //     gyp verb architecture ia32
    fn parse_node_arch(node_gyp_output: &str) -> String {
        let version_regex =
            Regex::new(r"gyp verb architecture (?P<arch>ia32|x64|arm|arm64)").unwrap();
        let captures = version_regex.captures(&node_gyp_output).unwrap();
        String::from(&captures["arch"])
    }

    // The node-gyp output includes the root directory of shared resources
    // for the Node installation in a string that looks like:
    //
    //     '-Dnode_root_dir=C:\\Users\\dherman\\.node-gyp\\8.3.0'
    fn parse_node_root_dir(node_gyp_output: &str) -> &str {
        let node_root_dir_flag_pattern = "'-Dnode_root_dir=";
        let node_root_dir_start_index = node_gyp_output
            .find(node_root_dir_flag_pattern)
            .map(|i| i + node_root_dir_flag_pattern.len())
            .expect("Couldn't find node_root_dir in node-gyp output.");
        let node_root_dir_end_index = node_gyp_output[node_root_dir_start_index..]
            .find('\'')
            .unwrap()
            + node_root_dir_start_index;
        &node_gyp_output[node_root_dir_start_index..node_root_dir_end_index]
    }

    // The node-gyp output includes the name of the shared Node library file.
    // In NPM versions <= v5.0.3, this was just the filename by itself, e.g.:
    //
    //     '-Dnode_lib_file=node.lib'
    //
    // In NPM versions >= v5.3.0, this was a templated absolute path with a
    // reference to a gyp variable, e.g.:
    //
    //     '-Dnode_lib_file=C:\\Users\\dherman\\.node-gyp\\8.3.0\\<(target_arch)\\node.lib'
    //
    // Either way, we simply extract the value here. The `neon-build` crate
    // processes it further.
    fn parse_node_lib_file(node_gyp_output: &str) -> &str {
        let node_lib_file_flag_pattern = "'-Dnode_lib_file=";
        let node_lib_file_start_index = node_gyp_output
            .find(node_lib_file_flag_pattern)
            .map(|i| i + node_lib_file_flag_pattern.len())
            .expect("Couldn't find node_lib_file in node-gyp output.");
        let node_lib_file_end_index = node_gyp_output[node_lib_file_start_index..]
            .find('\'')
            .unwrap()
            + node_lib_file_start_index;
        &node_gyp_output[node_lib_file_start_index..node_lib_file_end_index]
    }

    fn build_object_file(native_dir: &Path) {
        if cfg!(windows) {
            // Downcase all the npm environment variables to ensure they are read by node-gyp.
            for (key, value) in env::vars() {
                if key.starts_with("NPM_CONFIG") {
                    env::remove_var(&key);
                    env::set_var(key.to_lowercase(), value);
                }
            }
        }

        // Ensure that all package.json dependencies and dev dependencies are installed.
        npm(native_dir)
            .args(&["install", "--silent"])
            .status()
            .expect("Failed to run \"npm install\" for neon-sys!");

        // Run `node-gyp configure` in verbose mode to read node_root_dir on Windows.
        let output = npm(native_dir)
            .args(&[
                "run",
                if debug() {
                    "configure-debug"
                } else {
                    "configure-release"
                },
            ])
            .output()
            .expect("Failed to run \"node-gyp configure\" for neon-sys!");

        if !output.status.success() {
            panic!(
                "Failed to run \"node-gyp configure\" for neon-sys!\n Out: {}\n Err: {}",
                String::from_utf8_lossy(&output.stdout),
                String::from_utf8_lossy(&output.stderr)
            );
        }

        if cfg!(windows) {
            let node_gyp_output = String::from_utf8_lossy(&output.stderr);
            println!(
                "cargo:node_root_dir={}",
                parse_node_root_dir(&node_gyp_output)
            );
            println!(
                "cargo:node_lib_file={}",
                parse_node_lib_file(&node_gyp_output)
            );
        }

        // Run `node-gyp build`.
        let build_output = npm(native_dir)
            .args(&[
                "run",
                if debug() {
                    "build-debug"
                } else {
                    "build-release"
                },
            ])
            .output()
            .expect("Failed to run \"node-gyp build\" for neon-sys!");

        if !build_output.status.success() {
            panic!(
                "Failed to run \"node-gyp build\" for neon-sys!\n Out: {}\n Err: {}",
                String::from_utf8_lossy(&build_output.stdout),
                String::from_utf8_lossy(&build_output.stderr)
            );
        }

        let node_gyp_build_output = String::from_utf8_lossy(&build_output.stderr);
        println!(
            "cargo:node_arch={}",
            parse_node_arch(&node_gyp_build_output)
        );
    }

    // Link the built object file into a static library.
    fn link_library(native_dir: &Path) {
        let configuration = if debug() { "Debug" } else { "Release" };

        let object_path = if cfg!(unix) {
            native_dir
                .join("build")
                .join(configuration)
                .join("obj.target")
                .join("neon")
                .join("src")
                .join("neon.o")
        } else {
            let neon_dir = native_dir
                .join("build")
                .join(configuration)
                .join("obj")
                .join("neon");
            let object_path = neon_dir.join("src").join("neon.obj");
            if object_path.exists() {
                object_path
            } else {
                neon_dir.join("neon.obj")
            }
        };

        cc::Build::new()
            .cpp(true)
            .object(object_path)
            .compile("libneon.a");
    }

    #[cfg(unix)]
    fn copy_build_artifacts(_native_dir: &Path, _out_dir: &Path) {}

    #[cfg(windows)]
    fn copy_build_artifacts(native_dir: &Path, out_dir: &Path) {
        let configuration = if debug() { "Debug" } else { "Release" };
        let win_delay_file = "win_delay_load_hook.obj";
        let win_delay_dest = out_dir.join(win_delay_file);
        let win_delay_source = native_dir
            .join("build")
            .join(configuration)
            .join("obj")
            .join("neon")
            .join(win_delay_file);

        // Win delay hook is only needed for electron, warn instead of crash if not found
        if let Err(err) = fs::copy(win_delay_source, win_delay_dest) {
            if err.kind() == std::io::ErrorKind::NotFound {
                eprintln!("warning: {} could not be found", win_delay_file);
            } else {
                panic!("{:?}", err);
            }
        }
    }

    fn debug() -> bool {
        match env::var("DEBUG") {
            Ok(s) => s == "true",
            Err(_) => false,
        }
    }
}
<|endoftext|>


<commit_before>use anyhow::{Error, Result};
use std::env;
use std::path::Path;
use std::process::Command;

/// Return a `String` to use for the given executable.
///
/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that
/// gives a valid Cargo executable; or it may return a full path to a valid
/// Cargo.
pub fn get_path_for_executable(executable_name: impl AsRef<str>) -> Result<String> {
    // The current implementation checks three places for an executable to use:
    // 1) Appropriate environment variable (erroring if this is set but not a usable executable)
    //      example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc
    // 2) `<executable_name>`
    //      example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH
    // 3) `~/.cargo/bin/<executable_name>`
    //      example: for cargo, this tries ~/.cargo/bin/cargo
    //      It seems that this is a reasonable place to try for cargo, rustc, and rustup
    let executable_name = executable_name.as_ref();
    let env_var = executable_name.to_ascii_uppercase();
    if let Ok(path) = env::var(&env_var) {
        if is_valid_executable(&path) {
            Ok(path)
        } else {
            Err(Error::msg(format!("`{}` environment variable points to something that's not a valid executable", env_var)))
        }
    } else {
        let final_path: Option<String> = if is_valid_executable(executable_name) {
            Some(executable_name.to_owned())
        } else {
            if let Some(mut path) = dirs::home_dir() {
                path.push(".cargo");
                path.push("bin");
                path.push(executable_name);
                if is_valid_executable(&path) {
                    Some(path.into_os_string().into_string().expect("Invalid Unicode in path"))
                } else {
                    None
                }
            } else {
                None
            }
        };
        final_path.ok_or(
            // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly
            // for VSCode, even if they are set correctly in a terminal.
            // On macOS in particular, launching VSCode from terminal with `code <dirname>` causes VSCode
            // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal;
            // but launching VSCode from Dock does not inherit environment variables from a terminal.
            // For more discussion, see #3118.
            Error::msg(format!("Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", executable_name, executable_name, env_var))
        )
    }
}

/// Does the given `Path` point to a usable executable?
///
/// (assumes the executable takes a `--version` switch and writes to stdout,
/// which is true for `cargo`, `rustc`, and `rustup`)
fn is_valid_executable(p: impl AsRef<Path>) -> bool {
    Command::new(p.as_ref()).arg("--version").output().is_ok()
}
<commit_msg>cargo fmt<commit_after>use anyhow::{Error, Result};
use std::env;
use std::path::Path;
use std::process::Command;

/// Return a `String` to use for the given executable.
///
/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that
/// gives a valid Cargo executable; or it may return a full path to a valid
/// Cargo.
pub fn get_path_for_executable(executable_name: impl AsRef<str>) -> Result<String> {
    // The current implementation checks three places for an executable to use:
    // 1) Appropriate environment variable (erroring if this is set but not a usable executable)
    //      example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc
    // 2) `<executable_name>`
    //      example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH
    // 3) `~/.cargo/bin/<executable_name>`
    //      example: for cargo, this tries ~/.cargo/bin/cargo
    //      It seems that this is a reasonable place to try for cargo, rustc, and rustup
    let executable_name = executable_name.as_ref();
    let env_var = executable_name.to_ascii_uppercase();
    if let Ok(path) = env::var(&env_var) {
        if is_valid_executable(&path) {
            Ok(path)
        } else {
            Err(Error::msg(format!(
                "`{}` environment variable points to something that's not a valid executable",
                env_var
            )))
        }
    } else {
        let final_path: Option<String> = if is_valid_executable(executable_name) {
            Some(executable_name.to_owned())
        } else {
            if let Some(mut path) = dirs::home_dir() {
                path.push(".cargo");
                path.push("bin");
                path.push(executable_name);
                if is_valid_executable(&path) {
                    Some(path.into_os_string().into_string().expect("Invalid Unicode in path"))
                } else {
                    None
                }
            } else {
                None
            }
        };
        final_path.ok_or(
            // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly
            // for VSCode, even if they are set correctly in a terminal.
            // On macOS in particular, launching VSCode from terminal with `code <dirname>` causes VSCode
            // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal;
            // but launching VSCode from Dock does not inherit environment variables from a terminal.
            // For more discussion, see #3118.
            Error::msg(format!("Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", executable_name, executable_name, env_var))
        )
    }
}

/// Does the given `Path` point to a usable executable?
///
/// (assumes the executable takes a `--version` switch and writes to stdout,
/// which is true for `cargo`, `rustc`, and `rustup`)
fn is_valid_executable(p: impl AsRef<Path>) -> bool {
    Command::new(p.as_ref()).arg("--version").output().is_ok()
}
<|endoftext|>


<commit_before>//! Object safe interface for file watching and reading.
use std::fmt;

use paths::{AbsPath, AbsPathBuf};

/// A set of files on the file system.
#[derive(Debug, Clone)]
pub enum Entry {
    /// The `Entry` is represented by a raw set of files.
    Files(Vec<AbsPathBuf>),
    /// The `Entry` is represented by `Directories`.
    Directories(Directories),
}

/// Specifies a set of files on the file system.
///
/// A file is included if:
///   * it has included extension
///   * it is under an `include` path
///   * it is not under `exclude` path
///
/// If many include/exclude paths match, the longest one wins.
///
/// If a path is in both `include` and `exclude`, the `exclude` one wins.
#[derive(Debug, Clone, Default)]
pub struct Directories {
    pub extensions: Vec<String>,
    pub include: Vec<AbsPathBuf>,
    pub exclude: Vec<AbsPathBuf>,
}

/// [`Handle`]'s configuration.
#[derive(Debug)]
pub struct Config {
    /// Version number to associate progress updates to the right config
    /// version.
    pub version: u32,
    /// Set of initially loaded files.
    pub load: Vec<Entry>,
    /// Index of watched entries in `load`.
    ///
    /// If a path in a watched entry is modified,the [`Handle`] should notify it.
    pub watch: Vec<usize>,
}

/// Message about an action taken by a [`Handle`].
pub enum Message {
    /// Indicate a gradual progress.
    ///
    /// This is supposed to be the number of loaded files.
    Progress { n_total: usize, n_done: usize, config_version: u32 },
    /// The handle loaded the following files' content.
    Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },
}

/// Type that will receive [`Messages`](Message) from a [`Handle`].
pub type Sender = Box<dyn Fn(Message) + Send>;

/// Interface for reading and watching files.
pub trait Handle: fmt::Debug {
    /// Spawn a new handle with the given `sender`.
    fn spawn(sender: Sender) -> Self
    where
        Self: Sized;

    /// Set this handle's configuration.
    fn set_config(&mut self, config: Config);

    /// The file's content at `path` has been modified, and should be reloaded.
    fn invalidate(&mut self, path: AbsPathBuf);

    /// Load the content of the given file, returning [`None`] if it does not
    /// exists.
    fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>;
}

impl Entry {
    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git],
    /// })
    /// ```
    pub fn rs_files_recursively(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git"]))
    }

    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git, base/target],
    /// })
    /// ```
    pub fn local_cargo_package(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git", "target"]))
    }

    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git, /tests, /examples, /benches],
    /// })
    /// ```
    pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"]))
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// See [`Directories::contains_file`].
    pub fn contains_file(&self, path: &AbsPath) -> bool {
        match self {
            Entry::Files(files) => files.iter().any(|it| it == path),
            Entry::Directories(dirs) => dirs.contains_file(path),
        }
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// - If `self` is `Entry::Files`, returns `false`
    /// - Else, see [`Directories::contains_dir`].
    pub fn contains_dir(&self, path: &AbsPath) -> bool {
        match self {
            Entry::Files(_) => false,
            Entry::Directories(dirs) => dirs.contains_dir(path),
        }
    }
}

impl Directories {
    /// Returns `true` if `path` is included in `self`.
    pub fn contains_file(&self, path: &AbsPath) -> bool {
        // First, check the file extension...
        let ext = path.extension().unwrap_or_default();
        if self.extensions.iter().all(|it| it.as_str() != ext) {
            return false;
        }

        // Then, check for path inclusion...
        self.includes_path(path)
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// Since `path` is supposed to be a directory, this will not take extension
    /// into account.
    pub fn contains_dir(&self, path: &AbsPath) -> bool {
        self.includes_path(path)
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// It is included if
    ///   - An element in `self.include` is a prefix of `path`.
    ///   - This path is longer than any element in `self.exclude` that is a prefix
    ///     of `path`. In case of equality, exclusion wins.
    fn includes_path(&self, path: &AbsPath) -> bool {
        let include = self.include.iter().fold(None::<&AbsPathBuf>, |include, incl| {
            if !path.starts_with(incl) {
                return include;
            }

            Some(match include {
                Some(prev) if prev.starts_with(incl) => prev,
                _ => incl,
            })
        });

        let include = match include {
            Some(it) => it,
            None => return false,
        };

        !self.exclude.iter().any(|excl| path.starts_with(excl) && excl.starts_with(include))
    }
}

/// Returns :
/// ```text
/// Directories {
///     extensions: ["rs"],
///     include: [base],
///     exclude: [base/<exclude>],
/// }
/// ```
fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories {
    let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>();
    Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude }
}

impl fmt::Debug for Message {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Message::Loaded { files } => {
                f.debug_struct("Loaded").field("n_files", &files.len()).finish()
            }
            Message::Progress { n_total, n_done, config_version } => f
                .debug_struct("Progress")
                .field("n_total", n_total)
                .field("n_done", n_done)
                .field("config_version", config_version)
                .finish(),
        }
    }
}

#[test]
fn handle_is_object_safe() {
    fn _assert(_: &dyn Handle) {}
}
<commit_msg>Fix: prefer the usage of `for` loops over `fold`<commit_after>//! Object safe interface for file watching and reading.
use std::fmt;

use paths::{AbsPath, AbsPathBuf};

/// A set of files on the file system.
#[derive(Debug, Clone)]
pub enum Entry {
    /// The `Entry` is represented by a raw set of files.
    Files(Vec<AbsPathBuf>),
    /// The `Entry` is represented by `Directories`.
    Directories(Directories),
}

/// Specifies a set of files on the file system.
///
/// A file is included if:
///   * it has included extension
///   * it is under an `include` path
///   * it is not under `exclude` path
///
/// If many include/exclude paths match, the longest one wins.
///
/// If a path is in both `include` and `exclude`, the `exclude` one wins.
#[derive(Debug, Clone, Default)]
pub struct Directories {
    pub extensions: Vec<String>,
    pub include: Vec<AbsPathBuf>,
    pub exclude: Vec<AbsPathBuf>,
}

/// [`Handle`]'s configuration.
#[derive(Debug)]
pub struct Config {
    /// Version number to associate progress updates to the right config
    /// version.
    pub version: u32,
    /// Set of initially loaded files.
    pub load: Vec<Entry>,
    /// Index of watched entries in `load`.
    ///
    /// If a path in a watched entry is modified,the [`Handle`] should notify it.
    pub watch: Vec<usize>,
}

/// Message about an action taken by a [`Handle`].
pub enum Message {
    /// Indicate a gradual progress.
    ///
    /// This is supposed to be the number of loaded files.
    Progress { n_total: usize, n_done: usize, config_version: u32 },
    /// The handle loaded the following files' content.
    Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },
}

/// Type that will receive [`Messages`](Message) from a [`Handle`].
pub type Sender = Box<dyn Fn(Message) + Send>;

/// Interface for reading and watching files.
pub trait Handle: fmt::Debug {
    /// Spawn a new handle with the given `sender`.
    fn spawn(sender: Sender) -> Self
    where
        Self: Sized;

    /// Set this handle's configuration.
    fn set_config(&mut self, config: Config);

    /// The file's content at `path` has been modified, and should be reloaded.
    fn invalidate(&mut self, path: AbsPathBuf);

    /// Load the content of the given file, returning [`None`] if it does not
    /// exists.
    fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>;
}

impl Entry {
    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git],
    /// })
    /// ```
    pub fn rs_files_recursively(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git"]))
    }

    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git, base/target],
    /// })
    /// ```
    pub fn local_cargo_package(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git", "target"]))
    }

    /// Returns:
    /// ```text
    /// Entry::Directories(Directories {
    ///     extensions: ["rs"],
    ///     include: [base],
    ///     exclude: [base/.git, /tests, /examples, /benches],
    /// })
    /// ```
    pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry {
        Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"]))
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// See [`Directories::contains_file`].
    pub fn contains_file(&self, path: &AbsPath) -> bool {
        match self {
            Entry::Files(files) => files.iter().any(|it| it == path),
            Entry::Directories(dirs) => dirs.contains_file(path),
        }
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// - If `self` is `Entry::Files`, returns `false`
    /// - Else, see [`Directories::contains_dir`].
    pub fn contains_dir(&self, path: &AbsPath) -> bool {
        match self {
            Entry::Files(_) => false,
            Entry::Directories(dirs) => dirs.contains_dir(path),
        }
    }
}

impl Directories {
    /// Returns `true` if `path` is included in `self`.
    pub fn contains_file(&self, path: &AbsPath) -> bool {
        // First, check the file extension...
        let ext = path.extension().unwrap_or_default();
        if self.extensions.iter().all(|it| it.as_str() != ext) {
            return false;
        }

        // Then, check for path inclusion...
        self.includes_path(path)
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// Since `path` is supposed to be a directory, this will not take extension
    /// into account.
    pub fn contains_dir(&self, path: &AbsPath) -> bool {
        self.includes_path(path)
    }

    /// Returns `true` if `path` is included in `self`.
    ///
    /// It is included if
    ///   - An element in `self.include` is a prefix of `path`.
    ///   - This path is longer than any element in `self.exclude` that is a prefix
    ///     of `path`. In case of equality, exclusion wins.
    fn includes_path(&self, path: &AbsPath) -> bool {
        let mut include = None::<&AbsPathBuf>;
        for incl in &self.include {
            if path.starts_with(incl) {
                include = Some(match include {
                    Some(prev) if prev.starts_with(incl) => prev,
                    _ => incl,
                });
            }
        }

        let include = match include {
            Some(it) => it,
            None => return false,
        };

        !self.exclude.iter().any(|excl| path.starts_with(excl) && excl.starts_with(include))
    }
}

/// Returns :
/// ```text
/// Directories {
///     extensions: ["rs"],
///     include: [base],
///     exclude: [base/<exclude>],
/// }
/// ```
fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories {
    let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>();
    Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude }
}

impl fmt::Debug for Message {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Message::Loaded { files } => {
                f.debug_struct("Loaded").field("n_files", &files.len()).finish()
            }
            Message::Progress { n_total, n_done, config_version } => f
                .debug_struct("Progress")
                .field("n_total", n_total)
                .field("n_done", n_done)
                .field("config_version", config_version)
                .finish(),
        }
    }
}

#[test]
fn handle_is_object_safe() {
    fn _assert(_: &dyn Handle) {}
}
<|endoftext|>


<commit_before>/// The expansion from a test function to the appropriate test struct for libtest
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
use crate::util::check_builtin_macro_attribute;

use rustc_ast_pretty::pprust;
use rustc_expand::base::*;
use rustc_span::source_map::respan;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use syntax::ast;
use syntax::attr;

use std::iter;

// #[test_case] is used by custom test authors to mark tests
// When building for test, it needs to make the item public and gensym the name
// Otherwise, we'll omit the item. This behavior means that any item annotated
// with #[test_case] is never addressable.
//
// We mark item with an inert attribute "rustc_test_marker" which the test generation
// logic will pick up on.
pub fn expand_test_case(
    ecx: &mut ExtCtxt<'_>,
    attr_sp: Span,
    meta_item: &ast::MetaItem,
    anno_item: Annotatable,
) -> Vec<Annotatable> {
    check_builtin_macro_attribute(ecx, meta_item, sym::test_case);

    if !ecx.ecfg.should_test {
        return vec![];
    }

    let sp = ecx.with_def_site_ctxt(attr_sp);
    let mut item = anno_item.expect_item();
    item = item.map(|mut item| {
        item.vis = respan(item.vis.span, ast::VisibilityKind::Public);
        item.ident.span = item.ident.span.with_ctxt(sp.ctxt());
        item.attrs.push(ecx.attribute(ecx.meta_word(sp, sym::rustc_test_marker)));
        item
    });

    return vec![Annotatable::Item(item)];
}

pub fn expand_test(
    cx: &mut ExtCtxt<'_>,
    attr_sp: Span,
    meta_item: &ast::MetaItem,
    item: Annotatable,
) -> Vec<Annotatable> {
    check_builtin_macro_attribute(cx, meta_item, sym::test);
    expand_test_or_bench(cx, attr_sp, item, false)
}

pub fn expand_bench(
    cx: &mut ExtCtxt<'_>,
    attr_sp: Span,
    meta_item: &ast::MetaItem,
    item: Annotatable,
) -> Vec<Annotatable> {
    check_builtin_macro_attribute(cx, meta_item, sym::bench);
    expand_test_or_bench(cx, attr_sp, item, true)
}

pub fn expand_test_or_bench(
    cx: &mut ExtCtxt<'_>,
    attr_sp: Span,
    item: Annotatable,
    is_bench: bool,
) -> Vec<Annotatable> {
    // If we're not in test configuration, remove the annotated item
    if !cx.ecfg.should_test {
        return vec![];
    }

    let item = if let Annotatable::Item(i) = item {
        i
    } else {
        cx.parse_sess
            .span_diagnostic
            .span_fatal(
                item.span(),
                "`#[test]` attribute is only allowed on non associated functions",
            )
            .raise();
    };

    if let ast::ItemKind::Mac(_) = item.kind {
        cx.parse_sess.span_diagnostic.span_warn(
            item.span,
            "`#[test]` attribute should not be used on macros. Use `#[cfg(test)]` instead.",
        );
        return vec![Annotatable::Item(item)];
    }

    // has_*_signature will report any errors in the type so compilation
    // will fail. We shouldn't try to expand in this case because the errors
    // would be spurious.
    if (!is_bench && !has_test_signature(cx, &item))
        || (is_bench && !has_bench_signature(cx, &item))
    {
        return vec![Annotatable::Item(item)];
    }

    let (sp, attr_sp) = (cx.with_def_site_ctxt(item.span), cx.with_def_site_ctxt(attr_sp));

    let test_id = ast::Ident::new(sym::test, attr_sp);

    // creates test::$name
    let test_path = |name| cx.path(sp, vec![test_id, cx.ident_of(name, sp)]);

    // creates test::ShouldPanic::$name
    let should_panic_path =
        |name| cx.path(sp, vec![test_id, cx.ident_of("ShouldPanic", sp), cx.ident_of(name, sp)]);

    // creates test::TestType::$name
    let test_type_path =
        |name| cx.path(sp, vec![test_id, cx.ident_of("TestType", sp), cx.ident_of(name, sp)]);

    // creates $name: $expr
    let field = |name, expr| cx.field_imm(sp, cx.ident_of(name, sp), expr);

    let test_fn = if is_bench {
        // A simple ident for a lambda
        let b = cx.ident_of("b", attr_sp);

        cx.expr_call(
            sp,
            cx.expr_path(test_path("StaticBenchFn")),
            vec![
                // |b| self::test::assert_test_result(
                cx.lambda1(
                    sp,
                    cx.expr_call(
                        sp,
                        cx.expr_path(test_path("assert_test_result")),
                        vec![
                            // super::$test_fn(b)
                            cx.expr_call(
                                sp,
                                cx.expr_path(cx.path(sp, vec![item.ident])),
                                vec![cx.expr_ident(sp, b)],
                            ),
                        ],
                    ),
                    b,
                ), // )
            ],
        )
    } else {
        cx.expr_call(
            sp,
            cx.expr_path(test_path("StaticTestFn")),
            vec![
                // || {
                cx.lambda0(
                    sp,
                    // test::assert_test_result(
                    cx.expr_call(
                        sp,
                        cx.expr_path(test_path("assert_test_result")),
                        vec![
                            // $test_fn()
                            cx.expr_call(sp, cx.expr_path(cx.path(sp, vec![item.ident])), vec![]), // )
                        ],
                    ), // }
                ), // )
            ],
        )
    };

    let mut test_const = cx.item(
        sp,
        ast::Ident::new(item.ident.name, sp),
        vec![
            // #[cfg(test)]
            cx.attribute(attr::mk_list_item(
                ast::Ident::new(sym::cfg, attr_sp),
                vec![attr::mk_nested_word_item(ast::Ident::new(sym::test, attr_sp))],
            )),
            // #[rustc_test_marker]
            cx.attribute(cx.meta_word(attr_sp, sym::rustc_test_marker)),
        ],
        // const $ident: test::TestDescAndFn =
        ast::ItemKind::Const(
            cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
            // test::TestDescAndFn {
            cx.expr_struct(
                sp,
                test_path("TestDescAndFn"),
                vec![
                    // desc: test::TestDesc {
                    field(
                        "desc",
                        cx.expr_struct(
                            sp,
                            test_path("TestDesc"),
                            vec![
                                // name: "path::to::test"
                                field(
                                    "name",
                                    cx.expr_call(
                                        sp,
                                        cx.expr_path(test_path("StaticTestName")),
                                        vec![cx.expr_str(
                                            sp,
                                            Symbol::intern(&item_path(
                                                // skip the name of the root module
                                                &cx.current_expansion.module.mod_path[1..],
                                                &item.ident,
                                            )),
                                        )],
                                    ),
                                ),
                                // ignore: true | false
                                field("ignore", cx.expr_bool(sp, should_ignore(&item))),
                                // allow_fail: true | false
                                field("allow_fail", cx.expr_bool(sp, should_fail(&item))),
                                // should_panic: ...
                                field(
                                    "should_panic",
                                    match should_panic(cx, &item) {
                                        // test::ShouldPanic::No
                                        ShouldPanic::No => cx.expr_path(should_panic_path("No")),
                                        // test::ShouldPanic::Yes
                                        ShouldPanic::Yes(None) => {
                                            cx.expr_path(should_panic_path("Yes"))
                                        }
                                        // test::ShouldPanic::YesWithMessage("...")
                                        ShouldPanic::Yes(Some(sym)) => cx.expr_call(
                                            sp,
                                            cx.expr_path(should_panic_path("YesWithMessage")),
                                            vec![cx.expr_str(sp, sym)],
                                        ),
                                    },
                                ),
                                // test_type: ...
                                field(
                                    "test_type",
                                    match test_type(cx) {
                                        // test::TestType::UnitTest
                                        TestType::UnitTest => {
                                            cx.expr_path(test_type_path("UnitTest"))
                                        }
                                        // test::TestType::IntegrationTest
                                        TestType::IntegrationTest => {
                                            cx.expr_path(test_type_path("IntegrationTest"))
                                        }
                                        // test::TestPath::Unknown
                                        TestType::Unknown => {
                                            cx.expr_path(test_type_path("Unknown"))
                                        }
                                    },
                                ),
                                // },
                            ],
                        ),
                    ),
                    // testfn: test::StaticTestFn(...) | test::StaticBenchFn(...)
                    field("testfn", test_fn), // }
                ],
            ), // }
        ),
    );
    test_const = test_const.map(|mut tc| {
        tc.vis.node = ast::VisibilityKind::Public;
        tc
    });

    // extern crate test
    let test_extern = cx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None));

    log::debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));

    vec![
        // Access to libtest under a hygienic name
        Annotatable::Item(test_extern),
        // The generated test case
        Annotatable::Item(test_const),
        // The original item
        Annotatable::Item(item),
    ]
}

fn item_path(mod_path: &[ast::Ident], item_ident: &ast::Ident) -> String {
    mod_path
        .iter()
        .chain(iter::once(item_ident))
        .map(|x| x.to_string())
        .collect::<Vec<String>>()
        .join("::")
}

enum ShouldPanic {
    No,
    Yes(Option<Symbol>),
}

fn should_ignore(i: &ast::Item) -> bool {
    attr::contains_name(&i.attrs, sym::ignore)
}

fn should_fail(i: &ast::Item) -> bool {
    attr::contains_name(&i.attrs, sym::allow_fail)
}

fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
    match attr::find_by_name(&i.attrs, sym::should_panic) {
        Some(attr) => {
            let ref sd = cx.parse_sess.span_diagnostic;

            match attr.meta_item_list() {
                // Handle #[should_panic(expected = "foo")]
                Some(list) => {
                    let msg = list
                        .iter()
                        .find(|mi| mi.check_name(sym::expected))
                        .and_then(|mi| mi.meta_item())
                        .and_then(|mi| mi.value_str());
                    if list.len() != 1 || msg.is_none() {
                        sd.struct_span_warn(
                            attr.span,
                            "argument must be of the form: \
                             `expected = \"error message\"`",
                        )
                        .note(
                            "errors in this attribute were erroneously \
                                allowed and will become a hard error in a \
                                future release.",
                        )
                        .emit();
                        ShouldPanic::Yes(None)
                    } else {
                        ShouldPanic::Yes(msg)
                    }
                }
                // Handle #[should_panic] and #[should_panic = "expected"]
                None => ShouldPanic::Yes(attr.value_str()),
            }
        }
        None => ShouldPanic::No,
    }
}

enum TestType {
    UnitTest,
    IntegrationTest,
    Unknown,
}

/// Attempts to determine the type of test.
/// Since doctests are created without macro expanding, only possible variants here
/// are `UnitTest`, `IntegrationTest` or `Unknown`.
fn test_type(cx: &ExtCtxt<'_>) -> TestType {
    // Root path from context contains the topmost sources directory of the crate.
    // I.e., for `project` with sources in `src` and tests in `tests` folders
    // (no matter how many nested folders lie inside),
    // there will be two different root paths: `/project/src` and `/project/tests`.
    let crate_path = cx.root_path.as_path();

    if crate_path.ends_with("src") {
        // `/src` folder contains unit-tests.
        TestType::UnitTest
    } else if crate_path.ends_with("tests") {
        // `/tests` folder contains integration tests.
        TestType::IntegrationTest
    } else {
        // Crate layout doesn't match expected one, test type is unknown.
        TestType::Unknown
    }
}

fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
    let has_should_panic_attr = attr::contains_name(&i.attrs, sym::should_panic);
    let ref sd = cx.parse_sess.span_diagnostic;
    if let ast::ItemKind::Fn(ref sig, ref generics, _) = i.kind {
        if let ast::Unsafe::Yes(span) = sig.header.unsafety {
            sd.struct_span_err(i.span, "unsafe functions cannot be used for tests")
                .span_label(span, "unsafe because of this")
                .emit();
            return false;
        }
        if let ast::Async::Yes { span, .. } = sig.header.asyncness {
            sd.struct_span_err(i.span, "async functions cannot be used for tests")
                .span_label(span, "async because of this")
                .emit();
            return false;
        }

        // If the termination trait is active, the compiler will check that the output
        // type implements the `Termination` trait as `libtest` enforces that.
        let has_output = match sig.decl.output {
            ast::FunctionRetTy::Default(..) => false,
            ast::FunctionRetTy::Ty(ref t) if t.kind.is_unit() => false,
            _ => true,
        };

        if !sig.decl.inputs.is_empty() {
            sd.span_err(i.span, "functions used as tests can not have any arguments");
            return false;
        }

        match (has_output, has_should_panic_attr) {
            (true, true) => {
                sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
                false
            }
            (true, false) => {
                if !generics.params.is_empty() {
                    sd.span_err(i.span, "functions used as tests must have signature fn() -> ()");
                    false
                } else {
                    true
                }
            }
            (false, _) => true,
        }
    } else {
        sd.span_err(i.span, "only functions may be used as tests");
        false
    }
}

fn has_bench_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
    let has_sig = if let ast::ItemKind::Fn(ref sig, _, _) = i.kind {
        // N.B., inadequate check, but we're running
        // well before resolve, can't get too deep.
        sig.decl.inputs.len() == 1
    } else {
        false
    };

    if !has_sig {
        cx.parse_sess.span_diagnostic.span_err(
            i.span,
            "functions used as benches must have \
            signature `fn(&mut Bencher) -> impl Termination`",
        );
    }

    has_sig
}
<commit_msg>rustc_bulltin_macros: tweak span_labels<commit_after>/// The expansion from a test function to the appropriate test struct for libtest
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
use crate::util::check_builtin_macro_attribute;

use rustc_ast_pretty::pprust;
use rustc_expand::base::*;
use rustc_span::source_map::respan;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use syntax::ast;
use syntax::attr;

use std::iter;

// #[test_case] is used by custom test authors to mark tests
// When building for test, it needs to make the item public and gensym the name
// Otherwise, we'll omit the item. This behavior means that any item annotated
// with #[test_case] is never addressable.
//
// We mark item with an inert attribute "rustc_test_marker" which the test generation
// logic will pick up on.
pub fn expand_test_case(
    ecx: &mut ExtCtxt<'_>,
    attr_sp: Span,
    meta_item: &ast::MetaItem,
    anno_item: Annotatable,
) -> Vec<Annotatable> {
    check_builtin_macro_attribute(ecx, meta_item, sym::test_case);

    if !ecx.ecfg.should_test {
        return vec![];
    }

    let sp = ecx.with_def_site_ctxt(attr_sp);
    let mut item = anno_item.expect_item();
    item = item.map(|mut item| {
        item.vis = respan(item.vis.span, ast::VisibilityKind::Public);
        item.ident.span = item.ident.span.with_ctxt(sp.ctxt());
        item.attrs.push(ecx.attribute(ecx.meta_word(sp, sym::rustc_test_marker)));
        item
    });

    return vec![Annotatable::Item(item)];
}

pub fn expand_test(
    cx: &mut ExtCtxt<'_>,
    attr_sp: Span,
    meta_item: &ast::MetaItem,
    item: Annotatable,
) -> Vec<Annotatable> {
    check_builtin_macro_attribute(cx, meta_item, sym::test);
    expand_test_or_bench(cx, attr_sp, item, false)
}

pub fn expand_bench(
    cx: &mut ExtCtxt<'_>,
    attr_sp: Span,
    meta_item: &ast::MetaItem,
    item: Annotatable,
) -> Vec<Annotatable> {
    check_builtin_macro_attribute(cx, meta_item, sym::bench);
    expand_test_or_bench(cx, attr_sp, item, true)
}

pub fn expand_test_or_bench(
    cx: &mut ExtCtxt<'_>,
    attr_sp: Span,
    item: Annotatable,
    is_bench: bool,
) -> Vec<Annotatable> {
    // If we're not in test configuration, remove the annotated item
    if !cx.ecfg.should_test {
        return vec![];
    }

    let item = if let Annotatable::Item(i) = item {
        i
    } else {
        cx.parse_sess
            .span_diagnostic
            .span_fatal(
                item.span(),
                "`#[test]` attribute is only allowed on non associated functions",
            )
            .raise();
    };

    if let ast::ItemKind::Mac(_) = item.kind {
        cx.parse_sess.span_diagnostic.span_warn(
            item.span,
            "`#[test]` attribute should not be used on macros. Use `#[cfg(test)]` instead.",
        );
        return vec![Annotatable::Item(item)];
    }

    // has_*_signature will report any errors in the type so compilation
    // will fail. We shouldn't try to expand in this case because the errors
    // would be spurious.
    if (!is_bench && !has_test_signature(cx, &item))
        || (is_bench && !has_bench_signature(cx, &item))
    {
        return vec![Annotatable::Item(item)];
    }

    let (sp, attr_sp) = (cx.with_def_site_ctxt(item.span), cx.with_def_site_ctxt(attr_sp));

    let test_id = ast::Ident::new(sym::test, attr_sp);

    // creates test::$name
    let test_path = |name| cx.path(sp, vec![test_id, cx.ident_of(name, sp)]);

    // creates test::ShouldPanic::$name
    let should_panic_path =
        |name| cx.path(sp, vec![test_id, cx.ident_of("ShouldPanic", sp), cx.ident_of(name, sp)]);

    // creates test::TestType::$name
    let test_type_path =
        |name| cx.path(sp, vec![test_id, cx.ident_of("TestType", sp), cx.ident_of(name, sp)]);

    // creates $name: $expr
    let field = |name, expr| cx.field_imm(sp, cx.ident_of(name, sp), expr);

    let test_fn = if is_bench {
        // A simple ident for a lambda
        let b = cx.ident_of("b", attr_sp);

        cx.expr_call(
            sp,
            cx.expr_path(test_path("StaticBenchFn")),
            vec![
                // |b| self::test::assert_test_result(
                cx.lambda1(
                    sp,
                    cx.expr_call(
                        sp,
                        cx.expr_path(test_path("assert_test_result")),
                        vec![
                            // super::$test_fn(b)
                            cx.expr_call(
                                sp,
                                cx.expr_path(cx.path(sp, vec![item.ident])),
                                vec![cx.expr_ident(sp, b)],
                            ),
                        ],
                    ),
                    b,
                ), // )
            ],
        )
    } else {
        cx.expr_call(
            sp,
            cx.expr_path(test_path("StaticTestFn")),
            vec![
                // || {
                cx.lambda0(
                    sp,
                    // test::assert_test_result(
                    cx.expr_call(
                        sp,
                        cx.expr_path(test_path("assert_test_result")),
                        vec![
                            // $test_fn()
                            cx.expr_call(sp, cx.expr_path(cx.path(sp, vec![item.ident])), vec![]), // )
                        ],
                    ), // }
                ), // )
            ],
        )
    };

    let mut test_const = cx.item(
        sp,
        ast::Ident::new(item.ident.name, sp),
        vec![
            // #[cfg(test)]
            cx.attribute(attr::mk_list_item(
                ast::Ident::new(sym::cfg, attr_sp),
                vec![attr::mk_nested_word_item(ast::Ident::new(sym::test, attr_sp))],
            )),
            // #[rustc_test_marker]
            cx.attribute(cx.meta_word(attr_sp, sym::rustc_test_marker)),
        ],
        // const $ident: test::TestDescAndFn =
        ast::ItemKind::Const(
            cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
            // test::TestDescAndFn {
            cx.expr_struct(
                sp,
                test_path("TestDescAndFn"),
                vec![
                    // desc: test::TestDesc {
                    field(
                        "desc",
                        cx.expr_struct(
                            sp,
                            test_path("TestDesc"),
                            vec![
                                // name: "path::to::test"
                                field(
                                    "name",
                                    cx.expr_call(
                                        sp,
                                        cx.expr_path(test_path("StaticTestName")),
                                        vec![cx.expr_str(
                                            sp,
                                            Symbol::intern(&item_path(
                                                // skip the name of the root module
                                                &cx.current_expansion.module.mod_path[1..],
                                                &item.ident,
                                            )),
                                        )],
                                    ),
                                ),
                                // ignore: true | false
                                field("ignore", cx.expr_bool(sp, should_ignore(&item))),
                                // allow_fail: true | false
                                field("allow_fail", cx.expr_bool(sp, should_fail(&item))),
                                // should_panic: ...
                                field(
                                    "should_panic",
                                    match should_panic(cx, &item) {
                                        // test::ShouldPanic::No
                                        ShouldPanic::No => cx.expr_path(should_panic_path("No")),
                                        // test::ShouldPanic::Yes
                                        ShouldPanic::Yes(None) => {
                                            cx.expr_path(should_panic_path("Yes"))
                                        }
                                        // test::ShouldPanic::YesWithMessage("...")
                                        ShouldPanic::Yes(Some(sym)) => cx.expr_call(
                                            sp,
                                            cx.expr_path(should_panic_path("YesWithMessage")),
                                            vec![cx.expr_str(sp, sym)],
                                        ),
                                    },
                                ),
                                // test_type: ...
                                field(
                                    "test_type",
                                    match test_type(cx) {
                                        // test::TestType::UnitTest
                                        TestType::UnitTest => {
                                            cx.expr_path(test_type_path("UnitTest"))
                                        }
                                        // test::TestType::IntegrationTest
                                        TestType::IntegrationTest => {
                                            cx.expr_path(test_type_path("IntegrationTest"))
                                        }
                                        // test::TestPath::Unknown
                                        TestType::Unknown => {
                                            cx.expr_path(test_type_path("Unknown"))
                                        }
                                    },
                                ),
                                // },
                            ],
                        ),
                    ),
                    // testfn: test::StaticTestFn(...) | test::StaticBenchFn(...)
                    field("testfn", test_fn), // }
                ],
            ), // }
        ),
    );
    test_const = test_const.map(|mut tc| {
        tc.vis.node = ast::VisibilityKind::Public;
        tc
    });

    // extern crate test
    let test_extern = cx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None));

    log::debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));

    vec![
        // Access to libtest under a hygienic name
        Annotatable::Item(test_extern),
        // The generated test case
        Annotatable::Item(test_const),
        // The original item
        Annotatable::Item(item),
    ]
}

fn item_path(mod_path: &[ast::Ident], item_ident: &ast::Ident) -> String {
    mod_path
        .iter()
        .chain(iter::once(item_ident))
        .map(|x| x.to_string())
        .collect::<Vec<String>>()
        .join("::")
}

enum ShouldPanic {
    No,
    Yes(Option<Symbol>),
}

fn should_ignore(i: &ast::Item) -> bool {
    attr::contains_name(&i.attrs, sym::ignore)
}

fn should_fail(i: &ast::Item) -> bool {
    attr::contains_name(&i.attrs, sym::allow_fail)
}

fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
    match attr::find_by_name(&i.attrs, sym::should_panic) {
        Some(attr) => {
            let ref sd = cx.parse_sess.span_diagnostic;

            match attr.meta_item_list() {
                // Handle #[should_panic(expected = "foo")]
                Some(list) => {
                    let msg = list
                        .iter()
                        .find(|mi| mi.check_name(sym::expected))
                        .and_then(|mi| mi.meta_item())
                        .and_then(|mi| mi.value_str());
                    if list.len() != 1 || msg.is_none() {
                        sd.struct_span_warn(
                            attr.span,
                            "argument must be of the form: \
                             `expected = \"error message\"`",
                        )
                        .note(
                            "errors in this attribute were erroneously \
                                allowed and will become a hard error in a \
                                future release.",
                        )
                        .emit();
                        ShouldPanic::Yes(None)
                    } else {
                        ShouldPanic::Yes(msg)
                    }
                }
                // Handle #[should_panic] and #[should_panic = "expected"]
                None => ShouldPanic::Yes(attr.value_str()),
            }
        }
        None => ShouldPanic::No,
    }
}

enum TestType {
    UnitTest,
    IntegrationTest,
    Unknown,
}

/// Attempts to determine the type of test.
/// Since doctests are created without macro expanding, only possible variants here
/// are `UnitTest`, `IntegrationTest` or `Unknown`.
fn test_type(cx: &ExtCtxt<'_>) -> TestType {
    // Root path from context contains the topmost sources directory of the crate.
    // I.e., for `project` with sources in `src` and tests in `tests` folders
    // (no matter how many nested folders lie inside),
    // there will be two different root paths: `/project/src` and `/project/tests`.
    let crate_path = cx.root_path.as_path();

    if crate_path.ends_with("src") {
        // `/src` folder contains unit-tests.
        TestType::UnitTest
    } else if crate_path.ends_with("tests") {
        // `/tests` folder contains integration tests.
        TestType::IntegrationTest
    } else {
        // Crate layout doesn't match expected one, test type is unknown.
        TestType::Unknown
    }
}

fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
    let has_should_panic_attr = attr::contains_name(&i.attrs, sym::should_panic);
    let ref sd = cx.parse_sess.span_diagnostic;
    if let ast::ItemKind::Fn(ref sig, ref generics, _) = i.kind {
        if let ast::Unsafe::Yes(span) = sig.header.unsafety {
            sd.struct_span_err(i.span, "unsafe functions cannot be used for tests")
                .span_label(span, "`unsafe` because of this")
                .emit();
            return false;
        }
        if let ast::Async::Yes { span, .. } = sig.header.asyncness {
            sd.struct_span_err(i.span, "async functions cannot be used for tests")
                .span_label(span, "`async` because of this")
                .emit();
            return false;
        }

        // If the termination trait is active, the compiler will check that the output
        // type implements the `Termination` trait as `libtest` enforces that.
        let has_output = match sig.decl.output {
            ast::FunctionRetTy::Default(..) => false,
            ast::FunctionRetTy::Ty(ref t) if t.kind.is_unit() => false,
            _ => true,
        };

        if !sig.decl.inputs.is_empty() {
            sd.span_err(i.span, "functions used as tests can not have any arguments");
            return false;
        }

        match (has_output, has_should_panic_attr) {
            (true, true) => {
                sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
                false
            }
            (true, false) => {
                if !generics.params.is_empty() {
                    sd.span_err(i.span, "functions used as tests must have signature fn() -> ()");
                    false
                } else {
                    true
                }
            }
            (false, _) => true,
        }
    } else {
        sd.span_err(i.span, "only functions may be used as tests");
        false
    }
}

fn has_bench_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
    let has_sig = if let ast::ItemKind::Fn(ref sig, _, _) = i.kind {
        // N.B., inadequate check, but we're running
        // well before resolve, can't get too deep.
        sig.decl.inputs.len() == 1
    } else {
        false
    };

    if !has_sig {
        cx.parse_sess.span_diagnostic.span_err(
            i.span,
            "functions used as benches must have \
            signature `fn(&mut Bencher) -> impl Termination`",
        );
    }

    has_sig
}
<|endoftext|>