<commit_before>use std::path::Path;

use regex::Regex;

use crate::rustc_stderr::Level;

use color_eyre::eyre::{bail, ensure, eyre, Result};

#[cfg(test)]
mod tests;

/// This crate supports various magic comments that get parsed as file-specific
/// configuration values. This struct parses them all in one go and then they
/// get processed by their respective use sites.
#[derive(Default, Debug)]
pub(crate) struct Comments {
    /// List of revision names to execute. Can only be speicified once
    pub revisions: Option<Vec<String>>,
    /// Don't run this test if any of these filters apply
    pub ignore: Vec<Condition>,
    /// Only run this test if all of these filters apply
    pub only: Vec<Condition>,
    /// Generate one .stderr file per bit width, by prepending with `.64bit` and similar
    pub stderr_per_bitwidth: bool,
    /// Additional flags to pass to the executable
    pub compile_flags: Vec<String>,
    /// Additional env vars to set for the executable
    pub env_vars: Vec<(String, String)>,
    /// Normalizations to apply to the stderr output before emitting it to disk
    pub normalize_stderr: Vec<(Regex, String)>,
    /// An arbitrary pattern to look for in the stderr.
    pub error_pattern: Option<(Pattern, usize)>,
    pub error_matches: Vec<ErrorMatch>,
    /// Ignore diagnostics below this level.
    /// `None` means pick the lowest level from the `error_pattern`s.
    pub require_annotations_for_level: Option<Level>,
}

/// The conditions used for "ignore" and "only" filters.
#[derive(Debug)]
pub(crate) enum Condition {
    /// The given string must appear in the target.
    Target(String),
    /// Tests that the bitwidth is the given one.
    Bitwidth(u8),
    /// Tests that the target is the host.
    OnHost,
}

#[derive(Debug, Clone)]
pub(crate) enum Pattern {
    SubString(String),
    Regex(Regex),
}

#[derive(Debug)]
pub(crate) struct ErrorMatch {
    pub pattern: Pattern,
    pub revision: Option<String>,
    pub level: Level,
    /// The line where the message was defined, for reporting issues with it (e.g. in case it wasn't found).
    pub definition_line: usize,
    /// The line this pattern is expecting to find a message in.
    pub line: usize,
}

impl Condition {
    fn parse(c: &str) -> Self {
        match c {
            "on-host" => return Condition::OnHost,
            _ => {}
        }
        if let Some(bits) = c.strip_suffix("bit") {
            let bits: u8 = bits.parse().expect(
                "ignore/only filter ending in 'bit' must be of the form 'Nbit' for some integer N",
            );
            Condition::Bitwidth(bits)
        } else {
            Condition::Target(c.to_owned())
        }
    }
}

impl Comments {
    pub(crate) fn parse_file(path: &Path) -> Result<Self> {
        let content = std::fs::read_to_string(path)?;
        Self::parse(path, &content)
    }

    /// Parse comments in `content`.
    /// `path` is only used to emit diagnostics if parsing fails.
    pub(crate) fn parse(path: &Path, content: &str) -> Result<Self> {
        let mut this = Self::default();

        let mut fallthrough_to = None; // The line that a `|` will refer to.
        for (l, line) in content.lines().enumerate() {
            let l = l + 1; // enumerate starts at 0, but line numbers start at 1
            this.parse_checked_line(l, &mut fallthrough_to, line).map_err(|err| {
                err.wrap_err(format!("{}:{l}: failed to parse annotation", path.display()))
            })?;
        }
        Ok(this)
    }

    fn parse_checked_line(
        &mut self,
        l: usize,
        fallthrough_to: &mut Option<usize>,
        line: &str,
    ) -> Result<()> {
        if let Some((_, command)) = line.split_once("//@") {
            self.parse_command(command.trim(), l)
        } else if let Some((_, pattern)) = line.split_once("//~") {
            self.parse_pattern(pattern, fallthrough_to, l)
        } else if let Some((_, pattern)) = line.split_once("//[") {
            self.parse_revisioned_pattern(pattern, fallthrough_to, l)
        } else {
            *fallthrough_to = None;
            Ok(())
        }
    }

    fn parse_command(&mut self, command: &str, l: usize) -> Result<()> {
        // Commands are letters or dashes, grab everything until the first character that is neither of those.
        let (command, args) =
            match command.chars().position(|c: char| !c.is_alphanumeric() && c != '-') {
                None => (command, ""),
                Some(i) => {
                    let (command, args) = command.split_at(i);
                    let mut args = args.chars();
                    // Commands are separated from their arguments by ':' or ' '
                    let next = args
                        .next()
                        .expect("the `position` above guarantees that there is at least one char");
                    ensure!(next == ':', "test command must be followed by : (or end the line)");
                    (command, args.as_str().trim())
                }
            };

        match command {
            "revisions" => {
                ensure!(self.revisions.is_none(), "cannot specifiy revisions twice");
                self.revisions = Some(args.split_whitespace().map(|s| s.to_string()).collect());
            }
            "compile-flags" => {
                self.compile_flags.extend(args.split_whitespace().map(|s| s.to_string()));
            }
            "rustc-env" =>
                for env in args.split_whitespace() {
                    let (k, v) = env.split_once('=').ok_or_else(|| {
                        eyre!("environment variables must be key/value pairs separated by a `=`")
                    })?;
                    self.env_vars.push((k.to_string(), v.to_string()));
                },
            "normalize-stderr-test" => {
                /// Parses a string literal. `s` has to start with `"`; everything until the next `"` is
                /// returned in the first component. `\` can be used to escape arbitrary character.
                /// Second return component is the rest of the string with leading whitespace removed.
                fn parse_str(s: &str) -> Result<(&str, &str)> {
                    let mut chars = s.char_indices();
                    match chars.next().ok_or_else(|| eyre!("missing arguments"))?.1 {
                        '"' => {
                            let s = chars.as_str();
                            let mut escaped = false;
                            for (i, c) in chars {
                                if escaped {
                                    // Accept any character as literal after a `\`.
                                    escaped = false;
                                } else if c == '"' {
                                    return Ok((&s[..(i - 1)], s[i..].trim_start()));
                                } else {
                                    escaped = c == '\\';
                                }
                            }
                            bail!("no closing quotes found for {s}")
                        }
                        c => bail!("expected '\"', got {c}"),
                    }
                }

                let (from, rest) = parse_str(args)?;

                let to = rest.strip_prefix("->").ok_or_else(|| {
                    eyre!("normalize-stderr-test needs a pattern and replacement separated by `->`")
                })?.trim_start();
                let (to, rest) = parse_str(to)?;

                ensure!(rest.is_empty(), "trailing text after pattern replacement: {rest}");

                let from = Regex::new(from)?;
                self.normalize_stderr.push((from, to.to_string()));
            }
            "error-pattern" => {
                ensure!(
                    self.error_pattern.is_none(),
                    "cannot specifiy error_pattern twice, previous: {:?}",
                    self.error_pattern
                );
                self.error_pattern = Some((Pattern::parse(args.trim())?, l));
            }
            "stderr-per-bitwidth" => {
                // args are ignored (can be used as comment)
                ensure!(!self.stderr_per_bitwidth, "cannot specifiy stderr-per-bitwidth twice");
                self.stderr_per_bitwidth = true;
            }
            "require-annotations-for-level" => {
                ensure!(
                    self.require_annotations_for_level.is_none(),
                    "cannot specify `require-annotations-for-level` twice"
                );
                self.require_annotations_for_level = Some(args.trim().parse()?);
            }
            command => {
                if let Some(s) = command.strip_prefix("ignore-") {
                    // args are ignored (can be sue as comment)
                    self.ignore.push(Condition::parse(s));
                    return Ok(());
                }

                if let Some(s) = command.strip_prefix("only-") {
                    // args are ignored (can be sue as comment)
                    self.only.push(Condition::parse(s));
                    return Ok(());
                }
                bail!("unknown command {command}");
            }
        }

        Ok(())
    }

    fn parse_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        self.parse_pattern_inner(pattern, fallthrough_to, None, l)
    }

    fn parse_revisioned_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        let (revision, pattern) =
            pattern.split_once(']').ok_or_else(|| eyre!("`//[` without corresponding `]`"))?;
        if let Some(pattern) = pattern.strip_prefix('~') {
            self.parse_pattern_inner(pattern, fallthrough_to, Some(revision.to_owned()), l)
        } else {
            bail!("revisioned pattern must have `~` following the `]`");
        }
    }

    // parse something like (?P<offset>\||[\^]+)? *(?P<level>ERROR|HELP|WARN|NOTE): (?P<text>.*)
    fn parse_pattern_inner(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        revision: Option<String>,
        l: usize,
    ) -> Result<()> {
        let (match_line, pattern) =
            match pattern.chars().next().ok_or_else(|| eyre!("no pattern specified"))? {
                '|' =>
                    (
                        *fallthrough_to
                            .as_mut()
                            .ok_or_else(|| eyre!("`//~|` pattern without preceding line"))?,
                        &pattern[1..],
                    ),
                '^' => {
                    let offset = pattern.chars().take_while(|&c| c == '^').count();
                    (l - offset, &pattern[offset..])
                }
                _ => (l, pattern),
            };

        let pattern = pattern.trim_start();
        let offset = pattern
            .chars()
            .position(|c| !matches!(c, 'A'..='Z' | 'a'..='z'))
            .ok_or_else(|| eyre!("pattern without level"))?;

        let level = pattern[..offset].parse()?;
        let pattern = &pattern[offset..];
        let pattern = pattern.strip_prefix(':').ok_or_else(|| eyre!("no `:` after level found"))?;

        let pattern = pattern.trim();

        ensure!(!pattern.is_empty(), "no pattern specified");

        let pattern = Pattern::parse(pattern)?;

        *fallthrough_to = Some(match_line);

        self.error_matches.push(ErrorMatch {
            pattern,
            revision,
            level,
            definition_line: l,
            line: match_line,
        });

        Ok(())
    }
}

impl Pattern {
    pub(crate) fn matches(&self, message: &str) -> bool {
        match self {
            Pattern::SubString(s) => message.contains(s),
            Pattern::Regex(r) => r.is_match(message),
        }
    }

    pub(crate) fn parse(pattern: &str) -> Result<Self> {
        if let Some(pattern) = pattern.strip_prefix('/') {
            let regex =
                pattern.strip_suffix('/').ok_or_else(|| eyre!("regex must end with `/`"))?;
            Ok(Pattern::Regex(Regex::new(regex)?))
        } else {
            Ok(Pattern::SubString(pattern.to_string()))
        }
    }
}
<commit_msg>clippy<commit_after>use std::path::Path;

use regex::Regex;

use crate::rustc_stderr::Level;

use color_eyre::eyre::{bail, ensure, eyre, Result};

#[cfg(test)]
mod tests;

/// This crate supports various magic comments that get parsed as file-specific
/// configuration values. This struct parses them all in one go and then they
/// get processed by their respective use sites.
#[derive(Default, Debug)]
pub(crate) struct Comments {
    /// List of revision names to execute. Can only be speicified once
    pub revisions: Option<Vec<String>>,
    /// Don't run this test if any of these filters apply
    pub ignore: Vec<Condition>,
    /// Only run this test if all of these filters apply
    pub only: Vec<Condition>,
    /// Generate one .stderr file per bit width, by prepending with `.64bit` and similar
    pub stderr_per_bitwidth: bool,
    /// Additional flags to pass to the executable
    pub compile_flags: Vec<String>,
    /// Additional env vars to set for the executable
    pub env_vars: Vec<(String, String)>,
    /// Normalizations to apply to the stderr output before emitting it to disk
    pub normalize_stderr: Vec<(Regex, String)>,
    /// An arbitrary pattern to look for in the stderr.
    pub error_pattern: Option<(Pattern, usize)>,
    pub error_matches: Vec<ErrorMatch>,
    /// Ignore diagnostics below this level.
    /// `None` means pick the lowest level from the `error_pattern`s.
    pub require_annotations_for_level: Option<Level>,
}

