<commit_before>use std::ffi::OsString;
use std::fs::OpenOptions;
use std::io;
use std::io::BufWriter;
use std::io::Stderr;
use std::io::Stdin;
use std::io::Stdout;
use std::io::Write;
use std::path::PathBuf;
use structopt::StructOpt;
use super::Error;
use super::Result;

enum Context {
    /// The path is absolute (has root).
    Absolute,
    /// The `interactive` option is present.
    Interactive,
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Securely erase files (single-pass).")]
struct Options {
    /// Do not overwrite any files (verbose).
    #[structopt(short = "d", long = "dry-run")]
    dry_run: bool,

    /// Prompt before overwriting each file.
    #[structopt(short = "i", long = "interactive")]
    interactive: bool,

    /// Recursively descend into directories.
    #[structopt(short = "r", long = "recursive")]
    recursive: bool,

    /// Suppress all interaction.
    #[structopt(short = "s", long = "suppress")]
    suppress: bool,

    /// Explain what's being done.
    #[structopt(short = "V", long = "verbose")]
    verbose: bool,

    /// Show this message.
    #[structopt(short = "h", long = "help")]
    help: bool,

    /// Show the version.
    #[structopt(short = "v", long = "version")]
    version: bool,

    /// The paths to be accessed by this tool.
    #[structopt(name = "PATHS", parse(from_str))]
    paths: Vec<PathBuf>,
}

pub struct Zero {
    options: Options,
    stderr: Stderr,
    stdout: Stdout,
    stdin: Stdin,
}

impl Zero {
    /// Constructs this program from an iterable of arguments.
    pub fn from_iter<I>(iter: I) -> Result<Self>
    where
        Self: Sized,
        I: IntoIterator,
        I::Item: Into<OsString> + Clone,
    {
        return Ok(
            Self {
                options: Options::from_iter_safe(iter)?,
                stderr: io::stderr(),
                stdout: io::stdout(),
                stdin: io::stdin(),
            }
        );
    }

    /// Replaces the standard error stream for this program.
    pub fn stderr(&mut self, stderr: Stderr) -> &mut Self {
        self.stderr = stderr;
        return self;
    }

    /// Replaces the standard output stream for this program.
    pub fn stdout(&mut self, stdout: Stdout) -> &mut Self {
        self.stdout = stdout;
        return self;
    }

    /// Replaces the standard input stream for this program.
    pub fn stdin(&mut self, stdin: Stdin) -> &mut Self {
        self.stdin = stdin;
        return self;
    }

    /// Runs this program and writes all errors.
    pub fn run(&mut self) -> Result<()> {
        match self.run_inner() {
            Ok(val) => {
                return Ok(val);
            },
            Err(err) => {
                writeln!(self.stderr, "Error: {}", err)?;
                return Err(err);
            },
        }
    }

    /// Runs this program.
    fn run_inner(&mut self) -> Result<()> {
        // Write the help or version message
        if self.options.help {
            return self.help();
        }
        if self.options.version {
            return self.version();
        }

        // Check for conflicting options
        self.validate()?;

        // Loop through the paths
        return self.overwrite();
    }

    /// Checks for conflicts in the options.
    fn validate(&self) -> Result<()> {
        return if self.options.interactive && self.options.suppress {
            Err(Error::Conflict)
        } else {
            Ok(())
        };
    }