/// The conditions used for "ignore" and "only" filters.
#[derive(Debug)]
pub(crate) enum Condition {
    /// The given string must appear in the target.
    Target(String),
    /// Tests that the bitwidth is the given one.
    Bitwidth(u8),
    /// Tests that the target is the host.
    OnHost,
}

#[derive(Debug, Clone)]
pub(crate) enum Pattern {
    SubString(String),
    Regex(Regex),
}

#[derive(Debug)]
pub(crate) struct ErrorMatch {
    pub pattern: Pattern,
    pub revision: Option<String>,
    pub level: Level,
    /// The line where the message was defined, for reporting issues with it (e.g. in case it wasn't found).
    pub definition_line: usize,
    /// The line this pattern is expecting to find a message in.
    pub line: usize,
}

impl Condition {
    fn parse(c: &str) -> Self {
        if c == "on-host" {
            Condition::OnHost
        } else if let Some(bits) = c.strip_suffix("bit") {
            let bits: u8 = bits.parse().expect(
                "ignore/only filter ending in 'bit' must be of the form 'Nbit' for some integer N",
            );
            Condition::Bitwidth(bits)
        } else {
            Condition::Target(c.to_owned())
        }
    }
}

impl Comments {
    pub(crate) fn parse_file(path: &Path) -> Result<Self> {
        let content = std::fs::read_to_string(path)?;
        Self::parse(path, &content)
    }

    /// Parse comments in `content`.
    /// `path` is only used to emit diagnostics if parsing fails.
    pub(crate) fn parse(path: &Path, content: &str) -> Result<Self> {
        let mut this = Self::default();

        let mut fallthrough_to = None; // The line that a `|` will refer to.
        for (l, line) in content.lines().enumerate() {
            let l = l + 1; // enumerate starts at 0, but line numbers start at 1
            this.parse_checked_line(l, &mut fallthrough_to, line).map_err(|err| {
                err.wrap_err(format!("{}:{l}: failed to parse annotation", path.display()))
            })?;
        }
        Ok(this)
    }

    fn parse_checked_line(
        &mut self,
        l: usize,
        fallthrough_to: &mut Option<usize>,
        line: &str,
    ) -> Result<()> {
        if let Some((_, command)) = line.split_once("//@") {
            self.parse_command(command.trim(), l)
        } else if let Some((_, pattern)) = line.split_once("//~") {
            self.parse_pattern(pattern, fallthrough_to, l)
        } else if let Some((_, pattern)) = line.split_once("//[") {
            self.parse_revisioned_pattern(pattern, fallthrough_to, l)
        } else {
            *fallthrough_to = None;
            Ok(())
        }
    }

    fn parse_command(&mut self, command: &str, l: usize) -> Result<()> {
        // Commands are letters or dashes, grab everything until the first character that is neither of those.
        let (command, args) =
            match command.chars().position(|c: char| !c.is_alphanumeric() && c != '-') {
                None => (command, ""),
                Some(i) => {
                    let (command, args) = command.split_at(i);
                    let mut args = args.chars();
                    // Commands are separated from their arguments by ':' or ' '
                    let next = args
                        .next()
                        .expect("the `position` above guarantees that there is at least one char");
                    ensure!(next == ':', "test command must be followed by : (or end the line)");
                    (command, args.as_str().trim())
                }
            };

        match command {
            "revisions" => {
                ensure!(self.revisions.is_none(), "cannot specifiy revisions twice");
                self.revisions = Some(args.split_whitespace().map(|s| s.to_string()).collect());
            }
            "compile-flags" => {
                self.compile_flags.extend(args.split_whitespace().map(|s| s.to_string()));
            }
            "rustc-env" =>
                for env in args.split_whitespace() {
                    let (k, v) = env.split_once('=').ok_or_else(|| {
                        eyre!("environment variables must be key/value pairs separated by a `=`")
                    })?;
                    self.env_vars.push((k.to_string(), v.to_string()));
                },
            "normalize-stderr-test" => {
                /// Parses a string literal. `s` has to start with `"`; everything until the next `"` is
                /// returned in the first component. `\` can be used to escape arbitrary character.
                /// Second return component is the rest of the string with leading whitespace removed.
                fn parse_str(s: &str) -> Result<(&str, &str)> {
                    let mut chars = s.char_indices();
                    match chars.next().ok_or_else(|| eyre!("missing arguments"))?.1 {
                        '"' => {
                            let s = chars.as_str();
                            let mut escaped = false;
                            for (i, c) in chars {
                                if escaped {
                                    // Accept any character as literal after a `\`.
                                    escaped = false;
                                } else if c == '"' {
                                    return Ok((&s[..(i - 1)], s[i..].trim_start()));
                                } else {
                                    escaped = c == '\\';
                                }
                            }
                            bail!("no closing quotes found for {s}")
                        }
                        c => bail!("expected '\"', got {c}"),
                    }
                }

                let (from, rest) = parse_str(args)?;

                let to = rest.strip_prefix("->").ok_or_else(|| {
                    eyre!("normalize-stderr-test needs a pattern and replacement separated by `->`")
                })?.trim_start();
                let (to, rest) = parse_str(to)?;

                ensure!(rest.is_empty(), "trailing text after pattern replacement: {rest}");

                let from = Regex::new(from)?;
                self.normalize_stderr.push((from, to.to_string()));
            }
            "error-pattern" => {
                ensure!(
                    self.error_pattern.is_none(),
                    "cannot specifiy error_pattern twice, previous: {:?}",
                    self.error_pattern
                );
                self.error_pattern = Some((Pattern::parse(args.trim())?, l));
            }
            "stderr-per-bitwidth" => {
                // args are ignored (can be used as comment)
                ensure!(!self.stderr_per_bitwidth, "cannot specifiy stderr-per-bitwidth twice");
                self.stderr_per_bitwidth = true;
            }
            "require-annotations-for-level" => {
                ensure!(
                    self.require_annotations_for_level.is_none(),
                    "cannot specify `require-annotations-for-level` twice"
                );
                self.require_annotations_for_level = Some(args.trim().parse()?);
            }
            command => {
                if let Some(s) = command.strip_prefix("ignore-") {
                    // args are ignored (can be sue as comment)
                    self.ignore.push(Condition::parse(s));
                    return Ok(());
                }

                if let Some(s) = command.strip_prefix("only-") {
                    // args are ignored (can be sue as comment)
                    self.only.push(Condition::parse(s));
                    return Ok(());
                }
                bail!("unknown command {command}");
            }
        }

        Ok(())
    }

    fn parse_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        self.parse_pattern_inner(pattern, fallthrough_to, None, l)
    }

    fn parse_revisioned_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        let (revision, pattern) =
            pattern.split_once(']').ok_or_else(|| eyre!("`//[` without corresponding `]`"))?;
        if let Some(pattern) = pattern.strip_prefix('~') {
            self.parse_pattern_inner(pattern, fallthrough_to, Some(revision.to_owned()), l)
        } else {
            bail!("revisioned pattern must have `~` following the `]`");
        }
    }

    // parse something like (?P<offset>\||[\^]+)? *(?P<level>ERROR|HELP|WARN|NOTE): (?P<text>.*)
    fn parse_pattern_inner(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        revision: Option<String>,
        l: usize,
    ) -> Result<()> {
        let (match_line, pattern) =
            match pattern.chars().next().ok_or_else(|| eyre!("no pattern specified"))? {
                '|' =>
                    (
                        *fallthrough_to
                            .as_mut()
                            .ok_or_else(|| eyre!("`//~|` pattern without preceding line"))?,
                        &pattern[1..],
                    ),
                '^' => {
                    let offset = pattern.chars().take_while(|&c| c == '^').count();
                    (l - offset, &pattern[offset..])
                }
                _ => (l, pattern),
            };

        let pattern = pattern.trim_start();
        let offset = pattern
            .chars()
            .position(|c| !matches!(c, 'A'..='Z' | 'a'..='z'))
            .ok_or_else(|| eyre!("pattern without level"))?;

        let level = pattern[..offset].parse()?;
        let pattern = &pattern[offset..];
        let pattern = pattern.strip_prefix(':').ok_or_else(|| eyre!("no `:` after level found"))?;

        let pattern = pattern.trim();

        ensure!(!pattern.is_empty(), "no pattern specified");

        let pattern = Pattern::parse(pattern)?;

        *fallthrough_to = Some(match_line);

        self.error_matches.push(ErrorMatch {
            pattern,
            revision,
            level,
            definition_line: l,
            line: match_line,
        });

        Ok(())
    }
}

impl Pattern {
    pub(crate) fn matches(&self, message: &str) -> bool {
        match self {
            Pattern::SubString(s) => message.contains(s),
            Pattern::Regex(r) => r.is_match(message),
        }
    }

    pub(crate) fn parse(pattern: &str) -> Result<Self> {
        if let Some(pattern) = pattern.strip_prefix('/') {
            let regex =
                pattern.strip_suffix('/').ok_or_else(|| eyre!("regex must end with `/`"))?;
            Ok(Pattern::Regex(Regex::new(regex)?))
        } else {
            Ok(Pattern::SubString(pattern.to_string()))
        }
    }
}
<|endoftext|>


<commit_before>import ptr::extensions;

export handle;
export reader_methods;
export writer_methods;
export scope;

type scope_data<T:send,A> = {
    mut layout_active: bool,
    mut free_list: [handle<T,A>],
    mut first_dirty: handle<T,A>
};

resource scope_rsrc<T:send,A>(d: scope_data<T,A>) {
    unsafe {
        for d.free_list.each { |h| free_handle(h); }
    }
}

type scope<T:send,A> = @scope_rsrc<T,A>;

type handle_data<T:send,A> = {mut rd_ptr: *T,
                              mut wr_ptr: *T,
                              mut rd_aux: *A,
                              mut next_dirty: handle<T,A>};
enum handle<T:send,A> {
    _handle(*handle_data<T,A>)
}

impl private_methods<T:send,A> for handle<T,A> {
    fn rd_ptr() -> *T unsafe { (**self).rd_ptr }
    fn wr_ptr() -> *T unsafe { (**self).wr_ptr }
    fn rd_aux() -> *A unsafe { (**self).rd_aux }
    fn next_dirty() -> handle<T,A> unsafe { (**self).next_dirty }

    fn set_rd_ptr(t: *T) unsafe { (**self).rd_ptr = t; }
    fn set_wr_ptr(t: *T) unsafe { (**self).wr_ptr = t; }
    fn set_rd_aux(t: *A) unsafe { (**self).rd_aux = t; }
    fn set_next_dirty(+h: handle<T,A>) unsafe { (**self).next_dirty = h; }

    fn is_null() -> bool { (*self).is_null() }
    fn is_not_null() -> bool { (*self).is_not_null() }
}

impl reader_methods<T:send,A> for handle<T,A> {
    fn rd<U>(f: fn(T) -> U) -> U unsafe {
        f(*self.rd_ptr())
    }

    fn has_aux() -> bool unsafe {
        self.rd_aux().is_not_null()
    }

    fn set_aux(p: @A) unsafe {
        let p2 = p;
        unsafe::forget(p2); // Bump the reference count.

        (**self).rd_aux = ptr::addr_of(*p);
    }

    fn aux<U>(f: fn(A) -> U) -> U unsafe {
        assert self.has_aux();
        f(*self.rd_aux())
    }
}

impl private_methods<T:send,A> for scope<T,A> {
    fn clone(v: *T) -> *T unsafe {
        let n: *mut T =
            unsafe::reinterpret_cast(
                libc::calloc(sys::size_of::<T>(), 1u));

        // n.b.: this assignment will run the drop glue for <T,A>.
        // *Hopefully* the fact that everything is initialized to NULL
        // by calloc will make this ok.  We may have to make the take
        // glue be tolerant.
        *n = unsafe{*v};

        ret unsafe::reinterpret_cast(n);
    }
}

unsafe fn free<T:send>(t: *T) {
    let _x <- *unsafe::reinterpret_cast::<*T,*mut T>(t);
    libc::free(unsafe::reinterpret_cast(t));
}

unsafe fn free_handle<T:send,A>(h: handle<T,A>) {
    free(h.rd_ptr());
    if h.wr_ptr() != h.rd_ptr() { free(h.wr_ptr()); }
}

fn null_handle<T:send,A>() -> handle<T,A> {
    _handle(ptr::null())
}

fn scope<T:send,A>() -> scope<T,A> {
    @scope_rsrc({mut layout_active: false,
                 mut free_list: [],
                 mut first_dirty: null_handle()})
}

impl writer_methods<T:send,A> for scope<T,A> {
    fn is_reader_forked() -> bool {
        self.layout_active
    }

    fn reader_forked() {
        assert !self.layout_active;
        assert self.first_dirty.is_null();
        self.layout_active = true;
    }

    fn reader_joined() unsafe {
        assert self.layout_active;

        if self.first_dirty.is_not_null() {
            let mut handle = self.first_dirty;
            while (*handle).is_not_null() {
                free(handle.rd_ptr());

                handle.set_rd_ptr(handle.wr_ptr());
                let next_handle = handle.next_dirty();
                handle.set_next_dirty(null_handle());
                handle = next_handle;
            }
            self.first_dirty = null_handle();
        }

        assert self.first_dirty.is_null();
        self.layout_active = false;
    }

    fn rd<U>(h: handle<T,A>, f: fn(T) -> U) -> U unsafe {
        // Use the wr_ptr, which may be more up to date than the
        // rd_ptr or may not
        f(*h.wr_ptr())
    }

    fn wr<U>(h: handle<T,A>, f: fn(T) -> U) -> U unsafe {
        if self.layout_active && h.rd_ptr() == h.wr_ptr() {
            #debug["marking handle %? as dirty", h];
            h.set_wr_ptr(self.clone(h.rd_ptr()));
            h.set_next_dirty(self.first_dirty);
            self.first_dirty = h;
        }
        f(*h.wr_ptr())
    }

    fn handle(v: T) -> handle<T,A> unsafe {
        let d: *handle_data<T,A> =
            unsafe::reinterpret_cast(
                libc::malloc(sys::size_of::<handle_data<T,A>>()));
        (*d).rd_ptr = self.clone(ptr::addr_of(v));
        (*d).wr_ptr = (*d).rd_ptr;
        (*d).rd_aux = ptr::null();
        (*d).next_dirty = null_handle();
        let h = _handle(d);
        self.free_list += [h];
        ret h;
    }
}

#[cfg(test)]
mod test {

    type animal = {name: str, species: species};
    enum species {
        chicken(~chicken),
        bull(~bull)
    }
    type chicken = {mut eggs_per_day:uint};
    type bull = {mut horns:uint};

    type processed = {flag: bool};

    type animal_scope = scope<animal, processed>;

    #[test]
    fn handles_get_freed() {
        let s: animal_scope = scope();
        s.handle({name:"henrietta", species:chicken(~{mut eggs_per_day:22u})});
        s.handle({name:"ferdinand", species:bull(~{mut horns:3u})});
    }

    fn mutate(a: animal) {
        alt a.species {
          chicken(c) { c.eggs_per_day += 1u; }
          bull(c) { c.horns += 1u; }
        }
    }

    fn read_characteristic(a: animal) -> uint {
        alt a.species {
          chicken(c) { c.eggs_per_day }
          bull(c) { c.horns }
        }
    }

    #[test]
    fn interspersed_execution() {
        let s: animal_scope = scope();
        let henrietta =
            s.handle({name:"henrietta",
                      species:chicken(~{mut eggs_per_day:0u})});
        let ferdinand =
            s.handle({name:"ferdinand",
                      species:bull(~{mut horns:0u})});

        let iter1 = 3u;
        let iter2 = 22u;
        let read_port = comm::port();
        let read_chan = comm::chan(read_port);

        // fire up a reader task
        uint::range(0u, iter1) { |i|
            s.reader_forked();
            let wait_chan = task::spawn_listener {|wait_port|
                uint::range(0u, iter2) { |_i|
                    comm::send(read_chan, henrietta.rd(read_characteristic));
                    comm::send(read_chan, ferdinand.rd(read_characteristic));
                    comm::recv(wait_port);
                }
            };

            let hrc = henrietta.rd(read_characteristic);
            assert hrc == (i * iter2);

            let frc = ferdinand.rd(read_characteristic);
            assert frc == i * iter2;

            uint::range(0u, iter2) { |_i|
                assert hrc == comm::recv(read_port);
                s.wr(henrietta, mutate);
                assert frc == comm::recv(read_port);
                s.wr(ferdinand, mutate);
                comm::send(wait_chan, ());
            }
            s.reader_joined();
        }

        assert henrietta.rd(read_characteristic) == iter1 * iter2;
        assert ferdinand.rd(read_characteristic) == iter1 * iter2;
    }

}
<commit_msg>add some comments<commit_after>#[doc(str = "

Implements the RCU dom-sharing model.  This model allows for a single
writer and any number of readers, but the writer must be able to
control and manage the lifetimes of the reader(s).  For simplicity I
will describe the impl as though there were a single reader.

The basic idea is that every object in the RCU pool has both a reader
view and a writer view.  The writer always sees the writer view, which
contains the most up-to-date values.  The reader uses the reader view,
which contains the values as of the point where the reader was forked.
When the writer joins the reader, the reader view will be synchronized
with the writer view.

Internally, the way this works is using a copy-on-write scheme.  Each
RCU node maintains two pointers (`rd_ptr` and `wr_ptr`).  Assuming
that readers are active, when a writer wants to modify a node, it
first copies the reader's data into a new pointer.  Any writes that
occur after that point (but before the reader is joined) will operate
on this same copy.  When the reader is joined, any nodes which the
writer modified will free the stale reader data and update the reader
pointer to be the same as the writer pointer.

# Using the RCU APIs as a writer

You must first create a `scope` object.  The scope object manages the
memory and the RCU operations.  RCU'd objects of some sendable type
`T` are not referenced directly but rather through a `handle<T>`.  To
create a new RCU object, you use `scope.handle(t)` where `t` is some
initial value of type `T`.  To write to an RCU object, use
`scope.wr()` and to read from it use `scope.rd()`. Be sure not to use
the various `reader_methods`.

Handles can be freely sent between tasks but the RCU scope cannot.  It
must stay with the writer task.  You are responsible for correctly
invoking `reader_forked()` and `reader_joined()` to keep the RCU scope
abreast of when the reader is active.  Failure to do so will lead to
race conditions or worse.

# Using the RCU APIs as a reader

Import the `reader_methods` impl.  When you receive a handle, you can
invoke `h.rd { |v| ... }` and so forth.  There is also a piece of
auxiliary data that can be optionally associated with each handle.

Note: if the type `T` contains mutable fields, then there is nothing
to stop the reader from mutating those fields in the `rd()` method.
Do not do this.  It will lead to race conditions.

# Auxiliary data

Readers can associate a piece of auxiliary data of type `A` along with
main nodes.  This is convenient but dangerous: it is the reader's job
to ensure that this data remains live independent of the RCU nodes
themselves.

")];

import ptr::extensions;

export handle;
export reader_methods;
export writer_methods;
export scope;

type scope_data<T:send,A> = {
    mut layout_active: bool,
    mut free_list: [handle<T,A>],
    mut first_dirty: handle<T,A>
};

resource scope_rsrc<T:send,A>(d: scope_data<T,A>) {
    unsafe {
        for d.free_list.each { |h| free_handle(h); }
    }
}

type scope<T:send,A> = @scope_rsrc<T,A>;

type handle_data<T:send,A> = {mut rd_ptr: *T,
                              mut wr_ptr: *T,
                              mut rd_aux: *A,
                              mut next_dirty: handle<T,A>};
enum handle<T:send,A> {
    _handle(*handle_data<T,A>)
}

impl private_methods<T:send,A> for handle<T,A> {
    fn rd_ptr() -> *T unsafe { (**self).rd_ptr }
    fn wr_ptr() -> *T unsafe { (**self).wr_ptr }
    fn rd_aux() -> *A unsafe { (**self).rd_aux }
    fn next_dirty() -> handle<T,A> unsafe { (**self).next_dirty }

    fn set_rd_ptr(t: *T) unsafe { (**self).rd_ptr = t; }
    fn set_wr_ptr(t: *T) unsafe { (**self).wr_ptr = t; }
    fn set_rd_aux(t: *A) unsafe { (**self).rd_aux = t; }
    fn set_next_dirty(+h: handle<T,A>) unsafe { (**self).next_dirty = h; }

    fn is_null() -> bool { (*self).is_null() }
    fn is_not_null() -> bool { (*self).is_not_null() }
}

impl reader_methods<T:send,A> for handle<T,A> {
    #[doc(str = "access the reader's view of the handle's data")]
    fn rd<U>(f: fn(T) -> U) -> U unsafe {
        f(*self.rd_ptr())
    }

    #[doc(str = "true if auxiliary data is associated with this handle")]
    fn has_aux() -> bool unsafe {
        self.rd_aux().is_not_null()
    }

    #[doc(str = "set the auxiliary data associated with this handle.

    **Warning:** the reader is responsible for keeping this data live!
    ")]
    fn set_aux(p: @A) unsafe {
        let p2 = p;
        unsafe::forget(p2); // Bump the reference count.

        (**self).rd_aux = ptr::addr_of(*p);
    }

    #[doc(str = "access the auxiliary data associated with this handle.")]
    fn aux<U>(f: fn(A) -> U) -> U unsafe {
        assert self.has_aux();
        f(*self.rd_aux())
    }
}

impl private_methods<T:send,A> for scope<T,A> {
    fn clone(v: *T) -> *T unsafe {
        let n: *mut T =
            unsafe::reinterpret_cast(
                libc::calloc(sys::size_of::<T>(), 1u));

        // n.b.: this assignment will run the drop glue for <T,A>.
        // *Hopefully* the fact that everything is initialized to NULL
        // by calloc will make this ok.  We may have to make the take
        // glue be tolerant.
        *n = unsafe{*v};

        ret unsafe::reinterpret_cast(n);
    }
}

unsafe fn free<T:send>(t: *T) {
    let _x <- *unsafe::reinterpret_cast::<*T,*mut T>(t);
    libc::free(unsafe::reinterpret_cast(t));
}

unsafe fn free_handle<T:send,A>(h: handle<T,A>) {
    free(h.rd_ptr());
    if h.wr_ptr() != h.rd_ptr() { free(h.wr_ptr()); }
}

fn null_handle<T:send,A>() -> handle<T,A> {
    _handle(ptr::null())
}

fn scope<T:send,A>() -> scope<T,A> {
    @scope_rsrc({mut layout_active: false,
                 mut free_list: [],
                 mut first_dirty: null_handle()})
}

impl writer_methods<T:send,A> for scope<T,A> {
    fn is_reader_forked() -> bool {
        self.layout_active
    }

    fn reader_forked() {
        assert !self.layout_active;
        assert self.first_dirty.is_null();
        self.layout_active = true;
    }

    fn reader_joined() unsafe {
        assert self.layout_active;

        if self.first_dirty.is_not_null() {
            let mut handle = self.first_dirty;
            while (*handle).is_not_null() {
                free(handle.rd_ptr());

                handle.set_rd_ptr(handle.wr_ptr());
                let next_handle = handle.next_dirty();
                handle.set_next_dirty(null_handle());
                handle = next_handle;
            }
            self.first_dirty = null_handle();
        }

        assert self.first_dirty.is_null();
        self.layout_active = false;
    }