    /// Writes the help message to the standard error stream.
    fn help(&mut self) -> Result<()> {
        Options::clap().write_help(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Writes the version message to the standard error stream.
    fn version(&mut self) -> Result<()> {
        Options::clap().write_version(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Authorizes directory and file access by prompting the user and reading
    /// from the standard input stream.
    fn auth(&mut self, path: &PathBuf, context: Context) -> Result<bool> {
        // Determine the appropriate prompt
        let prompt = match context {
            Context::Absolute => "is absolute",
            Context::Interactive => "will be overwritten",
        };

        let mut input = String::new();
        loop {
            // Prompt the user and normalize the input
            write!(self.stderr, r#""{}" {} - continue? [y/n] "#, path.display(), prompt)?;
            self.stdin.read_line(&mut input)?;

            // The response must be `y` or `n`
            match input.trim().to_lowercase().as_str() {
                "n" => {
                    if self.options.verbose {
                        writeln!(self.stderr, "Skipped.")?;
                    }
                    return Ok(false);
                },
                "y" => {
                    return Ok(true);
                },
                _ => {
                    input.clear();
                },
            }
        }
    }

    /// Overwrites all paths provided by the user. Authorization may be
    /// requested if the `suppress` option is not present.
    fn overwrite(&mut self) -> Result<()> {
        for path in self.options.paths.to_owned() {
            if !self.options.suppress && path.has_root() {
                // Authorize absolute paths (optional)
                if let Ok(false) = self.auth(&path, Context::Absolute) {
                    continue;
                }
            }

            if path.is_file() {
                // The path is a file
                self.overwrite_file(&path)?;
            } else {
                // Try the path as a directory
                self.overwrite_dir(&path)?;
            }
        }
        return Ok(());
    }

    /// Overwrites all files in the given directory and writes all errors.
    fn overwrite_dir(&mut self, path: &PathBuf) -> Result<()> {
        return if let Err(err) = self.overwrite_dir_inner(path) {
            self.write_error("Cannot access", path, &err)
        } else {
            Ok(())
        };
    }

    /// Overwrites all files in the given directory. If the `recursive` option
    /// is present, all files under the given directory will overwritten.
    fn overwrite_dir_inner(&mut self, path: &PathBuf) -> Result<()> {
        for entry in path.read_dir()? {
            let path = entry?.path();

            // Recurse if the entry is a directory (optional)
            if self.options.recursive && path.is_dir() {
                self.overwrite_dir(&path)?;
            } else if path.is_file() {
                self.overwrite_file(&path)?;
            }
        }
        return Ok(());
    }

    /// Overwrites the given file and writes all errors.
    fn overwrite_file(&mut self, path: &PathBuf) -> Result<()> {
        return if let Err(err) = self.overwrite_file_inner(path) {
            self.write_error("Cannot overwrite", path, &err)
        } else {
            Ok(())
        };
    }

    /// Overwrites the given file. Authorization may be requested and
    /// additional information may be written if the `interactive` and
    /// `verbose` options are present. The file will not be overwritten
    /// during a `dry-run`.
    fn overwrite_file_inner(&mut self, path: &PathBuf) -> Result<()> {
        if self.options.interactive {
            // Authorize every file (optional)
            if let Ok(false) = self.auth(path, Context::Interactive) {
                return Ok(());
            }
        }
        let metadata = path.metadata()?;

        if !self.options.dry_run {
            // Open the file and wrap it in a buffer
            let mut file = OpenOptions::new().write(true).open(path)?;
            let mut buf = BufWriter::new(file);

            // Overwrite the file
            for _ in 0..metadata.len() {
                buf.write(&[0])?;
            }

            // Flush the buffer to the disk
            file = buf.into_inner()?;
            file.sync_all()?;

            if self.options.verbose {
                // Write the results (optional)
                self.write_result("overwritten", path, metadata.len())?;
            }
        } else {
            // Perform a dry run (optional)
            self.write_result("will be overwritten", path, metadata.len())?;
        }
        return Ok(());
    }

    /// Writes a path related error to the standard error stream.
    fn write_error(&mut self, msg: &str, path: &PathBuf, err: &Error) -> Result<()> {
        writeln!(self.stderr, r#"Error: {} "{}": {}"#, msg, path.display(), err)?;
        return Ok(());
    }

    /// Writes the result of an operation to the standard output stream.
    fn write_result(&mut self, msg: &str, path: &PathBuf, len: u64) -> Result<()> {
        writeln!(self.stdout, r#""{}": {} byte(s) {}."#, path.display(), len, msg)?;
        return Ok(());
    }
}
<commit_msg>Updated comment<commit_after>use std::ffi::OsString;
use std::fs::OpenOptions;
use std::io;
use std::io::BufWriter;
use std::io::Stderr;
use std::io::Stdin;
use std::io::Stdout;
use std::io::Write;
use std::path::PathBuf;
use structopt::StructOpt;
use super::Error;
use super::Result;

enum Context {
    /// The path is absolute (has root).
    Absolute,
    /// The `interactive` option is present.
    Interactive,
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Securely erase files (single-pass).")]
struct Options {
    /// Do not overwrite any files (verbose).
    #[structopt(short = "d", long = "dry-run")]
    dry_run: bool,

    /// Prompt before overwriting each file.
    #[structopt(short = "i", long = "interactive")]
    interactive: bool,

    /// Recursively descend into directories.
    #[structopt(short = "r", long = "recursive")]
    recursive: bool,

    /// Suppress all interaction.
    #[structopt(short = "s", long = "suppress")]
    suppress: bool,

    /// Explain what's being done.
    #[structopt(short = "V", long = "verbose")]
    verbose: bool,

    /// Show this message.
    #[structopt(short = "h", long = "help")]
    help: bool,

    /// Show the version.
    #[structopt(short = "v", long = "version")]
    version: bool,

    /// The paths to be accessed by this tool.
    #[structopt(name = "PATHS", parse(from_str))]
    paths: Vec<PathBuf>,
}

pub struct Zero {
    options: Options,
    stderr: Stderr,
    stdout: Stdout,
    stdin: Stdin,
}

impl Zero {
    /// Constructs this program from an iterable of arguments.
    pub fn from_iter<I>(iter: I) -> Result<Self>
    where
        Self: Sized,
        I: IntoIterator,
        I::Item: Into<OsString> + Clone,
    {
        return Ok(
            Self {
                options: Options::from_iter_safe(iter)?,
                stderr: io::stderr(),
                stdout: io::stdout(),
                stdin: io::stdin(),
            }
        );
    }

    /// Replaces the standard error stream for this program.
    pub fn stderr(&mut self, stderr: Stderr) -> &mut Self {
        self.stderr = stderr;
        return self;
    }

    /// Replaces the standard output stream for this program.
    pub fn stdout(&mut self, stdout: Stdout) -> &mut Self {
        self.stdout = stdout;
        return self;
    }

    /// Replaces the standard input stream for this program.
    pub fn stdin(&mut self, stdin: Stdin) -> &mut Self {
        self.stdin = stdin;
        return self;
    }

    /// Runs this program and writes all errors.
    pub fn run(&mut self) -> Result<()> {
        match self.run_inner() {
            Ok(val) => {
                return Ok(val);
            },
            Err(err) => {
                writeln!(self.stderr, "Error: {}", err)?;
                return Err(err);
            },
        }
    }

    /// Runs this program.
    fn run_inner(&mut self) -> Result<()> {
        // Write the help or version message
        if self.options.help {
            return self.help();
        }
        if self.options.version {
            return self.version();
        }

        // Validate the options
        self.validate()?;

        // Handle the paths
        return self.overwrite();
    }

    /// Validates the options.
    fn validate(&self) -> Result<()> {
        return if self.options.interactive && self.options.suppress {
            Err(Error::Conflict)
        } else {
            Ok(())
        };
    }

    /// Writes the help message to the standard error stream.
    fn help(&mut self) -> Result<()> {
        Options::clap().write_help(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Writes the version message to the standard error stream.
    fn version(&mut self) -> Result<()> {
        Options::clap().write_version(&mut self.stderr)?;
        writeln!(self.stderr, "")?;
        return Ok(());
    }

    /// Authorizes directory and file access by prompting the user and reading
    /// from the standard input stream.
    fn auth(&mut self, path: &PathBuf, context: Context) -> Result<bool> {
        // Determine the appropriate prompt
        let prompt = match context {
            Context::Absolute => "is absolute",
            Context::Interactive => "will be overwritten",
        };

        let mut input = String::new();
        loop {
            // Prompt the user and normalize the input
            write!(self.stderr, r#""{}" {} - continue? [y/n] "#, path.display(), prompt)?;
            self.stdin.read_line(&mut input)?;

            // The response must be `y` or `n`
            match input.trim().to_lowercase().as_str() {
                "n" => {
                    if self.options.verbose {
                        writeln!(self.stderr, "Skipped.")?;
                    }
                    return Ok(false);
                },
                "y" => {
                    return Ok(true);
                },
                _ => {
                    input.clear();
                },
            }
        }
    }

    /// Overwrites all paths provided by the user. Authorization may be
    /// requested if the `suppress` option is not present.
    fn overwrite(&mut self) -> Result<()> {
        for path in self.options.paths.to_owned() {
            if !self.options.suppress && path.has_root() {
                // Authorize absolute paths (optional)
                if let Ok(false) = self.auth(&path, Context::Absolute) {
                    continue;
                }
            }

            if path.is_file() {
                // The path is a file
                self.overwrite_file(&path)?;
            } else {
                // Try the path as a directory
                self.overwrite_dir(&path)?;
            }
        }
        return Ok(());
    }

    /// Overwrites all files in the given directory and writes all errors.
    fn overwrite_dir(&mut self, path: &PathBuf) -> Result<()> {
        return if let Err(err) = self.overwrite_dir_inner(path) {
            self.write_error("Cannot access", path, &err)
        } else {
            Ok(())
        };
    }

    /// Overwrites all files in the given directory. If the `recursive` option
    /// is present, all files under the given directory will overwritten.
    fn overwrite_dir_inner(&mut self, path: &PathBuf) -> Result<()> {
        for entry in path.read_dir()? {
            let path = entry?.path();

            // Recurse if the entry is a directory (optional)
            if self.options.recursive && path.is_dir() {
                self.overwrite_dir(&path)?;
            } else if path.is_file() {
                self.overwrite_file(&path)?;
            }
        }
        return Ok(());
    }

    /// Overwrites the given file and writes all errors.
    fn overwrite_file(&mut self, path: &PathBuf) -> Result<()> {
        return if let Err(err) = self.overwrite_file_inner(path) {
            self.write_error("Cannot overwrite", path, &err)
        } else {
            Ok(())
        };
    }

    /// Overwrites the given file. Authorization may be requested and
    /// additional information may be written if the `interactive` and
    /// `verbose` options are present. The file will not be overwritten
    /// during a `dry-run`.
    fn overwrite_file_inner(&mut self, path: &PathBuf) -> Result<()> {
        if self.options.interactive {
            // Authorize every file (optional)
            if let Ok(false) = self.auth(path, Context::Interactive) {
                return Ok(());
            }
        }
        let metadata = path.metadata()?;

        if !self.options.dry_run {
            // Open the file and wrap it in a buffer
            let mut file = OpenOptions::new().write(true).open(path)?;
            let mut buf = BufWriter::new(file);

            // Overwrite the file
            for _ in 0..metadata.len() {
                buf.write(&[0])?;
            }

            // Flush the buffer to the disk
            file = buf.into_inner()?;
            file.sync_all()?;

            if self.options.verbose {
                // Write the results (optional)
                self.write_result("overwritten", path, metadata.len())?;
            }
        } else {
            // Perform a dry run (optional)
            self.write_result("will be overwritten", path, metadata.len())?;
        }
        return Ok(());
    }

    /// Writes a path related error to the standard error stream.
    fn write_error(&mut self, msg: &str, path: &PathBuf, err: &Error) -> Result<()> {
        writeln!(self.stderr, r#"Error: {} "{}": {}"#, msg, path.display(), err)?;
        return Ok(());
    }

    /// Writes the result of an operation to the standard output stream.
    fn write_result(&mut self, msg: &str, path: &PathBuf, len: u64) -> Result<()> {
        writeln!(self.stdout, r#""{}": {} byte(s) {}."#, path.display(), len, msg)?;
        return Ok(());
    }
}
<|endoftext|>


<commit_before>use std::collections::TreeMap;

#[deriving(Clone)]
struct RationalRect(f32, f32, f32, f32);

#[deriving(Clone)]
struct Window;

#[deriving(Clone)]
struct Stack<T> {
    focus: T,
    up:    Vec<T>,
    down:  Vec<T>
}

#[deriving(Clone)]
struct Workspace {
    tag:    String,
    layout: String,
    stack:  Option<Stack<Window>>
}

#[deriving(Clone)]
impl Workspace {
    fn new(tag: String, layout: String, stack: Option<Stack<Window>>) -> Workspace {
        Workspace {
            tag: tag,
            layout: layout,
            stack: stack
        }
    }
}

#[deriving(Clone)]
struct Screen {
    workspace: Workspace,
    screen:    uint,
    screen_detail: String
}

impl Screen {
    fn new(workspace: Workspace, screen: uint, screen_detail: String) -> Screen {
        Screen {
            workspace: workspace,
            screen: screen,
            screen_detail: screen_detail
        }
    }
}

#[deriving(Clone)]
struct Workspaces {
    current: Screen,
    visible: Vec<Screen>,
    hidden: Vec<Workspace>,
    floating: TreeMap<uint, RationalRect>
}

impl Workspaces {
    fn new(layout: String, tags: Vec<String>, screens: Vec<String>) -> Workspaces {
        let workspaces : Vec<Workspace> = tags.iter()
            .map(|tag| Workspace::new(tag.clone(), layout.clone(), None))
            .collect();
        let seen   : Vec<Workspace> = workspaces.iter().take(screens.len()).map(|x| x.clone()).collect();
        let unseen : Vec<Workspace> = workspaces.iter().skip(screens.len()).map(|x| x.clone()).collect();

        let current : Vec<Screen> = seen.iter()
            .zip(range(0, seen.len()))
            .zip(screens.iter())
            .map(|((a, b), c)| Screen::new(a.clone(), b, c.clone()))
            .collect();

        Workspaces {
            current: current.head().unwrap().clone(),
            visible: current.iter().skip(1).map(|x| x.clone()).collect(),
            hidden: unseen,
            floating: TreeMap::new()
        }
    }
}
<commit_msg>added view methods for workspaces<commit_after>use std::collections::TreeMap;

#[deriving(Clone)]
struct RationalRect(f32, f32, f32, f32);

#[deriving(Clone)]
struct Window;

#[deriving(Clone)]
struct Stack<T> {
    focus: T,
    up:    Vec<T>,
    down:  Vec<T>
}

#[deriving(Clone)]
struct Workspace {
    id:     uint,
    tag:    String,
    layout: String,
    stack:  Option<Stack<Window>>
}

#[deriving(Clone)]
impl Workspace {
    fn new(id: uint, tag: String, layout: String, stack: Option<Stack<Window>>) -> Workspace {
        Workspace {
            id: id,
            tag: tag,
            layout: layout,
            stack: stack
        }
    }
}

#[deriving(Clone)]
struct Screen {
    workspace: Workspace,
    screen_id:    uint,
    screen_detail: String
}

impl Screen {
    fn new(workspace: Workspace, screen_id: uint, screen_detail: String) -> Screen {
        Screen {
            workspace: workspace,
            screen_id: screen_id,
            screen_detail: screen_detail
        }
    }
}

#[deriving(Clone)]
struct Workspaces {
    current: Screen,
    visible: Vec<Screen>,
    hidden: Vec<Workspace>,
    floating: TreeMap<uint, RationalRect>
}

impl Workspaces {
    /// Create a new stackset, of empty stacks, with given tags,
    /// with physical screens whose descriptions are given by 'm'. The
    /// number of physical screens (@length 'm'@) should be less than or
    /// equal to the number of workspace tags.  The first workspace in the
    /// list will be current.
    ///
    /// Xinerama: Virtual workspaces are assigned to physical screens, starting at 0.
    fn new(layout: String, tags: Vec<String>, screens: Vec<String>) -> Workspaces {
        let workspaces : Vec<Workspace> = tags.iter()
            .enumerate()
            .map(|(id, tag)| Workspace::new(id, tag.clone(), layout.clone(), None))
            .collect();
        let seen   : Vec<Workspace> = workspaces.iter().take(screens.len()).map(|x| x.clone()).collect();
        let unseen : Vec<Workspace> = workspaces.iter().skip(screens.len()).map(|x| x.clone()).collect();

        let current : Vec<Screen> = seen.iter()
            .zip(range(0, seen.len()))
            .zip(screens.iter())
            .map(|((a, b), c)| Screen::new(a.clone(), b, c.clone()))
            .collect();

        Workspaces {
            current: current.head().unwrap().clone(),
            visible: current.iter().skip(1).map(|x| x.clone()).collect(),
            hidden: unseen,
            floating: TreeMap::new()
        }
    }

    /// Set focus to the workspace with index \'i\'.
    /// If the index is out of range, return the original 'StackSet'.
    ///
    /// Xinerama: If the workspace is not visible on any Xinerama screen, it
    /// becomes the current screen. If it is in the visible list, it becomes
    /// current.
    fn view(&mut self, index: uint) {
        match self.visible.iter().position(|s| s.workspace.id == index) {
            Some(screen_pos) => {
                let screen = self.visible[screen_pos].clone();
                self.visible.remove(screen_pos);
                self.visible.insert(0, self.current.clone());
                self.current = screen;
                return;
            },
            _ => ()
        }

        match self.hidden.iter().position(|w| w.id == index) {
            Some(workspace_pos) => {
                let current_workspace = self.current.workspace.clone();
                self.current.workspace = self.hidden[workspace_pos].clone();
                self.hidden.insert(0, current_workspace);
                return;
            },
            _ => ()
        }
    }

    /// Set focus to the given workspace.  If that workspace does not exist
    /// in the stackset, the original workspace is returned.  If that workspace is
    /// 'hidden', then display that workspace on the current screen, and move the
    /// current workspace to 'hidden'.  If that workspace is 'visible' on another
    /// screen, the workspaces of the current screen and the other screen are
    /// swapped.
    fn greedy_view(&mut self, index: uint) {

    }
}
<|endoftext|>


<commit_before>use super::ActivationFunction;
use fixedbitset::FixedBitSet;

pub enum CppnNodeType {
    Bias,
    Input,
    Output,
    Hidden,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CppnNodeIndex(usize);

impl CppnNodeIndex {
    fn index(&self) -> usize {
        self.0
    }
}

struct CppnLink {
    node_idx: CppnNodeIndex,
    weight: f64,
}

struct CppnNode {
    node_type: CppnNodeType,
    activation_function: Box<ActivationFunction>,
    input_links: Vec<CppnLink>,
    output_links: Vec<CppnLink>,
}

pub struct CppnGraph {
    nodes: Vec<CppnNode>,
}

impl CppnGraph {
    pub fn new() -> CppnGraph {
        CppnGraph { nodes: Vec::new() }
    }

    pub fn add_node(&mut self,
                    node_type: CppnNodeType,
                    activation_function: Box<ActivationFunction>)
                    -> CppnNodeIndex {
        let idx = CppnNodeIndex(self.nodes.len());
        self.nodes.push(CppnNode {
            node_type: node_type,
            activation_function: activation_function,
            input_links: Vec::new(),
            output_links: Vec::new(),
        });
        return idx;
    }

    // Returns true if the introduction of this directed link would lead towards a cycle.
    pub fn would_cycle(&self,
                       source_node_idx: CppnNodeIndex,
                       target_node_idx: CppnNodeIndex)
                       -> bool {
        if source_node_idx == target_node_idx {
            return true;
        }

        let mut seen_nodes = FixedBitSet::with_capacity(self.nodes.len());
        let mut nodes_to_visit = Vec::new();

        // We start at the target node and iterate all paths from there. If we hit the source node,
        // we found a cycle. Otherwise not.
        let start = target_node_idx.index();

        // We are looking for a path to this node.
        let path_to = source_node_idx.index();

        nodes_to_visit.push(start);
        seen_nodes.insert(start);

        while let Some(visit_node) = nodes_to_visit.pop() {
            for out_link in &self.nodes[visit_node].output_links {
                let next_node = out_link.node_idx.index();
                if !seen_nodes.contains(next_node) {
                    if next_node == path_to {
                        // We found a path to `path_to`. We have found a cycle.
                        return true;
                    }

                    seen_nodes.insert(next_node);
                    nodes_to_visit.push(next_node)
                }
            }
        }

        // We haven't found a cycle.
        return false;
    }

    // Check if the link is valid. Doesn't check for cycles.
    pub fn valid_link(&self,
                      source_node_idx: CppnNodeIndex,
                      target_node_idx: CppnNodeIndex)
                      -> Result<(), &'static str> {
        if source_node_idx == target_node_idx {
            return Err("Loops are not allowed");
        }

        let source_node = &self.nodes[source_node_idx.index()];
        if let CppnNodeType::Output = source_node.node_type {
            return Err("Cannot have an outgoing connection from Output node");
        }

        let target_node = &self.nodes[target_node_idx.index()];
        if let CppnNodeType::Input = target_node.node_type {
            return Err("Cannot have an incoming connection to Input node");
        }

        if let CppnNodeType::Bias = target_node.node_type {
            return Err("Cannot have an incoming connection to Bias node");
        }

        Ok(())
    }

    // Note: Doesn't check for cycles (except in the simple reflexive case).
    pub fn add_link(&mut self,
                    source_node_idx: CppnNodeIndex,
                    target_node_idx: CppnNodeIndex,
                    weight: f64) {
        if let Err(err) = self.valid_link(source_node_idx, target_node_idx) {
            panic!(err);
        }

        self.nodes[source_node_idx.index()].output_links.push(CppnLink {
            node_idx: target_node_idx,
            weight: weight,
        });

        self.nodes[target_node_idx.index()].input_links.push(CppnLink {
            node_idx: source_node_idx,
            weight: weight,
        });
    }
}

/// Represents the output value of a CppnNode.
#[derive(Debug, Copy, Clone)]
enum CppnNodeValue {
    None,
    InCalculation,
    Cached(f64),
}

struct CppnState {
    values: Vec<CppnNodeValue>,
}

impl CppnState {
    fn new(n: usize) -> CppnState {
        CppnState { values: (0..n).map(|_| CppnNodeValue::None).collect() }
    }

    fn set(&mut self, node_idx: CppnNodeIndex, value: f64) {
        self.values[node_idx.index()] = CppnNodeValue::Cached(value);
    }

    fn reset(&mut self) {
        for value in self.values.iter_mut() {
            *value = CppnNodeValue::None;
        }
    }

    /// Calculates the output of node `node_idx`, recursively calculating
    /// the outputs of all dependent nodes.
    /// Panics if it hits a cycle.
    /// XXX: Implement a non-recursive version.
    fn calculate_output_of_node(&mut self, graph: &CppnGraph, node_idx: CppnNodeIndex) -> f64 {
        match self.values[node_idx.index()] {
            CppnNodeValue::InCalculation => {
                panic!("Cycle detected");
            }
            CppnNodeValue::Cached(value) => {
                // Return the cached value.
                return value;
            }
            CppnNodeValue::None => {
                // fall through.
            }
        }
        let node = &graph.nodes[node_idx.index()];

        // Sum the input links
        let input_sum: f64 = match node.node_type {
            CppnNodeType::Bias => 1.0,
            CppnNodeType::Input => {
                debug_assert!(node.input_links.is_empty());
                panic!("No input set for Input node");
            }
            CppnNodeType::Output | CppnNodeType::Hidden => {
                // Mark this node as being processes. If we hit such a node during a recursive call
                // we have a cycle.
                self.values[node_idx.index()] = CppnNodeValue::InCalculation;
                let mut sum: f64 = 0.0;
                for in_link in &node.input_links {
                    sum += in_link.weight * self.calculate_output_of_node(graph, in_link.node_idx);
                }
                sum
            }
        };

        // apply activation function on `input_sum`.
        let output = node.activation_function.calculate(input_sum);

        // cache the value. this also resets the InCalculation state.
        self.values[node_idx.index()] = CppnNodeValue::Cached(output);

        output
    }
}

/// Represents a Compositional Pattern Producing Network (CPPN)
pub struct Cppn {
    graph: CppnGraph,
    inputs: Vec<CppnNodeIndex>,
    outputs: Vec<CppnNodeIndex>,
    state: CppnState,
}

impl Cppn {
    pub fn new(graph: CppnGraph) -> Cppn {
        let mut inputs = Vec::new();
        let mut outputs = Vec::new();

        for (i, node) in graph.nodes.iter().enumerate() {
            match node.node_type {
                CppnNodeType::Input => {
                    inputs.push(CppnNodeIndex(i));
                }
                CppnNodeType::Output => {
                    outputs.push(CppnNodeIndex(i));
                }
                _ => {}
            }
        }

        let state = CppnState::new(graph.nodes.len());
        Cppn {
            graph: graph,
            inputs: inputs,
            outputs: outputs,
            state: state,
        }
    }

    /// Calculate all outputs
    pub fn calculate(&mut self, inputs: &[&[f64]]) -> Vec<f64> {
        let mut state = &mut self.state;

        state.reset();

        // assign all inputs
        let mut i = 0;
        for input_list in inputs.iter() {
            for &input in input_list.iter() {
                state.set(self.inputs[i], input);
                i += 1;
            }
        }
        assert!(i == self.inputs.len());

        let graph = &self.graph;
        self.outputs
            .iter()
            .map(|&node_idx| state.calculate_output_of_node(graph, node_idx))
            .collect()
    }
}

#[test]
fn test_cycle() {
    use super::Identity;
    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input, Box::new(Identity));
    let h1 = g.add_node(CppnNodeType::Hidden, Box::new(Identity));
    let h2 = g.add_node(CppnNodeType::Hidden, Box::new(Identity));
    assert_eq!(true, g.valid_link(i1, i1).is_err());
    assert_eq!(true, g.valid_link(h1, h1).is_err());

    assert_eq!(true, g.valid_link(h1, i1).is_err());
    assert_eq!(Ok(()), g.valid_link(i1, h1));
    assert_eq!(Ok(()), g.valid_link(i1, h2));
    assert_eq!(Ok(()), g.valid_link(h1, h2));

    g.add_link(i1, h1, 0.0);
    assert_eq!(true, g.would_cycle(h1, i1));
    assert_eq!(false, g.would_cycle(i1, h1));
    assert_eq!(false, g.would_cycle(i1, h2));
    assert_eq!(true, g.would_cycle(i1, i1));
    assert_eq!(false, g.would_cycle(h1, h2));
    assert_eq!(false, g.would_cycle(h2, h1));
    assert_eq!(false, g.would_cycle(h2, i1));

    g.add_link(h1, h2, 0.0);
    assert_eq!(true, g.would_cycle(h2, i1));
    assert_eq!(true, g.would_cycle(h1, i1));
    assert_eq!(true, g.would_cycle(h2, h1));
    assert_eq!(false, g.would_cycle(i1, h2));
}

#[test]
fn test_simple_cppn() {
    use super::Identity;
    use super::bipolar::Linear;

    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input, Box::new(Identity));
    let h1 = g.add_node(CppnNodeType::Hidden, Box::new(Linear));
    let o1 = g.add_node(CppnNodeType::Output, Box::new(Identity));
    g.add_link(i1, h1, 0.5);
    g.add_link(h1, o1, 1.0);

    let mut cppn = Cppn::new(g);

    assert_eq!(vec![0.5 * 0.5], cppn.calculate(&[&[0.5]]));
    assert_eq!(vec![1.0], cppn.calculate(&[&[4.0]]));
    assert_eq!(vec![-1.0], cppn.calculate(&[&[-4.0]]));
}
<commit_msg>Minor rewrite using match arms instead of repetetive if let's<commit_after>use super::ActivationFunction;
use fixedbitset::FixedBitSet;

pub enum CppnNodeType {
    Bias,
    Input,
    Output,
    Hidden,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CppnNodeIndex(usize);

impl CppnNodeIndex {
    fn index(&self) -> usize {
        self.0
    }
}

struct CppnLink {
    node_idx: CppnNodeIndex,
    weight: f64,
}

struct CppnNode {
    node_type: CppnNodeType,
    activation_function: Box<ActivationFunction>,
    input_links: Vec<CppnLink>,
    output_links: Vec<CppnLink>,
}

pub struct CppnGraph {
    nodes: Vec<CppnNode>,
}

impl CppnGraph {
    pub fn new() -> CppnGraph {
        CppnGraph { nodes: Vec::new() }
    }

    pub fn add_node(&mut self,
                    node_type: CppnNodeType,
                    activation_function: Box<ActivationFunction>)
                    -> CppnNodeIndex {
        let idx = CppnNodeIndex(self.nodes.len());
        self.nodes.push(CppnNode {
            node_type: node_type,
            activation_function: activation_function,
            input_links: Vec::new(),
            output_links: Vec::new(),
        });
        return idx;
    }

    // Returns true if the introduction of this directed link would lead towards a cycle.
    pub fn would_cycle(&self,
                       source_node_idx: CppnNodeIndex,
                       target_node_idx: CppnNodeIndex)
                       -> bool {
        if source_node_idx == target_node_idx {
            return true;
        }

        let mut seen_nodes = FixedBitSet::with_capacity(self.nodes.len());
        let mut nodes_to_visit = Vec::new();

        // We start at the target node and iterate all paths from there. If we hit the source node,
        // we found a cycle. Otherwise not.
        let start = target_node_idx.index();

        // We are looking for a path to this node.
        let path_to = source_node_idx.index();

        nodes_to_visit.push(start);
        seen_nodes.insert(start);

        while let Some(visit_node) = nodes_to_visit.pop() {
            for out_link in &self.nodes[visit_node].output_links {
                let next_node = out_link.node_idx.index();
                if !seen_nodes.contains(next_node) {
                    if next_node == path_to {
                        // We found a path to `path_to`. We have found a cycle.
                        return true;
                    }

                    seen_nodes.insert(next_node);
                    nodes_to_visit.push(next_node)
                }
            }
        }

        // We haven't found a cycle.
        return false;
    }

    // Check if the link is valid. Doesn't check for cycles.
    pub fn valid_link(&self,
                      source_node_idx: CppnNodeIndex,
                      target_node_idx: CppnNodeIndex)
                      -> Result<(), &'static str> {
        if source_node_idx == target_node_idx {
            return Err("Loops are not allowed");
        }

        match self.nodes[source_node_idx.index()].node_type {
            CppnNodeType::Output => {
                return Err("Cannot have an outgoing connection from Output node")
            }
            _ => {}
        }

        match self.nodes[target_node_idx.index()].node_type {
            CppnNodeType::Input | CppnNodeType::Bias => {
                return Err("Cannot have an incoming connection to Input/Bias node");
            }
            _ => {}
        }

        Ok(())
    }

    // Note: Doesn't check for cycles (except in the simple reflexive case).
    pub fn add_link(&mut self,
                    source_node_idx: CppnNodeIndex,
                    target_node_idx: CppnNodeIndex,
                    weight: f64) {
        if let Err(err) = self.valid_link(source_node_idx, target_node_idx) {
            panic!(err);
        }

        self.nodes[source_node_idx.index()].output_links.push(CppnLink {
            node_idx: target_node_idx,
            weight: weight,
        });

        self.nodes[target_node_idx.index()].input_links.push(CppnLink {
            node_idx: source_node_idx,
            weight: weight,
        });
    }
}

/// Represents the output value of a CppnNode.
#[derive(Debug, Copy, Clone)]
enum CppnNodeValue {
    None,
    InCalculation,
    Cached(f64),
}

struct CppnState {
    values: Vec<CppnNodeValue>,
}

impl CppnState {
    fn new(n: usize) -> CppnState {
        CppnState { values: (0..n).map(|_| CppnNodeValue::None).collect() }
    }

    fn set(&mut self, node_idx: CppnNodeIndex, value: f64) {
        self.values[node_idx.index()] = CppnNodeValue::Cached(value);
    }

    fn reset(&mut self) {
        for value in self.values.iter_mut() {
            *value = CppnNodeValue::None;
        }
    }

    /// Calculates the output of node `node_idx`, recursively calculating
    /// the outputs of all dependent nodes.
    /// Panics if it hits a cycle.
    /// XXX: Implement a non-recursive version.
    fn calculate_output_of_node(&mut self, graph: &CppnGraph, node_idx: CppnNodeIndex) -> f64 {
        match self.values[node_idx.index()] {
            CppnNodeValue::InCalculation => {
                panic!("Cycle detected");
            }
            CppnNodeValue::Cached(value) => {
                // Return the cached value.
                return value;
            }
            CppnNodeValue::None => {
                // fall through.
            }
        }
        let node = &graph.nodes[node_idx.index()];

        // Sum the input links
        let input_sum: f64 = match node.node_type {
            CppnNodeType::Bias => 1.0,
            CppnNodeType::Input => {
                debug_assert!(node.input_links.is_empty());
                panic!("No input set for Input node");
            }
            CppnNodeType::Output | CppnNodeType::Hidden => {
                // Mark this node as being processes. If we hit such a node during a recursive call
                // we have a cycle.
                self.values[node_idx.index()] = CppnNodeValue::InCalculation;
                let mut sum: f64 = 0.0;
                for in_link in &node.input_links {
                    sum += in_link.weight * self.calculate_output_of_node(graph, in_link.node_idx);
                }
                sum
            }
        };

        // apply activation function on `input_sum`.
        let output = node.activation_function.calculate(input_sum);

        // cache the value. this also resets the InCalculation state.
        self.values[node_idx.index()] = CppnNodeValue::Cached(output);

        output
    }
}

/// Represents a Compositional Pattern Producing Network (CPPN)
pub struct Cppn {
    graph: CppnGraph,
    inputs: Vec<CppnNodeIndex>,
    outputs: Vec<CppnNodeIndex>,
    state: CppnState,
}

impl Cppn {
    pub fn new(graph: CppnGraph) -> Cppn {
        let mut inputs = Vec::new();
        let mut outputs = Vec::new();

        for (i, node) in graph.nodes.iter().enumerate() {
            match node.node_type {
                CppnNodeType::Input => {
                    inputs.push(CppnNodeIndex(i));
                }
                CppnNodeType::Output => {
                    outputs.push(CppnNodeIndex(i));
                }
                _ => {}
            }
        }

        let state = CppnState::new(graph.nodes.len());
        Cppn {
            graph: graph,
            inputs: inputs,
            outputs: outputs,
            state: state,
        }
    }

    /// Calculate all outputs
    pub fn calculate(&mut self, inputs: &[&[f64]]) -> Vec<f64> {
        let mut state = &mut self.state;

        state.reset();

        // assign all inputs
        let mut i = 0;
        for input_list in inputs.iter() {
            for &input in input_list.iter() {
                state.set(self.inputs[i], input);
                i += 1;
            }
        }
        assert!(i == self.inputs.len());

        let graph = &self.graph;
        self.outputs
            .iter()
            .map(|&node_idx| state.calculate_output_of_node(graph, node_idx))
            .collect()
    }
}

#[test]
fn test_cycle() {
    use super::Identity;
    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input, Box::new(Identity));
    let h1 = g.add_node(CppnNodeType::Hidden, Box::new(Identity));
    let h2 = g.add_node(CppnNodeType::Hidden, Box::new(Identity));
    assert_eq!(true, g.valid_link(i1, i1).is_err());
    assert_eq!(true, g.valid_link(h1, h1).is_err());

    assert_eq!(true, g.valid_link(h1, i1).is_err());
    assert_eq!(Ok(()), g.valid_link(i1, h1));
    assert_eq!(Ok(()), g.valid_link(i1, h2));
    assert_eq!(Ok(()), g.valid_link(h1, h2));

    g.add_link(i1, h1, 0.0);
    assert_eq!(true, g.would_cycle(h1, i1));
    assert_eq!(false, g.would_cycle(i1, h1));
    assert_eq!(false, g.would_cycle(i1, h2));
    assert_eq!(true, g.would_cycle(i1, i1));
    assert_eq!(false, g.would_cycle(h1, h2));
    assert_eq!(false, g.would_cycle(h2, h1));
    assert_eq!(false, g.would_cycle(h2, i1));

    g.add_link(h1, h2, 0.0);
    assert_eq!(true, g.would_cycle(h2, i1));
    assert_eq!(true, g.would_cycle(h1, i1));
    assert_eq!(true, g.would_cycle(h2, h1));
    assert_eq!(false, g.would_cycle(i1, h2));
}

#[test]
fn test_simple_cppn() {
    use super::Identity;
    use super::bipolar::Linear;

    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input, Box::new(Identity));
    let h1 = g.add_node(CppnNodeType::Hidden, Box::new(Linear));
    let o1 = g.add_node(CppnNodeType::Output, Box::new(Identity));
    g.add_link(i1, h1, 0.5);
    g.add_link(h1, o1, 1.0);

    let mut cppn = Cppn::new(g);

    assert_eq!(vec![0.5 * 0.5], cppn.calculate(&[&[0.5]]));
    assert_eq!(vec![1.0], cppn.calculate(&[&[4.0]]));
    assert_eq!(vec![-1.0], cppn.calculate(&[&[-4.0]]));
}
<|endoftext|>


<commit_before>//! This module contains the machinery necessary to detect infinite loops
//! during const-evaluation by taking snapshots of the state of the interpreter
//! at regular intervals.

// This lives in `interpret` because it needs access to all sots of private state.  However,
// it is not used by the general miri engine, just by CTFE.

use std::hash::{Hash, Hasher};

use rustc::ich::{StableHashingContextProvider, StableHashingContext};
use rustc::mir;
use rustc::mir::interpret::{
    AllocId, Pointer, Scalar,
    Relocations, Allocation, UndefMask,
    EvalResult, EvalErrorKind,
};

use rustc::ty::{self, TyCtxt};
use rustc::ty::layout::Align;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
use syntax::ast::Mutability;
use syntax::source_map::Span;

use super::eval_context::{LocalValue, StackPopCleanup};
use super::{Frame, Memory, Operand, MemPlace, Place, Immediate, ScalarMaybeUndef, LocalState};
use const_eval::CompileTimeInterpreter;

#[derive(Default)]
pub(crate) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir> {
    /// The set of all `EvalSnapshot` *hashes* observed by this detector.
    ///
    /// When a collision occurs in this table, we store the full snapshot in
    /// `snapshots`.
    hashes: FxHashSet<u64>,

    /// The set of all `EvalSnapshot`s observed by this detector.
    ///
    /// An `EvalSnapshot` will only be fully cloned once it has caused a
    /// collision in `hashes`. As a result, the detector must observe at least
    /// *two* full cycles of an infinite loop before it triggers.
    snapshots: FxHashSet<EvalSnapshot<'a, 'mir, 'tcx>>,
}

impl<'a, 'mir, 'tcx> InfiniteLoopDetector<'a, 'mir, 'tcx>
{
    pub fn observe_and_analyze<'b>(
        &mut self,
        tcx: &TyCtxt<'b, 'tcx, 'tcx>,
        span: Span,
        memory: &Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
        stack: &[Frame<'mir, 'tcx>],
    ) -> EvalResult<'tcx, ()> {
        // Compute stack's hash before copying anything
        let mut hcx = tcx.get_stable_hashing_context();
        let mut hasher = StableHasher::<u64>::new();
        stack.hash_stable(&mut hcx, &mut hasher);
        let hash = hasher.finish();

        // Check if we know that hash already
        if self.hashes.is_empty() {
            // FIXME(#49980): make this warning a lint
            tcx.sess.span_warn(span,
                "Constant evaluating a complex constant, this might take some time");
        }
        if self.hashes.insert(hash) {
            // No collision
            return Ok(())
        }

        // We need to make a full copy. NOW things that to get really expensive.
        info!("snapshotting the state of the interpreter");

        if self.snapshots.insert(EvalSnapshot::new(memory, stack)) {
            // Spurious collision or first cycle
            return Ok(())
        }

        // Second cycle
        Err(EvalErrorKind::InfiniteLoop.into())
    }
}

trait SnapshotContext<'a> {
    fn resolve(&'a self, id: &AllocId) -> Option<&'a Allocation>;
}

/// Taking a snapshot of the evaluation context produces a view of
/// the state of the interpreter that is invariant to `AllocId`s.
trait Snapshot<'a, Ctx: SnapshotContext<'a>> {
    type Item;
    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item;
}

macro_rules! __impl_snapshot_field {
    ($field:ident, $ctx:expr) => ($field.snapshot($ctx));
    ($field:ident, $ctx:expr, $delegate:expr) => ($delegate);
}

// This assumes the type has two type parameters, first for the tag (set to `()`),
// then for the id
macro_rules! impl_snapshot_for {
    // FIXME(mark-i-m): Some of these should be `?` rather than `*`.
    (enum $enum_name:ident {
        $( $variant:ident $( ( $($field:ident $(-> $delegate:expr)*),* ) )* ),* $(,)*
    }) => {

        impl<'a, Ctx> self::Snapshot<'a, Ctx> for $enum_name
            where Ctx: self::SnapshotContext<'a>,
        {
            type Item = $enum_name<(), AllocIdSnapshot<'a>>;

            #[inline]
            fn snapshot(&self, __ctx: &'a Ctx) -> Self::Item {
                match *self {
                    $(
                        $enum_name::$variant $( ( $(ref $field),* ) )* =>
                            $enum_name::$variant $(
                                ( $( __impl_snapshot_field!($field, __ctx $(, $delegate)*) ),* ),
                            )*
                    )*
                }
            }
        }
    };

    // FIXME(mark-i-m): same here.
    (struct $struct_name:ident { $($field:ident $(-> $delegate:expr)*),*  $(,)* }) => {
        impl<'a, Ctx> self::Snapshot<'a, Ctx> for $struct_name
            where Ctx: self::SnapshotContext<'a>,
        {
            type Item = $struct_name<(), AllocIdSnapshot<'a>>;

            #[inline]
            fn snapshot(&self, __ctx: &'a Ctx) -> Self::Item {
                let $struct_name {
                    $(ref $field),*
                } = *self;

                $struct_name {
                    $( $field: __impl_snapshot_field!($field, __ctx $(, $delegate)*) ),*
                }
            }
        }
    };
}

impl<'a, Ctx, T> Snapshot<'a, Ctx> for Option<T>
    where Ctx: SnapshotContext<'a>,
          T: Snapshot<'a, Ctx>
{
    type Item = Option<<T as Snapshot<'a, Ctx>>::Item>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        match self {
            Some(x) => Some(x.snapshot(ctx)),
            None => None,
        }
    }
}

#[derive(Eq, PartialEq)]
struct AllocIdSnapshot<'a>(Option<AllocationSnapshot<'a>>);

impl<'a, Ctx> Snapshot<'a, Ctx> for AllocId
    where Ctx: SnapshotContext<'a>,
{
    type Item = AllocIdSnapshot<'a>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        AllocIdSnapshot(ctx.resolve(self).map(|alloc| alloc.snapshot(ctx)))
    }
}

impl_snapshot_for!(struct Pointer {
    alloc_id,
    offset -> *offset, // just copy offset verbatim
    tag -> *tag, // just copy tag
});

impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar
    where Ctx: SnapshotContext<'a>,
{
    type Item = Scalar<(), AllocIdSnapshot<'a>>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        match self {
            Scalar::Ptr(p) => Scalar::Ptr(p.snapshot(ctx)),
            Scalar::Bits{ size, bits } => Scalar::Bits {
                size: *size,
                bits: *bits,
            },
        }
    }
}

impl_snapshot_for!(enum ScalarMaybeUndef {
    Scalar(s),
    Undef,
});

impl_stable_hash_for!(struct ::interpret::MemPlace {
    ptr,
    align,
    meta,
});
impl_snapshot_for!(struct MemPlace {
    ptr,
    meta,
    align -> *align, // just copy alignment verbatim
});

impl_stable_hash_for!(enum ::interpret::Place {
    Ptr(mem_place),
    Local { frame, local },
});
impl<'a, Ctx> Snapshot<'a, Ctx> for Place
    where Ctx: SnapshotContext<'a>,
{
    type Item = Place<(), AllocIdSnapshot<'a>>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        match self {
            Place::Ptr(p) => Place::Ptr(p.snapshot(ctx)),

            Place::Local{ frame, local } => Place::Local{
                frame: *frame,
                local: *local,
            },
        }
    }
}