    fn rd<U>(h: handle<T,A>, f: fn(T) -> U) -> U unsafe {
        // Use the wr_ptr, which may be more up to date than the
        // rd_ptr or may not
        f(*h.wr_ptr())
    }

    fn wr<U>(h: handle<T,A>, f: fn(T) -> U) -> U unsafe {
        if self.layout_active && h.rd_ptr() == h.wr_ptr() {
            #debug["marking handle %? as dirty", h];
            h.set_wr_ptr(self.clone(h.rd_ptr()));
            h.set_next_dirty(self.first_dirty);
            self.first_dirty = h;
        }
        f(*h.wr_ptr())
    }

    fn handle(v: T) -> handle<T,A> unsafe {
        let d: *handle_data<T,A> =
            unsafe::reinterpret_cast(
                libc::malloc(sys::size_of::<handle_data<T,A>>()));
        (*d).rd_ptr = self.clone(ptr::addr_of(v));
        (*d).wr_ptr = (*d).rd_ptr;
        (*d).rd_aux = ptr::null();
        (*d).next_dirty = null_handle();
        let h = _handle(d);
        self.free_list += [h];
        ret h;
    }
}

#[cfg(test)]
mod test {

    type animal = {name: str, species: species};
    enum species {
        chicken(~chicken),
        bull(~bull)
    }
    type chicken = {mut eggs_per_day:uint};
    type bull = {mut horns:uint};

    type processed = {flag: bool};

    type animal_scope = scope<animal, processed>;

    #[test]
    fn handles_get_freed() {
        let s: animal_scope = scope();
        s.handle({name:"henrietta", species:chicken(~{mut eggs_per_day:22u})});
        s.handle({name:"ferdinand", species:bull(~{mut horns:3u})});
    }

    fn mutate(a: animal) {
        alt a.species {
          chicken(c) { c.eggs_per_day += 1u; }
          bull(c) { c.horns += 1u; }
        }
    }

    fn read_characteristic(a: animal) -> uint {
        alt a.species {
          chicken(c) { c.eggs_per_day }
          bull(c) { c.horns }
        }
    }

    #[test]
    fn interspersed_execution() {
        let s: animal_scope = scope();
        let henrietta =
            s.handle({name:"henrietta",
                      species:chicken(~{mut eggs_per_day:0u})});
        let ferdinand =
            s.handle({name:"ferdinand",
                      species:bull(~{mut horns:0u})});

        let iter1 = 3u;
        let iter2 = 22u;
        let read_port = comm::port();
        let read_chan = comm::chan(read_port);

        // fire up a reader task
        uint::range(0u, iter1) { |i|
            s.reader_forked();
            let wait_chan = task::spawn_listener {|wait_port|
                uint::range(0u, iter2) { |_i|
                    comm::send(read_chan, henrietta.rd(read_characteristic));
                    comm::send(read_chan, ferdinand.rd(read_characteristic));
                    comm::recv(wait_port);
                }
            };

            let hrc = henrietta.rd(read_characteristic);
            assert hrc == (i * iter2);

            let frc = ferdinand.rd(read_characteristic);
            assert frc == i * iter2;

            uint::range(0u, iter2) { |_i|
                assert hrc == comm::recv(read_port);
                s.wr(henrietta, mutate);
                assert frc == comm::recv(read_port);
                s.wr(ferdinand, mutate);
                comm::send(wait_chan, ());
            }
            s.reader_joined();
        }

        assert henrietta.rd(read_characteristic) == iter1 * iter2;
        assert ferdinand.rd(read_characteristic) == iter1 * iter2;
    }

}
<|endoftext|>


<commit_before>use crate::common::util::*;

#[test]
fn test_rejects_nan() {
    new_ucmd!()
        .args(&["NaN"])
        .fails()
        .stderr_only("seq: invalid 'not-a-number' argument: 'NaN'\nTry 'seq --help' for more information.");
}

#[test]
fn test_rejects_non_floats() {
    new_ucmd!()
        .args(&["foo"])
        .fails()
        .stderr_only("seq: invalid floating point argument: 'foo'\nTry 'seq --help' for more information.");
}

// ---- Tests for the big integer based path ----

#[test]
fn test_count_up() {
    new_ucmd!()
        .args(&["10"])
        .run()
        .stdout_is("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n");
}

#[test]
fn test_count_down() {
    new_ucmd!()
        .args(&["--", "5", "-1", "1"])
        .run()
        .stdout_is("5\n4\n3\n2\n1\n");
    new_ucmd!()
        .args(&["5", "-1", "1"])
        .run()
        .stdout_is("5\n4\n3\n2\n1\n");
}

#[test]
fn test_separator_and_terminator() {
    new_ucmd!()
        .args(&["-s", ",", "-t", "!", "2", "6"])
        .run()
        .stdout_is("2,3,4,5,6!");
    new_ucmd!()
        .args(&["-s", ",", "2", "6"])
        .run()
        .stdout_is("2,3,4,5,6\n");
    new_ucmd!()
        .args(&["-s", "\n", "2", "6"])
        .run()
        .stdout_is("2\n3\n4\n5\n6\n");
    new_ucmd!()
        .args(&["-s", "\\n", "2", "6"])
        .run()
        .stdout_is("2\\n3\\n4\\n5\\n6\n");
}

#[test]
fn test_equalize_widths() {
    new_ucmd!()
        .args(&["-w", "5", "10"])
        .run()
        .stdout_is("05\n06\n07\n08\n09\n10\n");
}

#[test]
fn test_seq_wrong_arg() {
    new_ucmd!().args(&["-w", "5", "10", "33", "32"]).fails();
}

#[test]
fn test_zero_step() {
    new_ucmd!().args(&["10", "0", "32"]).fails();
}

#[test]
fn test_big_numbers() {
    new_ucmd!()
        .args(&[
            "1000000000000000000000000000",
            "1000000000000000000000000001",
        ])
        .succeeds()
        .stdout_only("1000000000000000000000000000\n1000000000000000000000000001\n");
}

// ---- Tests for the floating point based path ----

#[test]
fn test_count_up_floats() {
    new_ucmd!()
        .args(&["10.0"])
        .run()
        .stdout_is("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n");
}

#[test]
fn test_count_down_floats() {
    new_ucmd!()
        .args(&["--", "5", "-1.0", "1"])
        .run()
        .stdout_is("5.0\n4.0\n3.0\n2.0\n1.0\n");
    new_ucmd!()
        .args(&["5", "-1", "1.0"])
        .run()
        .stdout_is("5\n4\n3\n2\n1\n");
}

#[test]
fn test_separator_and_terminator_floats() {
    new_ucmd!()
        .args(&["-s", ",", "-t", "!", "2.0", "6"])
        .run()
        .stdout_is("2.0,3.0,4.0,5.0,6.0!");
}

#[test]
fn test_equalize_widths_floats() {
    new_ucmd!()
        .args(&["-w", "5", "10.0"])
        .run()
        .stdout_is("05\n06\n07\n08\n09\n10\n");
}

#[test]
fn test_seq_wrong_arg_floats() {
    new_ucmd!().args(&["-w", "5", "10.0", "33", "32"]).fails();
}

#[test]
fn test_zero_step_floats() {
    new_ucmd!().args(&["10.0", "0", "32"]).fails();
}
<commit_msg>rustfmt the recent change<commit_after>use crate::common::util::*;

#[test]
fn test_rejects_nan() {
    new_ucmd!().args(&["NaN"]).fails().stderr_only(
        "seq: invalid 'not-a-number' argument: 'NaN'\nTry 'seq --help' for more information.",
    );
}

#[test]
fn test_rejects_non_floats() {
    new_ucmd!().args(&["foo"]).fails().stderr_only(
        "seq: invalid floating point argument: 'foo'\nTry 'seq --help' for more information.",
    );
}

// ---- Tests for the big integer based path ----

#[test]
fn test_count_up() {
    new_ucmd!()
        .args(&["10"])
        .run()
        .stdout_is("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n");
}

#[test]
fn test_count_down() {
    new_ucmd!()
        .args(&["--", "5", "-1", "1"])
        .run()
        .stdout_is("5\n4\n3\n2\n1\n");
    new_ucmd!()
        .args(&["5", "-1", "1"])
        .run()
        .stdout_is("5\n4\n3\n2\n1\n");
}

#[test]
fn test_separator_and_terminator() {
    new_ucmd!()
        .args(&["-s", ",", "-t", "!", "2", "6"])
        .run()
        .stdout_is("2,3,4,5,6!");
    new_ucmd!()
        .args(&["-s", ",", "2", "6"])
        .run()
        .stdout_is("2,3,4,5,6\n");
    new_ucmd!()
        .args(&["-s", "\n", "2", "6"])
        .run()
        .stdout_is("2\n3\n4\n5\n6\n");
    new_ucmd!()
        .args(&["-s", "\\n", "2", "6"])
        .run()
        .stdout_is("2\\n3\\n4\\n5\\n6\n");
}

#[test]
fn test_equalize_widths() {
    new_ucmd!()
        .args(&["-w", "5", "10"])
        .run()
        .stdout_is("05\n06\n07\n08\n09\n10\n");
}

#[test]
fn test_seq_wrong_arg() {
    new_ucmd!().args(&["-w", "5", "10", "33", "32"]).fails();
}

#[test]
fn test_zero_step() {
    new_ucmd!().args(&["10", "0", "32"]).fails();
}

#[test]
fn test_big_numbers() {
    new_ucmd!()
        .args(&[
            "1000000000000000000000000000",
            "1000000000000000000000000001",
        ])
        .succeeds()
        .stdout_only("1000000000000000000000000000\n1000000000000000000000000001\n");
}

// ---- Tests for the floating point based path ----

#[test]
fn test_count_up_floats() {
    new_ucmd!()
        .args(&["10.0"])
        .run()
        .stdout_is("1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n");
}

#[test]
fn test_count_down_floats() {
    new_ucmd!()
        .args(&["--", "5", "-1.0", "1"])
        .run()
        .stdout_is("5.0\n4.0\n3.0\n2.0\n1.0\n");
    new_ucmd!()
        .args(&["5", "-1", "1.0"])
        .run()
        .stdout_is("5\n4\n3\n2\n1\n");
}

#[test]
fn test_separator_and_terminator_floats() {
    new_ucmd!()
        .args(&["-s", ",", "-t", "!", "2.0", "6"])
        .run()
        .stdout_is("2.0,3.0,4.0,5.0,6.0!");
}

#[test]
fn test_equalize_widths_floats() {
    new_ucmd!()
        .args(&["-w", "5", "10.0"])
        .run()
        .stdout_is("05\n06\n07\n08\n09\n10\n");
}

#[test]
fn test_seq_wrong_arg_floats() {
    new_ucmd!().args(&["-w", "5", "10.0", "33", "32"]).fails();
}

#[test]
fn test_zero_step_floats() {
    new_ucmd!().args(&["10.0", "0", "32"]).fails();
}
<|endoftext|>


<commit_before>//! Tests for the `cargo search` command.

use cargo_test_support::cargo_process;
use cargo_test_support::git::repo;
use cargo_test_support::paths;
use cargo_test_support::registry::{api_path, registry_path, registry_url};
use std::collections::HashSet;
use std::fs;
use std::path::Path;
use url::Url;

fn api() -> Url {
    Url::from_file_path(&*api_path()).ok().unwrap()
}

fn write_crates(dest: &Path) {
    let content = r#"{
        "crates": [{
            "created_at": "2014-11-16T20:17:35Z",
            "description": "Design by contract style assertions for Rust",
            "documentation": null,
            "downloads": 2,
            "homepage": null,
            "id": "hoare",
            "keywords": [],
            "license": null,
            "links": {
                "owners": "/api/v1/crates/hoare/owners",
                "reverse_dependencies": "/api/v1/crates/hoare/reverse_dependencies",
                "version_downloads": "/api/v1/crates/hoare/downloads",
                "versions": "/api/v1/crates/hoare/versions"
            },
            "max_version": "0.1.1",
            "name": "hoare",
            "repository": "https://github.com/nick29581/libhoare",
            "updated_at": "2014-11-20T21:49:21Z",
            "versions": null
        },
        {
            "id": "postgres",
            "name": "postgres",
            "updated_at": "2020-05-01T23:17:54.335921+00:00",
            "versions": null,
            "keywords": null,
            "categories": null,
            "badges": [
                {
                    "badge_type": "circle-ci",
                    "attributes": {
                        "repository": "sfackler/rust-postgres",
                        "branch": null
                    }
                }
            ],
            "created_at": "2014-11-24T02:34:44.756689+00:00",
            "downloads": 535491,
            "recent_downloads": 88321,
            "max_version": "0.17.3",
            "newest_version": "0.17.3",
            "description": "A native, synchronous PostgreSQL client",
            "homepage": null,
            "documentation": null,
            "repository": "https://github.com/sfackler/rust-postgres",
            "links": {
                "version_downloads": "/api/v1/crates/postgres/downloads",
                "versions": "/api/v1/crates/postgres/versions",
                "owners": "/api/v1/crates/postgres/owners",
                "owner_team": "/api/v1/crates/postgres/owner_team",
                "owner_user": "/api/v1/crates/postgres/owner_user",
                "reverse_dependencies": "/api/v1/crates/postgres/reverse_dependencies"
            },
            "exact_match": true
        }
        ],
        "meta": {
            "total": 2
        }
    }"#;

    // Older versions of curl don't peel off query parameters when looking for
    // filenames, so just make both files.
    //
    // On windows, though, `?` is an invalid character, but we always build curl
    // from source there anyway!
    fs::write(&dest, content).unwrap();
    if !cfg!(windows) {
        fs::write(
            &dest.with_file_name("crates?q=postgres&per_page=10"),
            content,
        )
        .unwrap();
    }
}

const SEARCH_RESULTS: &str = "\
hoare = \"0.1.1\"        # Design by contract style assertions for Rust
postgres = \"0.17.3\"    # A native, synchronous PostgreSQL client
";

fn setup() {
    let cargo_home = paths::root().join(".cargo");
    fs::create_dir_all(cargo_home).unwrap();
    fs::create_dir_all(&api_path().join("api/v1")).unwrap();

    // Init a new registry
    let _ = repo(&registry_path())
        .file(
            "config.json",
            &format!(r#"{{"dl":"{0}","api":"{0}"}}"#, api()),
        )
        .build();

    let base = api_path().join("api/v1/crates");
    write_crates(&base);
}

fn set_cargo_config() {
    let config = paths::root().join(".cargo/config");

    fs::write(
        &config,
        format!(
            r#"
            [source.crates-io]
            registry = 'https://wut'
            replace-with = 'dummy-registry'

            [source.dummy-registry]
            registry = '{reg}'
            "#,
            reg = registry_url(),
        ),
    )
    .unwrap();
}

#[cargo_test]
fn not_update() {
    setup();
    set_cargo_config();

    use cargo::core::{Shell, Source, SourceId};
    use cargo::sources::RegistrySource;
    use cargo::util::Config;

    let sid = SourceId::for_registry(&registry_url()).unwrap();
    let cfg = Config::new(
        Shell::from_write(Box::new(Vec::new())),
        paths::root(),
        paths::home().join(".cargo"),
    );
    let lock = cfg.acquire_package_cache_lock().unwrap();
    let mut regsrc = RegistrySource::remote(sid, &HashSet::new(), &cfg);
    regsrc.update().unwrap();
    drop(lock);

    cargo_process("search postgres")
        .with_stdout_contains(SEARCH_RESULTS)
        .with_stderr("") // without "Updating ... index"
        .run();
}

#[cargo_test]
fn replace_default() {
    setup();
    set_cargo_config();

    cargo_process("search postgres")
        .with_stdout_contains(SEARCH_RESULTS)
        .with_stderr_contains("[..]Updating [..] index")
        .run();
}

#[cargo_test]
fn simple() {
    setup();

    cargo_process("search postgres --index")
        .arg(registry_url().to_string())
        .with_stdout_contains(SEARCH_RESULTS)
        .run();
}

#[cargo_test]
fn multiple_query_params() {
    setup();

    cargo_process("search postgres sql --index")
        .arg(registry_url().to_string())
        .with_stdout_contains(SEARCH_RESULTS)
        .run();
}
<commit_msg>test(search): Ensure we maintain quiet behavior<commit_after>//! Tests for the `cargo search` command.

use cargo_test_support::cargo_process;
use cargo_test_support::git::repo;
use cargo_test_support::paths;
use cargo_test_support::registry::{api_path, registry_path, registry_url};
use std::collections::HashSet;
use std::fs;
use std::path::Path;
use url::Url;

fn api() -> Url {
    Url::from_file_path(&*api_path()).ok().unwrap()
}

fn write_crates(dest: &Path) {
    let content = r#"{
        "crates": [{
            "created_at": "2014-11-16T20:17:35Z",
            "description": "Design by contract style assertions for Rust",
            "documentation": null,
            "downloads": 2,
            "homepage": null,
            "id": "hoare",
            "keywords": [],
            "license": null,
            "links": {
                "owners": "/api/v1/crates/hoare/owners",
                "reverse_dependencies": "/api/v1/crates/hoare/reverse_dependencies",
                "version_downloads": "/api/v1/crates/hoare/downloads",
                "versions": "/api/v1/crates/hoare/versions"
            },
            "max_version": "0.1.1",
            "name": "hoare",
            "repository": "https://github.com/nick29581/libhoare",
            "updated_at": "2014-11-20T21:49:21Z",
            "versions": null
        },
        {
            "id": "postgres",
            "name": "postgres",
            "updated_at": "2020-05-01T23:17:54.335921+00:00",
            "versions": null,
            "keywords": null,
            "categories": null,
            "badges": [
                {
                    "badge_type": "circle-ci",
                    "attributes": {
                        "repository": "sfackler/rust-postgres",
                        "branch": null
                    }
                }
            ],
            "created_at": "2014-11-24T02:34:44.756689+00:00",
            "downloads": 535491,
            "recent_downloads": 88321,
            "max_version": "0.17.3",
            "newest_version": "0.17.3",
            "description": "A native, synchronous PostgreSQL client",
            "homepage": null,
            "documentation": null,
            "repository": "https://github.com/sfackler/rust-postgres",
            "links": {
                "version_downloads": "/api/v1/crates/postgres/downloads",
                "versions": "/api/v1/crates/postgres/versions",
                "owners": "/api/v1/crates/postgres/owners",
                "owner_team": "/api/v1/crates/postgres/owner_team",
                "owner_user": "/api/v1/crates/postgres/owner_user",
                "reverse_dependencies": "/api/v1/crates/postgres/reverse_dependencies"
            },
            "exact_match": true
        }
        ],
        "meta": {
            "total": 2
        }
    }"#;

    // Older versions of curl don't peel off query parameters when looking for
    // filenames, so just make both files.
    //
    // On windows, though, `?` is an invalid character, but we always build curl
    // from source there anyway!
    fs::write(&dest, content).unwrap();
    if !cfg!(windows) {
        fs::write(
            &dest.with_file_name("crates?q=postgres&per_page=10"),
            content,
        )
        .unwrap();
    }
}

const SEARCH_RESULTS: &str = "\
hoare = \"0.1.1\"        # Design by contract style assertions for Rust
postgres = \"0.17.3\"    # A native, synchronous PostgreSQL client
";

fn setup() {
    let cargo_home = paths::root().join(".cargo");
    fs::create_dir_all(cargo_home).unwrap();
    fs::create_dir_all(&api_path().join("api/v1")).unwrap();

    // Init a new registry
    let _ = repo(&registry_path())
        .file(
            "config.json",
            &format!(r#"{{"dl":"{0}","api":"{0}"}}"#, api()),
        )
        .build();

    let base = api_path().join("api/v1/crates");
    write_crates(&base);
}

fn set_cargo_config() {
    let config = paths::root().join(".cargo/config");

    fs::write(
        &config,
        format!(
            r#"
            [source.crates-io]
            registry = 'https://wut'
            replace-with = 'dummy-registry'

            [source.dummy-registry]
            registry = '{reg}'
            "#,
            reg = registry_url(),
        ),
    )
    .unwrap();
}

#[cargo_test]
fn not_update() {
    setup();
    set_cargo_config();

    use cargo::core::{Shell, Source, SourceId};
    use cargo::sources::RegistrySource;
    use cargo::util::Config;

    let sid = SourceId::for_registry(&registry_url()).unwrap();
    let cfg = Config::new(
        Shell::from_write(Box::new(Vec::new())),
        paths::root(),
        paths::home().join(".cargo"),
    );
    let lock = cfg.acquire_package_cache_lock().unwrap();
    let mut regsrc = RegistrySource::remote(sid, &HashSet::new(), &cfg);
    regsrc.update().unwrap();
    drop(lock);

    cargo_process("search postgres")
        .with_stdout_contains(SEARCH_RESULTS)
        .with_stderr("") // without "Updating ... index"
        .run();
}

#[cargo_test]
fn replace_default() {
    setup();
    set_cargo_config();

    cargo_process("search postgres")
        .with_stdout_contains(SEARCH_RESULTS)
        .with_stderr_contains("[..]Updating [..] index")
        .run();
}

#[cargo_test]
fn simple() {
    setup();

    cargo_process("search postgres --index")
        .arg(registry_url().to_string())
        .with_stdout_contains(SEARCH_RESULTS)
        .run();
}

#[cargo_test]
fn multiple_query_params() {
    setup();

    cargo_process("search postgres sql --index")
        .arg(registry_url().to_string())
        .with_stdout_contains(SEARCH_RESULTS)
        .run();
}

#[cargo_test]
fn ignore_quiet() {
    setup();
    set_cargo_config();

    cargo_process("search -q postgres")
        .with_stdout_contains(SEARCH_RESULTS)
        .run();
}
<|endoftext|>


<commit_before>use std::{
    collections::{BTreeMap, VecDeque},
    fs::File,
    path::{Path, PathBuf},
};

use crate::{background_jobs::Environment, uploaders::Uploader, util::errors::std_error_no_send};

use scopeguard::defer;
use swirl::PerformError;

/// Create CSV dumps of the public information in the database, wrap them in a
/// tarball and upload to S3.
#[swirl::background_job]
pub fn dump_db(
    env: &Environment,
    database_url: String,
    target_name: String,
) -> Result<(), PerformError> {
    let timestamp = chrono::Utc::now().format("%Y-%m-%d-%H%M%S").to_string();
    let export_dir = std::env::temp_dir().join("dump-db").join(timestamp);
    std::fs::create_dir_all(&export_dir)?;
    defer! {{
        std::fs::remove_dir_all(&export_dir).unwrap();
    }}
    let visibility_config: VisibilityConfig = toml::from_str(include_str!("dump-db.toml")).unwrap();
    let export_script = export_dir.join("export.sql");
    let import_script = export_dir.join("import.sql");
    visibility_config.gen_psql_scripts(&export_script, &import_script)?;
    run_psql(&database_url, &export_script)?;
    let tarball = create_tarball(&export_dir)?;
    defer! {{
        std::fs::remove_file(&tarball).unwrap();
    }}
    upload_tarball(&tarball, &target_name, &env.uploader)?;
    println!("Database dump uploaded to {}.", &target_name);
    Ok(())
}

/// An enum indicating whether a column is included in the database dumps.
/// Public columns are included, private are not.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
enum ColumnVisibility {
    Private,
    Public,
}

/// Filtering information for a single table. The `dependencies` field is only
/// used to determine the order of the tables in the generated import script,
/// and should list all tables the current tables refers to with foreign key
/// constraints on public columns. The `filter` field is a valid SQL expression
/// used in a `WHERE` clause to filter the rows of the table. The `columns`
/// field maps column names to their respective visibilities.
#[derive(Clone, Debug, Deserialize)]
struct TableConfig {
    #[serde(default)]
    dependencies: Vec<String>,
    filter: Option<String>,
    columns: BTreeMap<String, ColumnVisibility>,
}

/// Subset of the configuration data to be passed on to the Handlbars template.
#[derive(Debug, Serialize)]
struct HandlebarsTableContext<'a> {
    name: &'a str,
    filter: Option<&'a str>,
    columns: String,
}