impl_stable_hash_for!(enum ::interpret::Immediate {
    Scalar(x),
    ScalarPair(x, y),
});
impl_snapshot_for!(enum Immediate {
    Scalar(s),
    ScalarPair(s, t),
});

impl_stable_hash_for!(enum ::interpret::Operand {
    Immediate(x),
    Indirect(x),
});
impl_snapshot_for!(enum Operand {
    Immediate(v),
    Indirect(m),
});

impl_stable_hash_for!(enum ::interpret::LocalState {
    Dead,
    Live(x),
});
impl_snapshot_for!(enum LocalState {
    Live(v),
    Dead,
});

impl<'a, Ctx> Snapshot<'a, Ctx> for Relocations
    where Ctx: SnapshotContext<'a>,
{
    type Item = Relocations<(), AllocIdSnapshot<'a>>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        Relocations::from_presorted(self.iter()
            .map(|(size, ((), id))| (*size, ((), id.snapshot(ctx))))
            .collect())
    }
}

#[derive(Eq, PartialEq)]
struct AllocationSnapshot<'a> {
    bytes: &'a [u8],
    relocations: Relocations<(), AllocIdSnapshot<'a>>,
    undef_mask: &'a UndefMask,
    align: &'a Align,
    mutability: &'a Mutability,
}

impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation
    where Ctx: SnapshotContext<'a>,
{
    type Item = AllocationSnapshot<'a>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        let Allocation { bytes, relocations, undef_mask, align, mutability, extra: () } = self;

        AllocationSnapshot {
            bytes,
            undef_mask,
            align,
            mutability,
            relocations: relocations.snapshot(ctx),
        }
    }
}

impl_stable_hash_for!(enum ::interpret::eval_context::StackPopCleanup {
    Goto(block),
    None { cleanup },
});

#[derive(Eq, PartialEq)]
struct FrameSnapshot<'a, 'tcx: 'a> {
    instance: &'a ty::Instance<'tcx>,
    span: &'a Span,
    return_to_block: &'a StackPopCleanup,
    return_place: Option<Place<(), AllocIdSnapshot<'a>>>,
    locals: IndexVec<mir::Local, LocalState<(), AllocIdSnapshot<'a>>>,
    block: &'a mir::BasicBlock,
    stmt: usize,
}

impl_stable_hash_for!(impl<'mir, 'tcx: 'mir> for struct Frame<'mir, 'tcx> {
    mir,
    instance,
    span,
    return_to_block,
    return_place -> (return_place.as_ref().map(|r| &**r)),
    locals,
    block,
    stmt,
    extra,
});

impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
    where Ctx: SnapshotContext<'a>,
{
    type Item = FrameSnapshot<'a, 'tcx>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        let Frame {
            mir: _,
            instance,
            span,
            return_to_block,
            return_place,
            locals,
            block,
            stmt,
            extra: _,
        } = self;

        FrameSnapshot {
            instance,
            span,
            return_to_block,
            block,
            stmt: *stmt,
            return_place: return_place.map(|r| r.snapshot(ctx)),
            locals: locals.iter().map(|local| local.snapshot(ctx)).collect(),
        }
    }
}

impl<'a, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a LocalValue<'tcx>
    where Ctx: SnapshotContext<'a>,
{
    type Item = LocalState<(), AllocIdSnapshot<'a>>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        self.state.snapshot(ctx)
    }
}

impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for LocalValue<'gcx> {
    fn hash_stable<W: StableHasherResult>(
        &self,
        hcx: &mut StableHashingContext<'a>,
        hasher: &mut StableHasher<W>,
    ) {
        self.state.hash_stable(hcx, hasher);
    }
}

impl<'a, 'b, 'mir, 'tcx: 'a+'mir> SnapshotContext<'b>
    for Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>
{
    fn resolve(&'b self, id: &AllocId) -> Option<&'b Allocation> {
        self.get(*id).ok()
    }
}

/// The virtual machine state during const-evaluation at a given point in time.
/// We assume the `CompileTimeInterpreter` has no interesting extra state that
/// is worth considering here.
struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir> {
    memory: Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
    stack: Vec<Frame<'mir, 'tcx>>,
}

impl<'a, 'mir, 'tcx: 'a + 'mir> EvalSnapshot<'a, 'mir, 'tcx>
{
    fn new(
        memory: &Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
        stack: &[Frame<'mir, 'tcx>]
    ) -> Self {
        EvalSnapshot {
            memory: memory.clone(),
            stack: stack.into(),
        }
    }

    // Used to compare two snapshots
    fn snapshot(&'b self)
        -> Vec<FrameSnapshot<'b, 'tcx>>
    {
        // Start with the stack, iterate and recursively snapshot
        self.stack.iter().map(|frame| frame.snapshot(&self.memory)).collect()
    }

}

impl<'a, 'mir, 'tcx> Hash for EvalSnapshot<'a, 'mir, 'tcx>
{
    fn hash<H: Hasher>(&self, state: &mut H) {
        // Implement in terms of hash stable, so that k1 == k2 -> hash(k1) == hash(k2)
        let mut hcx = self.memory.tcx.get_stable_hashing_context();
        let mut hasher = StableHasher::<u64>::new();
        self.hash_stable(&mut hcx, &mut hasher);
        hasher.finish().hash(state)
    }
}

impl_stable_hash_for!(impl<'tcx, 'b, 'mir> for struct EvalSnapshot<'b, 'mir, 'tcx> {
    // Not hashing memory: Avoid hashing memory all the time during execution
    memory -> _,
    stack,
});

impl<'a, 'mir, 'tcx> Eq for EvalSnapshot<'a, 'mir, 'tcx>
{}

impl<'a, 'mir, 'tcx> PartialEq for EvalSnapshot<'a, 'mir, 'tcx>
{
    fn eq(&self, other: &Self) -> bool {
        // FIXME: This looks to be a *ridicolously expensive* comparison operation.
        // Doesn't this make tons of copies?  Either `snapshot` is very badly named,
        // or it does!
        self.snapshot() == other.snapshot()
    }
}
<commit_msg>Prefer macro over manual implementation<commit_after>//! This module contains the machinery necessary to detect infinite loops
//! during const-evaluation by taking snapshots of the state of the interpreter
//! at regular intervals.

// This lives in `interpret` because it needs access to all sots of private state.  However,
// it is not used by the general miri engine, just by CTFE.

use std::hash::{Hash, Hasher};

use rustc::ich::StableHashingContextProvider;
use rustc::mir;
use rustc::mir::interpret::{
    AllocId, Pointer, Scalar,
    Relocations, Allocation, UndefMask,
    EvalResult, EvalErrorKind,
};

use rustc::ty::{self, TyCtxt};
use rustc::ty::layout::Align;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use syntax::ast::Mutability;
use syntax::source_map::Span;

use super::eval_context::{LocalValue, StackPopCleanup};
use super::{Frame, Memory, Operand, MemPlace, Place, Immediate, ScalarMaybeUndef, LocalState};
use const_eval::CompileTimeInterpreter;

#[derive(Default)]
pub(crate) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir> {
    /// The set of all `EvalSnapshot` *hashes* observed by this detector.
    ///
    /// When a collision occurs in this table, we store the full snapshot in
    /// `snapshots`.
    hashes: FxHashSet<u64>,

    /// The set of all `EvalSnapshot`s observed by this detector.
    ///
    /// An `EvalSnapshot` will only be fully cloned once it has caused a
    /// collision in `hashes`. As a result, the detector must observe at least
    /// *two* full cycles of an infinite loop before it triggers.
    snapshots: FxHashSet<EvalSnapshot<'a, 'mir, 'tcx>>,
}

impl<'a, 'mir, 'tcx> InfiniteLoopDetector<'a, 'mir, 'tcx>
{
    pub fn observe_and_analyze<'b>(
        &mut self,
        tcx: &TyCtxt<'b, 'tcx, 'tcx>,
        span: Span,
        memory: &Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
        stack: &[Frame<'mir, 'tcx>],
    ) -> EvalResult<'tcx, ()> {
        // Compute stack's hash before copying anything
        let mut hcx = tcx.get_stable_hashing_context();
        let mut hasher = StableHasher::<u64>::new();
        stack.hash_stable(&mut hcx, &mut hasher);
        let hash = hasher.finish();

        // Check if we know that hash already
        if self.hashes.is_empty() {
            // FIXME(#49980): make this warning a lint
            tcx.sess.span_warn(span,
                "Constant evaluating a complex constant, this might take some time");
        }
        if self.hashes.insert(hash) {
            // No collision
            return Ok(())
        }

        // We need to make a full copy. NOW things that to get really expensive.
        info!("snapshotting the state of the interpreter");

        if self.snapshots.insert(EvalSnapshot::new(memory, stack)) {
            // Spurious collision or first cycle
            return Ok(())
        }

        // Second cycle
        Err(EvalErrorKind::InfiniteLoop.into())
    }
}