impl TableConfig {
    fn handlebars_context<'a>(&'a self, name: &'a str) -> Option<HandlebarsTableContext<'a>> {
        let columns = self
            .columns
            .iter()
            .filter(|&(_, &vis)| vis == ColumnVisibility::Public)
            .map(|(col, _)| format!("\"{}\"", col))
            .collect::<Vec<String>>()
            .join(", ");
        if columns.is_empty() {
            None
        } else {
            Some(HandlebarsTableContext {
                name,
                filter: self.filter.as_ref().map(String::as_str),
                columns,
            })
        }
    }
}

/// Maps table names to the respective configurations. Used to load `dump_db.toml`.
#[derive(Clone, Debug, Deserialize)]
#[serde(transparent)]
struct VisibilityConfig(BTreeMap<String, TableConfig>);

/// Subset of the configuration data to be passed on to the Handlbars template.
#[derive(Debug, Serialize)]
struct HandlebarsContext<'a> {
    tables: Vec<HandlebarsTableContext<'a>>,
}

impl VisibilityConfig {
    /// Sort the tables in a way that dependencies come before dependent tables.
    ///
    /// Returns a vector of table names.
    fn topological_sort(&self) -> Vec<&str> {
        let mut result = Vec::new();
        let mut num_deps = BTreeMap::new();
        let mut rev_deps: BTreeMap<&str, Vec<&str>> = self
            .0
            .iter()
            .map(|(table, _)| (table.as_str(), vec![]))
            .collect();
        for (table, config) in self.0.iter() {
            num_deps.insert(table.as_str(), config.dependencies.len());
            for dep in &config.dependencies {
                rev_deps.get_mut(dep.as_str()).unwrap().push(table.as_str());
            }
        }
        let mut ready: VecDeque<&str> = num_deps
            .iter()
            .filter(|(_, &count)| count == 0)
            .map(|(&table, _)| table)
            .collect();
        while let Some(table) = ready.pop_front() {
            result.push(table);
            for dep in &rev_deps[table] {
                *num_deps.get_mut(dep).unwrap() -= 1;
                if num_deps[dep] == 0 {
                    ready.push_back(dep);
                }
            }
        }
        assert_eq!(
            self.0.len(),
            result.len(),
            "circular dependencies in DB dump config detected",
        );
        result
    }

    fn handlebars_context(&self) -> HandlebarsContext<'_> {
        let tables = self
            .topological_sort()
            .into_iter()
            .filter_map(|table| self.0[table].handlebars_context(table))
            .collect();
        HandlebarsContext { tables }
    }

    fn gen_psql_scripts(
        &self,
        export_script: &Path,
        import_script: &Path,
    ) -> Result<(), PerformError> {
        let context = self.handlebars_context();
        let mut handlebars = handlebars::Handlebars::new();
        handlebars.register_escape_fn(handlebars::no_escape);
        let export_sql = File::create(export_script)?;
        handlebars.render_template_to_write(
            include_str!("dump-export.hbs"),
            &context,
            export_sql,
        )?;
        let import_sql = File::create(import_script)?;
        handlebars.render_template_to_write(
            include_str!("dump-import.hbs"),
            &context,
            import_sql,
        )?;
        Ok(())
    }
}

fn run_psql(database_url: &str, export_script: &Path) -> Result<(), PerformError> {
    use std::process::{Command, Stdio};

    let psql_script = File::open(export_script)?;
    let psql = Command::new("psql")
        .arg(database_url)
        .current_dir(export_script.parent().unwrap())
        .stdin(psql_script)
        .stdout(Stdio::null())
        .stderr(Stdio::piped())
        .spawn()?;
    let output = psql.wait_with_output()?;
    if !output.stderr.is_empty() {
        Err(format!(
            "Error while executing psql: {}",
            String::from_utf8_lossy(&output.stderr)
        ))?;
    }
    if !output.status.success() {
        Err("psql did not finish successfully.")?;
    }
    Ok(())
}

fn create_tarball(export_dir: &Path) -> Result<PathBuf, PerformError> {
    let tarball_name = export_dir.with_extension("tar.gz");
    let tarball = File::create(&tarball_name)?;
    let encoder = flate2::write::GzEncoder::new(tarball, flate2::Compression::default());
    let mut archive = tar::Builder::new(encoder);
    archive.append_dir_all(export_dir.file_name().unwrap(), &export_dir)?;
    Ok(tarball_name)
}

fn upload_tarball(
    tarball: &Path,
    target_name: &str,
    uploader: &Uploader,
) -> Result<(), PerformError> {
    let client = reqwest::Client::new();
    let tarfile = File::open(tarball)?;
    let content_length = tarfile.metadata()?.len();
    // TODO Figure out the correct content type.
    uploader
        .upload(
            &client,
            target_name,
            tarfile,
            content_length,
            "application/gzip",
        )
        .map_err(std_error_no_send)?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_util::pg_connection;
    use diesel::prelude::*;
    use std::collections::HashSet;

    /// Test whether the schema in the visibility configuration matches the test database.
    #[test]
    fn check_visibility_config() {
        let conn = pg_connection();
        let db_columns: HashSet<_> = get_db_columns(&conn)
            .into_iter()
            .map(|c| (c.table_name, c.column_name))
            .collect();
        let visibility_config: VisibilityConfig =
            toml::from_str(include_str!("dump-db.toml")).unwrap();
        let vis_columns: HashSet<_> = visibility_config
            .0
            .iter()
            .flat_map(|(table, config)| {
                config
                    .columns
                    .iter()
                    .map(move |(column, _)| (table.clone(), column.clone()))
            })
            .collect();
        let mut errors = vec![];
        for (table, col) in db_columns.difference(&vis_columns) {
            errors.push(format!(
                "No visibility information for columns {}.{}.",
                table, col
            ));
        }
        for (table, col) in vis_columns.difference(&db_columns) {
            errors.push(format!(
                "Column {}.{} does not exist in the database.",
                table, col
            ));
        }
        assert!(
            errors.is_empty(),
            "The visibility configuration does not match the database schema:\n{}",
            errors.join("\n  - "),
        );
    }

    mod information_schema {
        table! {
            information_schema.columns (table_schema, table_name, column_name) {
                table_schema -> Text,
                table_name -> Text,
                column_name -> Text,
                ordinal_position -> Integer,
            }
        }
    }

    #[derive(Debug, PartialEq, Queryable)]
    struct Column {
        table_name: String,
        column_name: String,
    }

    fn get_db_columns(conn: &PgConnection) -> Vec<Column> {
        use information_schema::columns::dsl::*;
        columns
            .select((table_name, column_name))
            .filter(table_schema.eq("public"))
            .order_by((table_name, ordinal_position))
            .load(conn)
            .unwrap()
    }
}
<commit_msg>Simplify topological sort implementation.<commit_after>use std::{
    collections::{BTreeMap, VecDeque},
    fs::File,
    path::{Path, PathBuf},
};

use crate::{background_jobs::Environment, uploaders::Uploader, util::errors::std_error_no_send};

use scopeguard::defer;
use swirl::PerformError;

/// Create CSV dumps of the public information in the database, wrap them in a
/// tarball and upload to S3.
#[swirl::background_job]
pub fn dump_db(
    env: &Environment,
    database_url: String,
    target_name: String,
) -> Result<(), PerformError> {
    let timestamp = chrono::Utc::now().format("%Y-%m-%d-%H%M%S").to_string();
    let export_dir = std::env::temp_dir().join("dump-db").join(timestamp);
    std::fs::create_dir_all(&export_dir)?;
    defer! {{
        std::fs::remove_dir_all(&export_dir).unwrap();
    }}
    let visibility_config: VisibilityConfig = toml::from_str(include_str!("dump-db.toml")).unwrap();
    let export_script = export_dir.join("export.sql");
    let import_script = export_dir.join("import.sql");
    visibility_config.gen_psql_scripts(&export_script, &import_script)?;
    run_psql(&database_url, &export_script)?;
    let tarball = create_tarball(&export_dir)?;
    defer! {{
        std::fs::remove_file(&tarball).unwrap();
    }}
    upload_tarball(&tarball, &target_name, &env.uploader)?;
    println!("Database dump uploaded to {}.", &target_name);
    Ok(())
}

/// An enum indicating whether a column is included in the database dumps.
/// Public columns are included, private are not.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
enum ColumnVisibility {
    Private,
    Public,
}

/// Filtering information for a single table. The `dependencies` field is only
/// used to determine the order of the tables in the generated import script,
/// and should list all tables the current tables refers to with foreign key
/// constraints on public columns. The `filter` field is a valid SQL expression
/// used in a `WHERE` clause to filter the rows of the table. The `columns`
/// field maps column names to their respective visibilities.
#[derive(Clone, Debug, Deserialize)]
struct TableConfig {
    #[serde(default)]
    dependencies: Vec<String>,
    filter: Option<String>,
    columns: BTreeMap<String, ColumnVisibility>,
}

/// Subset of the configuration data to be passed on to the Handlbars template.
#[derive(Debug, Serialize)]
struct HandlebarsTableContext<'a> {
    name: &'a str,
    filter: Option<&'a str>,
    columns: String,
}

impl TableConfig {
    fn handlebars_context<'a>(&'a self, name: &'a str) -> Option<HandlebarsTableContext<'a>> {
        let columns = self
            .columns
            .iter()
            .filter(|&(_, &vis)| vis == ColumnVisibility::Public)
            .map(|(col, _)| format!("\"{}\"", col))
            .collect::<Vec<String>>()
            .join(", ");
        if columns.is_empty() {
            None
        } else {
            Some(HandlebarsTableContext {
                name,
                filter: self.filter.as_ref().map(String::as_str),
                columns,
            })
        }
    }
}

/// Maps table names to the respective configurations. Used to load `dump_db.toml`.
#[derive(Clone, Debug, Deserialize)]
#[serde(transparent)]
struct VisibilityConfig(BTreeMap<String, TableConfig>);

/// Subset of the configuration data to be passed on to the Handlbars template.
#[derive(Debug, Serialize)]
struct HandlebarsContext<'a> {
    tables: Vec<HandlebarsTableContext<'a>>,
}

impl VisibilityConfig {
    /// Sort the tables in a way that dependencies come before dependent tables.
    ///
    /// Returns a vector of table names.
    fn topological_sort(&self) -> Vec<&str> {
        let mut result = Vec::new();
        let mut num_deps = BTreeMap::new();
        let mut rev_deps: BTreeMap<_, Vec<_>> = BTreeMap::new();
        for (table, config) in self.0.iter() {
            num_deps.insert(table.as_str(), config.dependencies.len());
            for dep in &config.dependencies {
                rev_deps.entry(dep.as_str()).or_default().push(table.as_str());
            }
        }
        let mut ready: VecDeque<&str> = num_deps
            .iter()
            .filter(|(_, &count)| count == 0)
            .map(|(&table, _)| table)
            .collect();
        while let Some(table) = ready.pop_front() {
            result.push(table);
            for dep in rev_deps.get(table).iter().copied().flatten() {
                *num_deps.get_mut(dep).unwrap() -= 1;
                if num_deps[dep] == 0 {
                    ready.push_back(dep);
                }
            }
        }
        assert_eq!(
            self.0.len(),
            result.len(),
            "circular dependencies in database dump configuration detected",
        );
        result
    }