trait SnapshotContext<'a> {
    fn resolve(&'a self, id: &AllocId) -> Option<&'a Allocation>;
}

/// Taking a snapshot of the evaluation context produces a view of
/// the state of the interpreter that is invariant to `AllocId`s.
trait Snapshot<'a, Ctx: SnapshotContext<'a>> {
    type Item;
    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item;
}

macro_rules! __impl_snapshot_field {
    ($field:ident, $ctx:expr) => ($field.snapshot($ctx));
    ($field:ident, $ctx:expr, $delegate:expr) => ($delegate);
}

// This assumes the type has two type parameters, first for the tag (set to `()`),
// then for the id
macro_rules! impl_snapshot_for {
    // FIXME(mark-i-m): Some of these should be `?` rather than `*`.
    (enum $enum_name:ident {
        $( $variant:ident $( ( $($field:ident $(-> $delegate:expr)*),* ) )* ),* $(,)*
    }) => {

        impl<'a, Ctx> self::Snapshot<'a, Ctx> for $enum_name
            where Ctx: self::SnapshotContext<'a>,
        {
            type Item = $enum_name<(), AllocIdSnapshot<'a>>;

            #[inline]
            fn snapshot(&self, __ctx: &'a Ctx) -> Self::Item {
                match *self {
                    $(
                        $enum_name::$variant $( ( $(ref $field),* ) )* =>
                            $enum_name::$variant $(
                                ( $( __impl_snapshot_field!($field, __ctx $(, $delegate)*) ),* ),
                            )*
                    )*
                }
            }
        }
    };

    // FIXME(mark-i-m): same here.
    (struct $struct_name:ident { $($field:ident $(-> $delegate:expr)*),*  $(,)* }) => {
        impl<'a, Ctx> self::Snapshot<'a, Ctx> for $struct_name
            where Ctx: self::SnapshotContext<'a>,
        {
            type Item = $struct_name<(), AllocIdSnapshot<'a>>;

            #[inline]
            fn snapshot(&self, __ctx: &'a Ctx) -> Self::Item {
                let $struct_name {
                    $(ref $field),*
                } = *self;

                $struct_name {
                    $( $field: __impl_snapshot_field!($field, __ctx $(, $delegate)*) ),*
                }
            }
        }
    };
}

impl<'a, Ctx, T> Snapshot<'a, Ctx> for Option<T>
    where Ctx: SnapshotContext<'a>,
          T: Snapshot<'a, Ctx>
{
    type Item = Option<<T as Snapshot<'a, Ctx>>::Item>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        match self {
            Some(x) => Some(x.snapshot(ctx)),
            None => None,
        }
    }
}

#[derive(Eq, PartialEq)]
struct AllocIdSnapshot<'a>(Option<AllocationSnapshot<'a>>);

impl<'a, Ctx> Snapshot<'a, Ctx> for AllocId
    where Ctx: SnapshotContext<'a>,
{
    type Item = AllocIdSnapshot<'a>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        AllocIdSnapshot(ctx.resolve(self).map(|alloc| alloc.snapshot(ctx)))
    }
}

impl_snapshot_for!(struct Pointer {
    alloc_id,
    offset -> *offset, // just copy offset verbatim
    tag -> *tag, // just copy tag
});

impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar
    where Ctx: SnapshotContext<'a>,
{
    type Item = Scalar<(), AllocIdSnapshot<'a>>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        match self {
            Scalar::Ptr(p) => Scalar::Ptr(p.snapshot(ctx)),
            Scalar::Bits{ size, bits } => Scalar::Bits {
                size: *size,
                bits: *bits,
            },
        }
    }
}

impl_snapshot_for!(enum ScalarMaybeUndef {
    Scalar(s),
    Undef,
});

impl_stable_hash_for!(struct ::interpret::MemPlace {
    ptr,
    align,
    meta,
});
impl_snapshot_for!(struct MemPlace {
    ptr,
    meta,
    align -> *align, // just copy alignment verbatim
});

impl_stable_hash_for!(enum ::interpret::Place {
    Ptr(mem_place),
    Local { frame, local },
});
impl<'a, Ctx> Snapshot<'a, Ctx> for Place
    where Ctx: SnapshotContext<'a>,
{
    type Item = Place<(), AllocIdSnapshot<'a>>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        match self {
            Place::Ptr(p) => Place::Ptr(p.snapshot(ctx)),

            Place::Local{ frame, local } => Place::Local{
                frame: *frame,
                local: *local,
            },
        }
    }
}

impl_stable_hash_for!(enum ::interpret::Immediate {
    Scalar(x),
    ScalarPair(x, y),
});
impl_snapshot_for!(enum Immediate {
    Scalar(s),
    ScalarPair(s, t),
});

impl_stable_hash_for!(enum ::interpret::Operand {
    Immediate(x),
    Indirect(x),
});
impl_snapshot_for!(enum Operand {
    Immediate(v),
    Indirect(m),
});

impl_stable_hash_for!(enum ::interpret::LocalState {
    Dead,
    Live(x),
});
impl_snapshot_for!(enum LocalState {
    Live(v),
    Dead,
});

impl<'a, Ctx> Snapshot<'a, Ctx> for Relocations
    where Ctx: SnapshotContext<'a>,
{
    type Item = Relocations<(), AllocIdSnapshot<'a>>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        Relocations::from_presorted(self.iter()
            .map(|(size, ((), id))| (*size, ((), id.snapshot(ctx))))
            .collect())
    }
}

#[derive(Eq, PartialEq)]
struct AllocationSnapshot<'a> {
    bytes: &'a [u8],
    relocations: Relocations<(), AllocIdSnapshot<'a>>,
    undef_mask: &'a UndefMask,
    align: &'a Align,
    mutability: &'a Mutability,
}

impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation
    where Ctx: SnapshotContext<'a>,
{
    type Item = AllocationSnapshot<'a>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        let Allocation { bytes, relocations, undef_mask, align, mutability, extra: () } = self;

        AllocationSnapshot {
            bytes,
            undef_mask,
            align,
            mutability,
            relocations: relocations.snapshot(ctx),
        }
    }
}

impl_stable_hash_for!(enum ::interpret::eval_context::StackPopCleanup {
    Goto(block),
    None { cleanup },
});

#[derive(Eq, PartialEq)]
struct FrameSnapshot<'a, 'tcx: 'a> {
    instance: &'a ty::Instance<'tcx>,
    span: &'a Span,
    return_to_block: &'a StackPopCleanup,
    return_place: Option<Place<(), AllocIdSnapshot<'a>>>,
    locals: IndexVec<mir::Local, LocalState<(), AllocIdSnapshot<'a>>>,
    block: &'a mir::BasicBlock,
    stmt: usize,
}

impl_stable_hash_for!(impl<'mir, 'tcx: 'mir> for struct Frame<'mir, 'tcx> {
    mir,
    instance,
    span,
    return_to_block,
    return_place -> (return_place.as_ref().map(|r| &**r)),
    locals,
    block,
    stmt,
    extra,
});

impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
    where Ctx: SnapshotContext<'a>,
{
    type Item = FrameSnapshot<'a, 'tcx>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        let Frame {
            mir: _,
            instance,
            span,
            return_to_block,
            return_place,
            locals,
            block,
            stmt,
            extra: _,
        } = self;

        FrameSnapshot {
            instance,
            span,
            return_to_block,
            block,
            stmt: *stmt,
            return_place: return_place.map(|r| r.snapshot(ctx)),
            locals: locals.iter().map(|local| local.snapshot(ctx)).collect(),
        }
    }
}

impl<'a, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a LocalValue<'tcx>
    where Ctx: SnapshotContext<'a>,
{
    type Item = LocalState<(), AllocIdSnapshot<'a>>;

    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
        self.state.snapshot(ctx)
    }
}

impl_stable_hash_for!(struct LocalValue<'tcx> {
    state,
    layout -> _,
});