    fn handlebars_context(&self) -> HandlebarsContext<'_> {
        let tables = self
            .topological_sort()
            .into_iter()
            .filter_map(|table| self.0[table].handlebars_context(table))
            .collect();
        HandlebarsContext { tables }
    }

    fn gen_psql_scripts(
        &self,
        export_script: &Path,
        import_script: &Path,
    ) -> Result<(), PerformError> {
        let context = self.handlebars_context();
        let mut handlebars = handlebars::Handlebars::new();
        handlebars.register_escape_fn(handlebars::no_escape);
        let export_sql = File::create(export_script)?;
        handlebars.render_template_to_write(
            include_str!("dump-export.hbs"),
            &context,
            export_sql,
        )?;
        let import_sql = File::create(import_script)?;
        handlebars.render_template_to_write(
            include_str!("dump-import.hbs"),
            &context,
            import_sql,
        )?;
        Ok(())
    }
}

fn run_psql(database_url: &str, export_script: &Path) -> Result<(), PerformError> {
    use std::process::{Command, Stdio};

    let psql_script = File::open(export_script)?;
    let psql = Command::new("psql")
        .arg(database_url)
        .current_dir(export_script.parent().unwrap())
        .stdin(psql_script)
        .stdout(Stdio::null())
        .stderr(Stdio::piped())
        .spawn()?;
    let output = psql.wait_with_output()?;
    if !output.stderr.is_empty() {
        Err(format!(
            "Error while executing psql: {}",
            String::from_utf8_lossy(&output.stderr)
        ))?;
    }
    if !output.status.success() {
        Err("psql did not finish successfully.")?;
    }
    Ok(())
}

fn create_tarball(export_dir: &Path) -> Result<PathBuf, PerformError> {
    let tarball_name = export_dir.with_extension("tar.gz");
    let tarball = File::create(&tarball_name)?;
    let encoder = flate2::write::GzEncoder::new(tarball, flate2::Compression::default());
    let mut archive = tar::Builder::new(encoder);
    archive.append_dir_all(export_dir.file_name().unwrap(), &export_dir)?;
    Ok(tarball_name)
}

fn upload_tarball(
    tarball: &Path,
    target_name: &str,
    uploader: &Uploader,
) -> Result<(), PerformError> {
    let client = reqwest::Client::new();
    let tarfile = File::open(tarball)?;
    let content_length = tarfile.metadata()?.len();
    // TODO Figure out the correct content type.
    uploader
        .upload(
            &client,
            target_name,
            tarfile,
            content_length,
            "application/gzip",
        )
        .map_err(std_error_no_send)?;
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_util::pg_connection;
    use diesel::prelude::*;
    use std::collections::HashSet;

    /// Test whether the schema in the visibility configuration matches the test database.
    #[test]
    fn check_visibility_config() {
        let conn = pg_connection();
        let db_columns: HashSet<_> = get_db_columns(&conn)
            .into_iter()
            .map(|c| (c.table_name, c.column_name))
            .collect();
        let visibility_config: VisibilityConfig =
            toml::from_str(include_str!("dump-db.toml")).unwrap();
        let vis_columns: HashSet<_> = visibility_config
            .0
            .iter()
            .flat_map(|(table, config)| {
                config
                    .columns
                    .iter()
                    .map(move |(column, _)| (table.clone(), column.clone()))
            })
            .collect();
        let mut errors = vec![];
        for (table, col) in db_columns.difference(&vis_columns) {
            errors.push(format!(
                "No visibility information for columns {}.{}.",
                table, col
            ));
        }
        for (table, col) in vis_columns.difference(&db_columns) {
            errors.push(format!(
                "Column {}.{} does not exist in the database.",
                table, col
            ));
        }
        assert!(
            errors.is_empty(),
            "The visibility configuration does not match the database schema:\n{}",
            errors.join("\n  - "),
        );
    }

    mod information_schema {
        table! {
            information_schema.columns (table_schema, table_name, column_name) {
                table_schema -> Text,
                table_name -> Text,
                column_name -> Text,
                ordinal_position -> Integer,
            }
        }
    }

    #[derive(Debug, PartialEq, Queryable)]
    struct Column {
        table_name: String,
        column_name: String,
    }

    fn get_db_columns(conn: &PgConnection) -> Vec<Column> {
        use information_schema::columns::dsl::*;
        columns
            .select((table_name, column_name))
            .filter(table_schema.eq("public"))
            .order_by((table_name, ordinal_position))
            .load(conn)
            .unwrap()
    }
}
<|endoftext|>


<commit_before>pub use super::*;

use crate::dataflow::generic::{AnalysisDomain, GenKill, GenKillAnalysis};
use rustc::mir::visit::Visitor;
use rustc::mir::*;
use rustc::ty::{ParamEnv, TyCtxt};
use rustc_span::DUMMY_SP;

pub type MaybeMutBorrowedLocals<'mir, 'tcx> = MaybeBorrowedLocals<MutBorrow<'mir, 'tcx>>;

/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
/// to a given local.
///
/// The `K` parameter determines what kind of borrows are tracked. By default,
/// `MaybeBorrowedLocals` looks for *any* borrow of a local. If you are only interested in borrows
/// that might allow mutation, use the `MaybeMutBorrowedLocals` type alias instead.
///
/// At present, this is used as a very limited form of alias analysis. For example,
/// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for
/// immovable generators. `MaybeMutBorrowedLocals` is used during const checking to prove that a
/// local has not been mutated via indirect assignment (e.g., `*p = 42`), the side-effects of a
/// function call or inline assembly.
pub struct MaybeBorrowedLocals<K = AnyBorrow> {
    kind: K,
    ignore_borrow_on_drop: bool,
}

impl MaybeBorrowedLocals {
    /// A dataflow analysis that records whether a pointer or reference exists that may alias the
    /// given local.
    pub fn all_borrows() -> Self {
        MaybeBorrowedLocals { kind: AnyBorrow, ignore_borrow_on_drop: false }
    }
}

impl MaybeMutBorrowedLocals<'mir, 'tcx> {
    /// A dataflow analysis that records whether a pointer or reference exists that may *mutably*
    /// alias the given local.
    ///
    /// This includes `&mut` and pointers derived from an `&mut`, as well as shared borrows of
    /// types with interior mutability.
    pub fn mut_borrows_only(
        tcx: TyCtxt<'tcx>,
        body: &'mir mir::Body<'tcx>,
        param_env: ParamEnv<'tcx>,
    ) -> Self {
        MaybeBorrowedLocals {
            kind: MutBorrow { body, tcx, param_env },
            ignore_borrow_on_drop: false,
        }
    }
}

impl<K> MaybeBorrowedLocals<K> {
    /// During dataflow analysis, ignore the borrow that may occur when a place is dropped.
    ///
    /// Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut self` as a
    /// parameter. In the general case, a drop impl could launder that reference into the
    /// surrounding environment through a raw pointer, thus creating a valid `*mut` pointing to the
    /// dropped local. We are not yet willing to declare this particular case UB, so we must treat
    /// all dropped locals as mutably borrowed for now. See discussion on [#61069].
    ///
    /// In some contexts, we know that this borrow will never occur. For example, during
    /// const-eval, custom drop glue cannot be run. Code that calls this should document the
    /// assumptions that justify `Drop` terminators in this way.
    ///
    /// [#61069]: https://github.com/rust-lang/rust/pull/61069
    pub fn unsound_ignore_borrow_on_drop(self) -> Self {
        MaybeBorrowedLocals { ignore_borrow_on_drop: true, ..self }
    }

    fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T, K> {
        TransferFunction {
            kind: &self.kind,
            trans,
            ignore_borrow_on_drop: self.ignore_borrow_on_drop,
        }
    }
}

impl<K> AnalysisDomain<'tcx> for MaybeBorrowedLocals<K>
where
    K: BorrowAnalysisKind<'tcx>,
{
    type Idx = Local;

    const NAME: &'static str = K::ANALYSIS_NAME;

    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
        body.local_decls().len()
    }

    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
        // No locals are aliased on function entry
    }
}

impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K>
where
    K: BorrowAnalysisKind<'tcx>,
{
    fn statement_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        statement: &mir::Statement<'tcx>,
        location: Location,
    ) {
        self.transfer_function(trans).visit_statement(statement, location);
    }

    fn terminator_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        terminator: &mir::Terminator<'tcx>,
        location: Location,
    ) {
        self.transfer_function(trans).visit_terminator(terminator, location);
    }

    fn call_return_effect(
        &self,
        _trans: &mut impl GenKill<Self::Idx>,
        _block: mir::BasicBlock,
        _func: &mir::Operand<'tcx>,
        _args: &[mir::Operand<'tcx>],
        _dest_place: &mir::Place<'tcx>,
    ) {
    }
}

impl<K> BottomValue for MaybeBorrowedLocals<K> {
    // bottom = unborrowed
    const BOTTOM_VALUE: bool = false;
}

/// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
struct TransferFunction<'a, T, K> {
    trans: &'a mut T,
    kind: &'a K,
    ignore_borrow_on_drop: bool,
}

impl<T, K> Visitor<'tcx> for TransferFunction<'a, T, K>
where
    T: GenKill<Local>,
    K: BorrowAnalysisKind<'tcx>,
{
    fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) {
        self.super_statement(stmt, location);

        // When we reach a `StorageDead` statement, we can assume that any pointers to this memory
        // are now invalid.
        if let StatementKind::StorageDead(local) = stmt.kind {
            self.trans.kill(local);
        }
    }

    fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
        self.super_rvalue(rvalue, location);

        match rvalue {
            mir::Rvalue::AddressOf(mt, borrowed_place) => {
                if !borrowed_place.is_indirect() && self.kind.in_address_of(*mt, borrowed_place) {
                    self.trans.gen(borrowed_place.local);
                }
            }

            mir::Rvalue::Ref(_, kind, borrowed_place) => {
                if !borrowed_place.is_indirect() && self.kind.in_ref(*kind, borrowed_place) {
                    self.trans.gen(borrowed_place.local);
                }
            }

            mir::Rvalue::Cast(..)
            | mir::Rvalue::Use(..)
            | mir::Rvalue::Repeat(..)
            | mir::Rvalue::Len(..)
            | mir::Rvalue::BinaryOp(..)
            | mir::Rvalue::CheckedBinaryOp(..)
            | mir::Rvalue::NullaryOp(..)
            | mir::Rvalue::UnaryOp(..)
            | mir::Rvalue::Discriminant(..)
            | mir::Rvalue::Aggregate(..) => {}
        }
    }

    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
        self.super_terminator(terminator, location);

        match terminator.kind {
            mir::TerminatorKind::Drop { location: dropped_place, .. }
            | mir::TerminatorKind::DropAndReplace { location: dropped_place, .. } => {
                // See documentation for `unsound_ignore_borrow_on_drop` for an explanation.
                if !self.ignore_borrow_on_drop {
                    self.trans.gen(dropped_place.local);
                }
            }

            TerminatorKind::Abort
            | TerminatorKind::Assert { .. }
            | TerminatorKind::Call { .. }
            | TerminatorKind::FalseEdges { .. }
            | TerminatorKind::FalseUnwind { .. }
            | TerminatorKind::GeneratorDrop
            | TerminatorKind::Goto { .. }
            | TerminatorKind::Resume
            | TerminatorKind::Return
            | TerminatorKind::SwitchInt { .. }
            | TerminatorKind::Unreachable
            | TerminatorKind::Yield { .. } => {}
        }
    }
}