impl<'a, 'b, 'mir, 'tcx: 'a+'mir> SnapshotContext<'b>
    for Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>
{
    fn resolve(&'b self, id: &AllocId) -> Option<&'b Allocation> {
        self.get(*id).ok()
    }
}

/// The virtual machine state during const-evaluation at a given point in time.
/// We assume the `CompileTimeInterpreter` has no interesting extra state that
/// is worth considering here.
struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir> {
    memory: Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
    stack: Vec<Frame<'mir, 'tcx>>,
}

impl<'a, 'mir, 'tcx: 'a + 'mir> EvalSnapshot<'a, 'mir, 'tcx>
{
    fn new(
        memory: &Memory<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
        stack: &[Frame<'mir, 'tcx>]
    ) -> Self {
        EvalSnapshot {
            memory: memory.clone(),
            stack: stack.into(),
        }
    }

    // Used to compare two snapshots
    fn snapshot(&'b self)
        -> Vec<FrameSnapshot<'b, 'tcx>>
    {
        // Start with the stack, iterate and recursively snapshot
        self.stack.iter().map(|frame| frame.snapshot(&self.memory)).collect()
    }

}

impl<'a, 'mir, 'tcx> Hash for EvalSnapshot<'a, 'mir, 'tcx>
{
    fn hash<H: Hasher>(&self, state: &mut H) {
        // Implement in terms of hash stable, so that k1 == k2 -> hash(k1) == hash(k2)
        let mut hcx = self.memory.tcx.get_stable_hashing_context();
        let mut hasher = StableHasher::<u64>::new();
        self.hash_stable(&mut hcx, &mut hasher);
        hasher.finish().hash(state)
    }
}

impl_stable_hash_for!(impl<'tcx, 'b, 'mir> for struct EvalSnapshot<'b, 'mir, 'tcx> {
    // Not hashing memory: Avoid hashing memory all the time during execution
    memory -> _,
    stack,
});

impl<'a, 'mir, 'tcx> Eq for EvalSnapshot<'a, 'mir, 'tcx>
{}

impl<'a, 'mir, 'tcx> PartialEq for EvalSnapshot<'a, 'mir, 'tcx>
{
    fn eq(&self, other: &Self) -> bool {
        // FIXME: This looks to be a *ridicolously expensive* comparison operation.
        // Doesn't this make tons of copies?  Either `snapshot` is very badly named,
        // or it does!
        self.snapshot() == other.snapshot()
    }
}
<|endoftext|>


<commit_before>use super::{span_of_attrs, Pass};
use crate::clean::*;
use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::html::markdown::opts;
use core::ops::Range;
use pulldown_cmark::{Event, LinkType, Parser, Tag};
use regex::Regex;
use rustc_errors::Applicability;

crate const CHECK_NON_AUTOLINKS: Pass = Pass {
    name: "check-non-autolinks",
    run: check_non_autolinks,
    description: "detects URLs that could be linkified",
};

const URL_REGEX: &str = concat!(
    r"https?://",                          // url scheme
    r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains
    r"[a-zA-Z]{2,63}",                     // root domain
    r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)"      // optional query or url fragments
);

struct NonAutolinksLinter<'a, 'tcx> {
    cx: &'a mut DocContext<'tcx>,
    regex: Regex,
}

impl<'a, 'tcx> NonAutolinksLinter<'a, 'tcx> {
    fn find_raw_urls(
        &self,
        text: &str,
        range: Range<usize>,
        f: &impl Fn(&DocContext<'_>, &str, &str, Range<usize>),
    ) {
        // For now, we only check "full" URLs (meaning, starting with "http://" or "https://").
        for match_ in self.regex.find_iter(&text) {
            let url = match_.as_str();
            let url_range = match_.range();
            f(
                self.cx,
                "this URL is not a hyperlink",
                url,
                Range { start: range.start + url_range.start, end: range.start + url_range.end },
            );
        }
    }
}

crate fn check_non_autolinks(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
    if !cx.tcx.sess.is_nightly_build() {
        krate
    } else {
        let mut coll =
            NonAutolinksLinter { cx, regex: Regex::new(URL_REGEX).expect("failed to build regex") };

        coll.fold_crate(krate)
    }
}

impl<'a, 'tcx> DocFolder for NonAutolinksLinter<'a, 'tcx> {
    fn fold_item(&mut self, item: Item) -> Option<Item> {
        let hir_id = match DocContext::as_local_hir_id(self.cx.tcx, item.def_id) {
            Some(hir_id) => hir_id,
            None => {
                // If non-local, no need to check anything.
                return Some(self.fold_item_recur(item));
            }
        };
        let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
        if !dox.is_empty() {
            let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| {
                let sp = super::source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
                    .or_else(|| span_of_attrs(&item.attrs))
                    .unwrap_or(item.span.inner());
                cx.tcx.struct_span_lint_hir(crate::lint::NON_AUTOLINKS, hir_id, sp, |lint| {
                    lint.build(msg)
                        .span_suggestion(
                            sp,
                            "use an automatic link instead",
                            format!("<{}>", url),
                            Applicability::MachineApplicable,
                        )
                        .emit()
                });
            };

            let mut p = Parser::new_ext(&dox, opts()).into_offset_iter();

            while let Some((event, range)) = p.next() {
                match event {
                    Event::Start(Tag::Link(kind, _, _)) => {
                        let ignore = matches!(kind, LinkType::Autolink | LinkType::Email);
                        let mut title = String::new();

                        while let Some((event, range)) = p.next() {
                            match event {
                                Event::End(Tag::Link(_, url, _)) => {
                                    // NOTE: links cannot be nested, so we don't need to
                                    // check `kind`
                                    if url.as_ref() == title && !ignore && self.regex.is_match(&url)
                                    {
                                        report_diag(
                                            self.cx,
                                            "unneeded long form for URL",
                                            &url,
                                            range,
                                        );
                                    }
                                    break;
                                }
                                Event::Text(s) if !ignore => title.push_str(&s),
                                _ => {}
                            }
                        }
                    }
                    Event::Text(s) => self.find_raw_urls(&s, range, &report_diag),
                    Event::Start(Tag::CodeBlock(_)) => {
                        // We don't want to check the text inside the code blocks.
                        while let Some((event, _)) = p.next() {
                            match event {
                                Event::End(Tag::CodeBlock(_)) => break,
                                _ => {}
                            }
                        }
                    }
                    _ => {}
                }
            }
        }

        Some(self.fold_item_recur(item))
    }
}
<commit_msg>Stabilize `non_autolinks` lint<commit_after>use super::{span_of_attrs, Pass};
use crate::clean::*;
use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::html::markdown::opts;
use core::ops::Range;
use pulldown_cmark::{Event, LinkType, Parser, Tag};
use regex::Regex;
use rustc_errors::Applicability;

crate const CHECK_NON_AUTOLINKS: Pass = Pass {
    name: "check-non-autolinks",
    run: check_non_autolinks,
    description: "detects URLs that could be linkified",
};

const URL_REGEX: &str = concat!(
    r"https?://",                          // url scheme
    r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains
    r"[a-zA-Z]{2,63}",                     // root domain
    r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)"      // optional query or url fragments
);

struct NonAutolinksLinter<'a, 'tcx> {
    cx: &'a mut DocContext<'tcx>,
    regex: Regex,
}

impl<'a, 'tcx> NonAutolinksLinter<'a, 'tcx> {
    fn find_raw_urls(
        &self,
        text: &str,
        range: Range<usize>,
        f: &impl Fn(&DocContext<'_>, &str, &str, Range<usize>),
    ) {
        // For now, we only check "full" URLs (meaning, starting with "http://" or "https://").
        for match_ in self.regex.find_iter(&text) {
            let url = match_.as_str();
            let url_range = match_.range();
            f(
                self.cx,
                "this URL is not a hyperlink",
                url,
                Range { start: range.start + url_range.start, end: range.start + url_range.end },
            );
        }
    }
}

crate fn check_non_autolinks(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
    NonAutolinksLinter::new(cx).fold_crate(krate)
}

impl<'a, 'tcx> DocFolder for NonAutolinksLinter<'a, 'tcx> {
    fn fold_item(&mut self, item: Item) -> Option<Item> {
        let hir_id = match DocContext::as_local_hir_id(self.cx.tcx, item.def_id) {
            Some(hir_id) => hir_id,
            None => {
                // If non-local, no need to check anything.
                return Some(self.fold_item_recur(item));
            }
        };
        let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
        if !dox.is_empty() {
            let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| {
                let sp = super::source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
                    .or_else(|| span_of_attrs(&item.attrs))
                    .unwrap_or(item.span.inner());
                cx.tcx.struct_span_lint_hir(crate::lint::NON_AUTOLINKS, hir_id, sp, |lint| {
                    lint.build(msg)
                        .span_suggestion(
                            sp,
                            "use an automatic link instead",
                            format!("<{}>", url),
                            Applicability::MachineApplicable,
                        )
                        .emit()
                });
            };

            let mut p = Parser::new_ext(&dox, opts()).into_offset_iter();

            while let Some((event, range)) = p.next() {
                match event {
                    Event::Start(Tag::Link(kind, _, _)) => {
                        let ignore = matches!(kind, LinkType::Autolink | LinkType::Email);
                        let mut title = String::new();

                        while let Some((event, range)) = p.next() {
                            match event {
                                Event::End(Tag::Link(_, url, _)) => {
                                    // NOTE: links cannot be nested, so we don't need to
                                    // check `kind`
                                    if url.as_ref() == title && !ignore && self.regex.is_match(&url)
                                    {
                                        report_diag(
                                            self.cx,
                                            "unneeded long form for URL",
                                            &url,
                                            range,
                                        );
                                    }
                                    break;
                                }
                                Event::Text(s) if !ignore => title.push_str(&s),
                                _ => {}
                            }
                        }
                    }
                    Event::Text(s) => self.find_raw_urls(&s, range, &report_diag),
                    Event::Start(Tag::CodeBlock(_)) => {
                        // We don't want to check the text inside the code blocks.
                        while let Some((event, _)) = p.next() {
                            match event {
                                Event::End(Tag::CodeBlock(_)) => break,
                                _ => {}
                            }
                        }
                    }
                    _ => {}
                }
            }
        }

        Some(self.fold_item_recur(item))
    }
}
<|endoftext|>


<commit_before>use glib_sys::{g_log_structured_array, GLogField, G_LOG_LEVEL_CRITICAL, G_LOG_LEVEL_WARNING};

/*
  G_LOG_LEVEL_CRITICAL          = 1 << 3,
  G_LOG_LEVEL_WARNING           = 1 << 4,

#define g_critical(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)
#define g_warning(...)  g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)
  GLogField fields[] =
    {
      { "PRIORITY", log_level_to_priority (log_level), -1 },
      { "CODE_FILE", file, -1 },
      { "CODE_LINE", line, -1 },
      { "CODE_FUNC", func, -1 },
      /* Filled in later: */
      { "MESSAGE", NULL, -1 },
      /* If @log_domain is %NULL, we will not pass this field: */
      { "GLIB_DOMAIN", log_domain, -1 },
    };

  g_log_structured_array (log_level, fields, n_fields);
 */

/// Helper for `rsvg_g_warning` and `rsvg_g_critical`
///
/// This simulates what in C would be a call to the g_warning() or g_critical()
/// macros, but with the underlying function g_log_structured_array().
///
/// If the implementation of g_warning() or g_critical() changes, we'll have
/// to change this function.
fn rsvg_g_log(level: glib_sys::GLogLevelFlags, msg: &str) {
    // stolen from gmessages.c:log_level_to_priority()
    let priority = match level {
        G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL => b"4\0",
        _ => unreachable!("please add another log level priority to rsvg_g_log()"),
    };

    // Glib's g_log_structured_standard() adds a few more fields for the source
    // file, line number, etc., but those are not terribly useful without a stack
    // trace.  So, we'll omit them.
    let fields = [
        GLogField {
            key: b"PRIORITY\0" as *const u8 as *const _,
            value: priority as *const u8 as *const _,
            length: -1,
        },
        GLogField {
            key: b"MESSAGE\0" as *const u8 as *const _,
            value: msg.as_ptr() as *const _,
            length: msg.len() as _,
        },
        // This is the G_LOG_DOMAIN set from the Makefile
        GLogField {
            key: b"GLIB_DOMAIN\0" as *const u8 as *const _,
            value: b"librsvg\0" as *const u8 as *const _,
            length: -1,
        },
    ];

    unsafe {
        g_log_structured_array(level, fields.as_ptr(), fields.len());
    }
}

pub fn rsvg_g_warning(msg: &str) {
    rsvg_g_log(glib_sys::G_LOG_LEVEL_WARNING, msg);
}

pub fn rsvg_g_critical(msg: &str) {
    rsvg_g_log(glib_sys::G_LOG_LEVEL_CRITICAL, msg);
}

// Once Rust has a function! macro that gives us the current function name, we
// can remove the $func_name argument.
#[macro_export]
macro_rules! rsvg_return_if_fail {
    {
        $func_name:ident;
        $($condition:expr,)+
    } => {
        $(
            if !$condition {
                glib_sys::g_return_if_fail_warning(
                    b"librsvg\0" as *const u8 as *const _,
                    concat!(stringify!($func_name), "\0").as_ptr() as *const _,
                    concat!(stringify!($condition), "\0").as_ptr() as *const _,
                );
                return;
            }
        )+
    }
}

#[macro_export]
macro_rules! rsvg_return_val_if_fail {
    {
        $func_name:ident => $retval:expr;
        $($condition:expr,)+
    } => {
        $(
            if !$condition {
                glib_sys::g_return_if_fail_warning(
                    b"librsvg\0" as *const u8 as *const _,
                    concat!(stringify!($func_name), "\0").as_ptr() as *const _,
                    concat!(stringify!($condition), "\0").as_ptr() as *const _,
                );
                return $retval;
            }
        )+
    }
}
<commit_msg>(#592): Fix invalid argument to g_log_structured_array()<commit_after>use glib::translate::*;
use glib_sys::{g_log_structured_array, GLogField, G_LOG_LEVEL_CRITICAL, G_LOG_LEVEL_WARNING};
use libc;

/*
  G_LOG_LEVEL_CRITICAL          = 1 << 3,
  G_LOG_LEVEL_WARNING           = 1 << 4,

#define g_critical(...) g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)
#define g_warning(...)  g_log_structured_standard (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, \
                                                   __FILE__, G_STRINGIFY (__LINE__), \
                                                   G_STRFUNC, __VA_ARGS__)
  GLogField fields[] =
    {
      { "PRIORITY", log_level_to_priority (log_level), -1 },
      { "CODE_FILE", file, -1 },
      { "CODE_LINE", line, -1 },
      { "CODE_FUNC", func, -1 },
      /* Filled in later: */
      { "MESSAGE", NULL, -1 },
      /* If @log_domain is %NULL, we will not pass this field: */
      { "GLIB_DOMAIN", log_domain, -1 },
    };

  g_log_structured_array (log_level, fields, n_fields);
 */

/// Helper for `rsvg_g_warning` and `rsvg_g_critical`
///
/// This simulates what in C would be a call to the g_warning() or g_critical()
/// macros, but with the underlying function g_log_structured_array().
///
/// If the implementation of g_warning() or g_critical() changes, we'll have
/// to change this function.
fn rsvg_g_log(level: glib_sys::GLogLevelFlags, msg: &str) {
    // stolen from gmessages.c:log_level_to_priority()
    let priority = match level {
        G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL => b"4\0",
        _ => unreachable!("please add another log level priority to rsvg_g_log()"),
    };

    let c_msg = msg.to_glib_none();
    let c_char_msg: *const libc::c_char = c_msg.0;

    // Glib's g_log_structured_standard() adds a few more fields for the source
    // file, line number, etc., but those are not terribly useful without a stack
    // trace.  So, we'll omit them.
    let fields = [
        GLogField {
            key: b"PRIORITY\0" as *const u8 as *const _,
            value: priority as *const u8 as *const _,
            length: -1,
        },
        GLogField {
            key: b"MESSAGE\0" as *const u8 as *const _,
            value: c_char_msg as *const _,
            length: msg.len() as _,
        },
        // This is the G_LOG_DOMAIN set from the Makefile
        GLogField {
            key: b"GLIB_DOMAIN\0" as *const u8 as *const _,
            value: b"librsvg\0" as *const u8 as *const _,
            length: -1,
        },
    ];

    unsafe {
        g_log_structured_array(level, fields.as_ptr(), fields.len());
    }
}

pub fn rsvg_g_warning(msg: &str) {
    rsvg_g_log(glib_sys::G_LOG_LEVEL_WARNING, msg);
}

pub fn rsvg_g_critical(msg: &str) {
    rsvg_g_log(glib_sys::G_LOG_LEVEL_CRITICAL, msg);
}

// Once Rust has a function! macro that gives us the current function name, we
// can remove the $func_name argument.
#[macro_export]
macro_rules! rsvg_return_if_fail {
    {
        $func_name:ident;
        $($condition:expr,)+
    } => {
        $(
            if !$condition {
                glib_sys::g_return_if_fail_warning(
                    b"librsvg\0" as *const u8 as *const _,
                    concat!(stringify!($func_name), "\0").as_ptr() as *const _,
                    concat!(stringify!($condition), "\0").as_ptr() as *const _,
                );
                return;
            }
        )+
    }
}

#[macro_export]
macro_rules! rsvg_return_val_if_fail {
    {
        $func_name:ident => $retval:expr;
        $($condition:expr,)+
    } => {
        $(
            if !$condition {
                glib_sys::g_return_if_fail_warning(
                    b"librsvg\0" as *const u8 as *const _,
                    concat!(stringify!($func_name), "\0").as_ptr() as *const _,
                    concat!(stringify!($condition), "\0").as_ptr() as *const _,
                );
                return $retval;
            }
        )+
    }
}
<|endoftext|>


<commit_before><commit_msg>clippy: get_unwrap<commit_after><|endoftext|>