pub struct AnyBorrow;

pub struct MutBorrow<'mir, 'tcx> {
    tcx: TyCtxt<'tcx>,
    body: &'mir Body<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl MutBorrow<'mir, 'tcx> {
    /// `&` and `&raw` only allow mutation if the borrowed place is `!Freeze`.
    ///
    /// This assumes that it is UB to take the address of a struct field whose type is
    /// `Freeze`, then use pointer arithmetic to derive a pointer to a *different* field of
    /// that same struct whose type is `!Freeze`. If we decide that this is not UB, we will
    /// have to check the type of the borrowed **local** instead of the borrowed **place**
    /// below. See [rust-lang/unsafe-code-guidelines#134].
    ///
    /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134
    fn shared_borrow_allows_mutation(&self, place: &Place<'tcx>) -> bool {
        !place.ty(self.body, self.tcx).ty.is_freeze(self.tcx, self.param_env, DUMMY_SP)
    }
}

pub trait BorrowAnalysisKind<'tcx> {
    const ANALYSIS_NAME: &'static str;

    fn in_address_of(&self, mt: Mutability, place: &Place<'tcx>) -> bool;
    fn in_ref(&self, kind: mir::BorrowKind, place: &Place<'tcx>) -> bool;
}

impl BorrowAnalysisKind<'tcx> for AnyBorrow {
    const ANALYSIS_NAME: &'static str = "maybe_borrowed_locals";

    fn in_ref(&self, _: mir::BorrowKind, _: &Place<'_>) -> bool {
        true
    }
    fn in_address_of(&self, _: Mutability, _: &Place<'_>) -> bool {
        true
    }
}

impl BorrowAnalysisKind<'tcx> for MutBorrow<'mir, 'tcx> {
    const ANALYSIS_NAME: &'static str = "maybe_mut_borrowed_locals";

    fn in_ref(&self, kind: mir::BorrowKind, place: &Place<'tcx>) -> bool {
        match kind {
            mir::BorrowKind::Mut { .. } => true,
            mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => {
                self.shared_borrow_allows_mutation(place)
            }
        }
    }

    fn in_address_of(&self, mt: Mutability, place: &Place<'tcx>) -> bool {
        match mt {
            Mutability::Mut => true,
            Mutability::Not => self.shared_borrow_allows_mutation(place),
        }
    }
}
<commit_msg>Fix typo in comment<commit_after>pub use super::*;

use crate::dataflow::generic::{AnalysisDomain, GenKill, GenKillAnalysis};
use rustc::mir::visit::Visitor;
use rustc::mir::*;
use rustc::ty::{ParamEnv, TyCtxt};
use rustc_span::DUMMY_SP;

pub type MaybeMutBorrowedLocals<'mir, 'tcx> = MaybeBorrowedLocals<MutBorrow<'mir, 'tcx>>;

/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
/// to a given local.
///
/// The `K` parameter determines what kind of borrows are tracked. By default,
/// `MaybeBorrowedLocals` looks for *any* borrow of a local. If you are only interested in borrows
/// that might allow mutation, use the `MaybeMutBorrowedLocals` type alias instead.
///
/// At present, this is used as a very limited form of alias analysis. For example,
/// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for
/// immovable generators. `MaybeMutBorrowedLocals` is used during const checking to prove that a
/// local has not been mutated via indirect assignment (e.g., `*p = 42`), the side-effects of a
/// function call or inline assembly.
pub struct MaybeBorrowedLocals<K = AnyBorrow> {
    kind: K,
    ignore_borrow_on_drop: bool,
}

impl MaybeBorrowedLocals {
    /// A dataflow analysis that records whether a pointer or reference exists that may alias the
    /// given local.
    pub fn all_borrows() -> Self {
        MaybeBorrowedLocals { kind: AnyBorrow, ignore_borrow_on_drop: false }
    }
}

impl MaybeMutBorrowedLocals<'mir, 'tcx> {
    /// A dataflow analysis that records whether a pointer or reference exists that may *mutably*
    /// alias the given local.
    ///
    /// This includes `&mut` and pointers derived from an `&mut`, as well as shared borrows of
    /// types with interior mutability.
    pub fn mut_borrows_only(
        tcx: TyCtxt<'tcx>,
        body: &'mir mir::Body<'tcx>,
        param_env: ParamEnv<'tcx>,
    ) -> Self {
        MaybeBorrowedLocals {
            kind: MutBorrow { body, tcx, param_env },
            ignore_borrow_on_drop: false,
        }
    }
}

impl<K> MaybeBorrowedLocals<K> {
    /// During dataflow analysis, ignore the borrow that may occur when a place is dropped.
    ///
    /// Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut self` as a
    /// parameter. In the general case, a drop impl could launder that reference into the
    /// surrounding environment through a raw pointer, thus creating a valid `*mut` pointing to the
    /// dropped local. We are not yet willing to declare this particular case UB, so we must treat
    /// all dropped locals as mutably borrowed for now. See discussion on [#61069].
    ///
    /// In some contexts, we know that this borrow will never occur. For example, during
    /// const-eval, custom drop glue cannot be run. Code that calls this should document the
    /// assumptions that justify ignoring `Drop` terminators in this way.
    ///
    /// [#61069]: https://github.com/rust-lang/rust/pull/61069
    pub fn unsound_ignore_borrow_on_drop(self) -> Self {
        MaybeBorrowedLocals { ignore_borrow_on_drop: true, ..self }
    }

    fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T, K> {
        TransferFunction {
            kind: &self.kind,
            trans,
            ignore_borrow_on_drop: self.ignore_borrow_on_drop,
        }
    }
}

impl<K> AnalysisDomain<'tcx> for MaybeBorrowedLocals<K>
where
    K: BorrowAnalysisKind<'tcx>,
{
    type Idx = Local;

    const NAME: &'static str = K::ANALYSIS_NAME;

    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
        body.local_decls().len()
    }

    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
        // No locals are aliased on function entry
    }
}

impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K>
where
    K: BorrowAnalysisKind<'tcx>,
{
    fn statement_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        statement: &mir::Statement<'tcx>,
        location: Location,
    ) {
        self.transfer_function(trans).visit_statement(statement, location);
    }

    fn terminator_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        terminator: &mir::Terminator<'tcx>,
        location: Location,
    ) {
        self.transfer_function(trans).visit_terminator(terminator, location);
    }

    fn call_return_effect(
        &self,
        _trans: &mut impl GenKill<Self::Idx>,
        _block: mir::BasicBlock,
        _func: &mir::Operand<'tcx>,
        _args: &[mir::Operand<'tcx>],
        _dest_place: &mir::Place<'tcx>,
    ) {
    }
}

impl<K> BottomValue for MaybeBorrowedLocals<K> {
    // bottom = unborrowed
    const BOTTOM_VALUE: bool = false;
}

/// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
struct TransferFunction<'a, T, K> {
    trans: &'a mut T,
    kind: &'a K,
    ignore_borrow_on_drop: bool,
}

impl<T, K> Visitor<'tcx> for TransferFunction<'a, T, K>
where
    T: GenKill<Local>,
    K: BorrowAnalysisKind<'tcx>,
{
    fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) {
        self.super_statement(stmt, location);

        // When we reach a `StorageDead` statement, we can assume that any pointers to this memory
        // are now invalid.
        if let StatementKind::StorageDead(local) = stmt.kind {
            self.trans.kill(local);
        }
    }

    fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
        self.super_rvalue(rvalue, location);

        match rvalue {
            mir::Rvalue::AddressOf(mt, borrowed_place) => {
                if !borrowed_place.is_indirect() && self.kind.in_address_of(*mt, borrowed_place) {
                    self.trans.gen(borrowed_place.local);
                }
            }

            mir::Rvalue::Ref(_, kind, borrowed_place) => {
                if !borrowed_place.is_indirect() && self.kind.in_ref(*kind, borrowed_place) {
                    self.trans.gen(borrowed_place.local);
                }
            }

            mir::Rvalue::Cast(..)
            | mir::Rvalue::Use(..)
            | mir::Rvalue::Repeat(..)
            | mir::Rvalue::Len(..)
            | mir::Rvalue::BinaryOp(..)
            | mir::Rvalue::CheckedBinaryOp(..)
            | mir::Rvalue::NullaryOp(..)
            | mir::Rvalue::UnaryOp(..)
            | mir::Rvalue::Discriminant(..)
            | mir::Rvalue::Aggregate(..) => {}
        }
    }

    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
        self.super_terminator(terminator, location);

        match terminator.kind {
            mir::TerminatorKind::Drop { location: dropped_place, .. }
            | mir::TerminatorKind::DropAndReplace { location: dropped_place, .. } => {
                // See documentation for `unsound_ignore_borrow_on_drop` for an explanation.
                if !self.ignore_borrow_on_drop {
                    self.trans.gen(dropped_place.local);
                }
            }

            TerminatorKind::Abort
            | TerminatorKind::Assert { .. }
            | TerminatorKind::Call { .. }
            | TerminatorKind::FalseEdges { .. }
            | TerminatorKind::FalseUnwind { .. }
            | TerminatorKind::GeneratorDrop
            | TerminatorKind::Goto { .. }
            | TerminatorKind::Resume
            | TerminatorKind::Return
            | TerminatorKind::SwitchInt { .. }
            | TerminatorKind::Unreachable
            | TerminatorKind::Yield { .. } => {}
        }
    }
}

pub struct AnyBorrow;

pub struct MutBorrow<'mir, 'tcx> {
    tcx: TyCtxt<'tcx>,
    body: &'mir Body<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl MutBorrow<'mir, 'tcx> {
    /// `&` and `&raw` only allow mutation if the borrowed place is `!Freeze`.
    ///
    /// This assumes that it is UB to take the address of a struct field whose type is
    /// `Freeze`, then use pointer arithmetic to derive a pointer to a *different* field of
    /// that same struct whose type is `!Freeze`. If we decide that this is not UB, we will
    /// have to check the type of the borrowed **local** instead of the borrowed **place**
    /// below. See [rust-lang/unsafe-code-guidelines#134].
    ///
    /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134
    fn shared_borrow_allows_mutation(&self, place: &Place<'tcx>) -> bool {
        !place.ty(self.body, self.tcx).ty.is_freeze(self.tcx, self.param_env, DUMMY_SP)
    }
}

pub trait BorrowAnalysisKind<'tcx> {
    const ANALYSIS_NAME: &'static str;

    fn in_address_of(&self, mt: Mutability, place: &Place<'tcx>) -> bool;
    fn in_ref(&self, kind: mir::BorrowKind, place: &Place<'tcx>) -> bool;
}

impl BorrowAnalysisKind<'tcx> for AnyBorrow {
    const ANALYSIS_NAME: &'static str = "maybe_borrowed_locals";

    fn in_ref(&self, _: mir::BorrowKind, _: &Place<'_>) -> bool {
        true
    }
    fn in_address_of(&self, _: Mutability, _: &Place<'_>) -> bool {
        true
    }
}

impl BorrowAnalysisKind<'tcx> for MutBorrow<'mir, 'tcx> {
    const ANALYSIS_NAME: &'static str = "maybe_mut_borrowed_locals";

    fn in_ref(&self, kind: mir::BorrowKind, place: &Place<'tcx>) -> bool {
        match kind {
            mir::BorrowKind::Mut { .. } => true,
            mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => {
                self.shared_borrow_allows_mutation(place)
            }
        }
    }

    fn in_address_of(&self, mt: Mutability, place: &Place<'tcx>) -> bool {
        match mt {
            Mutability::Mut => true,
            Mutability::Not => self.shared_borrow_allows_mutation(place),
        }
    }
}
<|endoftext|>