<commit_before>//! Just a dumping ground for cli stuff

use multirust::{Cfg, Result, Notification, Toolchain, Error};
use multirust_utils::{self, utils};
use multirust_utils::notify::NotificationLevel;
use self_update;
use std::ffi::OsStr;
use std::io::{Write, Read, BufRead};
use std::process::{self, Command};
use std;
use tty;
use term;

pub fn confirm(question: &str, default: bool) -> Result<bool> {
    print!("{} ", question);
    let _ = std::io::stdout().flush();
    let input = try!(read_line());

    let r = match &*input {
        "y" | "Y" => true,
        "n" | "N" => false,
        "" => default,
        _ => false,
    };

    println!("");

    Ok(r)
}

fn read_line() -> Result<String> {
    let stdin = std::io::stdin();
    let stdin = stdin.lock();
    let mut lines = stdin.lines();
    lines.next().and_then(|l| l.ok()).ok_or(Error::ReadStdin)
}

pub fn wait_for_keypress() -> Result<()> {
    let stdin = std::io::stdin();
    if stdin.bytes().next().is_some() {
        Ok(())
    } else {
        Err(Error::ReadStdin)
    }
}

pub fn set_globals(verbose: bool) -> Result<Cfg> {
    use download_tracker::DownloadTracker;
    use std::cell::RefCell;

    let download_tracker = RefCell::new(DownloadTracker::new());

    Cfg::from_env(shared_ntfy!(move |n: Notification| {
        if download_tracker.borrow_mut().handle_notification(&n) {
            return;
        }

        match n.level() {
            NotificationLevel::Verbose => {
                if verbose {
                    verbose!("{}", n);
                }
            }
            NotificationLevel::Info => {
                info!("{}", n);
            }
            NotificationLevel::Warn => {
                warn!("{}", n);
            }
            NotificationLevel::Error => {
                err!("{}", n);
            }
        }
    }))

}

pub fn run_inner<S: AsRef<OsStr>>(mut command: Command,
                                  args: &[S]) -> Result<()> {
    command.args(&args[1..]);
    // FIXME rust-lang/rust#32254. It's not clear to me
    // when and why this is needed.
    command.stdin(process::Stdio::inherit());

    let status = command.status();

    match status {
        Ok(status) => {
            // Ensure correct exit code is returned
            let code = status.code().unwrap_or(1);
            process::exit(code);
        }
        Err(e) => {
            Err(multirust_utils::Error::RunningCommand {
                name: args[0].as_ref().to_owned(),
                error: multirust_utils::raw::CommandError::Io(e),
            }.into())
        }
    }
}

pub fn show_channel_version(cfg: &Cfg, name: &str) -> Result<()> {
    let mut t = term::stdout().unwrap();
    if tty::stdout_isatty() { let _ = t.fg(term::color::BRIGHT_WHITE); }
    if tty::stdout_isatty() { let _ = t.bg(term::color::BLACK); }
    let _ = write!(t, "{}", name);
    if tty::stdout_isatty() { let _ = t.reset(); }
    let _ = writeln!(t, " revision:");
    println!("");
    try!(show_tool_versions(&try!(cfg.get_toolchain(&name, false))));
    println!("");
    Ok(())
}

pub fn show_channel_update(cfg: &Cfg, name: &str,
                           updated: Result<bool>) -> Result<()> {
    let tty = tty::stdout_isatty();
    let mut t = term::stdout().unwrap();
    match updated {
        Ok(true) => {
            if tty { let _ = t.fg(term::color::BRIGHT_GREEN); }
            let _ = write!(t, "{} updated", name);
        }
        Ok(false) => {
            if tty { let _ = t.fg(term::color::BRIGHT_WHITE); }
            let _ = write!(t, "{} unchanged", name);
        }
        Err(_) => {
            if tty { let _ = t.fg(term::color::BRIGHT_RED); }
            let _ = write!(t, "{} update failed", name);
        }
    }
    if tty {let _ = t.reset(); }
    println!(":");
    println!("");
    try!(show_tool_versions(&try!(cfg.get_toolchain(&name, false))));
    println!("");
    Ok(())
}

pub fn update_all_channels(cfg: &Cfg, self_update: bool) -> Result<()> {

    let toolchains = try!(cfg.update_all_channels());

    if toolchains.is_empty() {
        info!("no updatable toolchains installed");
    }

    let setup_path = if self_update {
        try!(self_update::prepare_update())
    } else {
        None
    };

    if !toolchains.is_empty() {
        println!("");

        for (name, result) in toolchains {
            try!(show_channel_update(cfg, &name, result));
        }
    }

    if let Some(ref setup_path) = setup_path {
        try!(self_update::run_update(setup_path));

        unreachable!(); // update exits on success
    }

    Ok(())
}

pub fn show_tool_versions(toolchain: &Toolchain) -> Result<()> {
    if toolchain.exists() {
        let rustc_path = toolchain.binary_file("rustc");
        let cargo_path = toolchain.binary_file("cargo");

        if utils::is_file(&rustc_path) {
            let mut cmd = Command::new(&rustc_path);
            cmd.arg("--version");
            toolchain.set_ldpath(&mut cmd);

            if utils::cmd_status("rustc", &mut cmd).is_err() {
                println!("(failed to run rustc)");
            }
        } else {
            println!("(no rustc command in toolchain?)");
        }
        if utils::is_file(&cargo_path) {
            let mut cmd = Command::new(&cargo_path);
            cmd.arg("--version");
            // cargo invokes rustc during --version, this
            // makes sure it can find it since it may not
            // be on the `PATH` and multirust does not
            // manipulate `PATH`.
            cmd.env("RUSTC", rustc_path);
            toolchain.set_ldpath(&mut cmd);

            if utils::cmd_status("cargo", &mut cmd).is_err() {
                println!("(failed to run cargo)");
            }
        } else {
            println!("(no cargo command in toolchain?)");
        }
    } else {
        println!("(toolchain not installed)");
    }
    Ok(())
}

pub fn list_targets(toolchain: &Toolchain) -> Result<()> {
    for component in try!(toolchain.list_components()) {
        if component.component.pkg == "rust-std" {
            if component.required {
                println!("{} (default)", component.component.target);
            } else if component.installed {
                println!("{} (installed)", component.component.target);
            } else {
                println!("{}", component.component.target);
            }
        }
    }

    Ok(())
}

pub fn list_toolchains(cfg: &Cfg) -> Result<()> {
    let mut toolchains = try!(cfg.list_toolchains());

    toolchains.sort();

    if toolchains.is_empty() {
        println!("no installed toolchains");
    } else {
        for toolchain in toolchains {
            println!("{}", &toolchain);
        }
    }
    Ok(())
}

pub fn list_overrides(cfg: &Cfg) -> Result<()> {
    let mut overrides = try!(cfg.override_db.list());

    overrides.sort();

    if overrides.is_empty() {
        println!("no overrides");
    } else {
        for o in overrides {
            println!("{}", o);
        }
    }
    Ok(())
}

<commit_msg>Stop using BRIGHT_WHITE for terminal output. Fixes #139<commit_after>//! Just a dumping ground for cli stuff

use multirust::{Cfg, Result, Notification, Toolchain, Error};
use multirust_utils::{self, utils};
use multirust_utils::notify::NotificationLevel;
use self_update;
use std::ffi::OsStr;
use std::io::{Write, Read, BufRead};
use std::process::{self, Command};
use std;
use tty;
use term;

pub fn confirm(question: &str, default: bool) -> Result<bool> {
    print!("{} ", question);
    let _ = std::io::stdout().flush();
    let input = try!(read_line());

    let r = match &*input {
        "y" | "Y" => true,
        "n" | "N" => false,
        "" => default,
        _ => false,
    };

    println!("");

    Ok(r)
}

fn read_line() -> Result<String> {
    let stdin = std::io::stdin();
    let stdin = stdin.lock();
    let mut lines = stdin.lines();
    lines.next().and_then(|l| l.ok()).ok_or(Error::ReadStdin)
}

pub fn wait_for_keypress() -> Result<()> {
    let stdin = std::io::stdin();
    if stdin.bytes().next().is_some() {
        Ok(())
    } else {
        Err(Error::ReadStdin)
    }
}

pub fn set_globals(verbose: bool) -> Result<Cfg> {
    use download_tracker::DownloadTracker;
    use std::cell::RefCell;

    let download_tracker = RefCell::new(DownloadTracker::new());

    Cfg::from_env(shared_ntfy!(move |n: Notification| {
        if download_tracker.borrow_mut().handle_notification(&n) {
            return;
        }

        match n.level() {
            NotificationLevel::Verbose => {
                if verbose {
                    verbose!("{}", n);
                }
            }
            NotificationLevel::Info => {
                info!("{}", n);
            }
            NotificationLevel::Warn => {
                warn!("{}", n);
            }
            NotificationLevel::Error => {
                err!("{}", n);
            }
        }
    }))

}

pub fn run_inner<S: AsRef<OsStr>>(mut command: Command,
                                  args: &[S]) -> Result<()> {
    command.args(&args[1..]);
    // FIXME rust-lang/rust#32254. It's not clear to me
    // when and why this is needed.
    command.stdin(process::Stdio::inherit());

    let status = command.status();

    match status {
        Ok(status) => {
            // Ensure correct exit code is returned
            let code = status.code().unwrap_or(1);
            process::exit(code);
        }
        Err(e) => {
            Err(multirust_utils::Error::RunningCommand {
                name: args[0].as_ref().to_owned(),
                error: multirust_utils::raw::CommandError::Io(e),
            }.into())
        }
    }
}

pub fn show_channel_version(cfg: &Cfg, name: &str) -> Result<()> {
    println!("{} revision:", name);
    println!("");
    try!(show_tool_versions(&try!(cfg.get_toolchain(&name, false))));
    println!("");
    Ok(())
}

pub fn show_channel_update(cfg: &Cfg, name: &str,
                           updated: Result<bool>) -> Result<()> {
    let tty = tty::stdout_isatty();
    let mut t = term::stdout().unwrap();
    match updated {
        Ok(true) => {
            if tty { let _ = t.fg(term::color::BRIGHT_GREEN); }
            let _ = write!(t, "{} updated", name);
        }
        Ok(false) => {
            let _ = write!(t, "{} unchanged", name);
        }
        Err(_) => {
            if tty { let _ = t.fg(term::color::BRIGHT_RED); }
            let _ = write!(t, "{} update failed", name);
        }
    }
    if tty {let _ = t.reset(); }
    println!(":");
    println!("");
    try!(show_tool_versions(&try!(cfg.get_toolchain(&name, false))));
    println!("");
    Ok(())
}

pub fn update_all_channels(cfg: &Cfg, self_update: bool) -> Result<()> {

    let toolchains = try!(cfg.update_all_channels());

    if toolchains.is_empty() {
        info!("no updatable toolchains installed");
    }

    let setup_path = if self_update {
        try!(self_update::prepare_update())
    } else {
        None
    };

    if !toolchains.is_empty() {
        println!("");

        for (name, result) in toolchains {
            try!(show_channel_update(cfg, &name, result));
        }
    }

    if let Some(ref setup_path) = setup_path {
        try!(self_update::run_update(setup_path));

        unreachable!(); // update exits on success
    }

    Ok(())
}

pub fn show_tool_versions(toolchain: &Toolchain) -> Result<()> {
    if toolchain.exists() {
        let rustc_path = toolchain.binary_file("rustc");
        let cargo_path = toolchain.binary_file("cargo");

        if utils::is_file(&rustc_path) {
            let mut cmd = Command::new(&rustc_path);
            cmd.arg("--version");
            toolchain.set_ldpath(&mut cmd);

            if utils::cmd_status("rustc", &mut cmd).is_err() {
                println!("(failed to run rustc)");
            }
        } else {
            println!("(no rustc command in toolchain?)");
        }
        if utils::is_file(&cargo_path) {
            let mut cmd = Command::new(&cargo_path);
            cmd.arg("--version");
            // cargo invokes rustc during --version, this
            // makes sure it can find it since it may not
            // be on the `PATH` and multirust does not
            // manipulate `PATH`.
            cmd.env("RUSTC", rustc_path);
            toolchain.set_ldpath(&mut cmd);

            if utils::cmd_status("cargo", &mut cmd).is_err() {
                println!("(failed to run cargo)");
            }
        } else {
            println!("(no cargo command in toolchain?)");
        }
    } else {
        println!("(toolchain not installed)");
    }
    Ok(())
}

pub fn list_targets(toolchain: &Toolchain) -> Result<()> {
    for component in try!(toolchain.list_components()) {
        if component.component.pkg == "rust-std" {
            if component.required {
                println!("{} (default)", component.component.target);
            } else if component.installed {
                println!("{} (installed)", component.component.target);
            } else {
                println!("{}", component.component.target);
            }
        }
    }

    Ok(())
}

pub fn list_toolchains(cfg: &Cfg) -> Result<()> {
    let mut toolchains = try!(cfg.list_toolchains());

    toolchains.sort();

    if toolchains.is_empty() {
        println!("no installed toolchains");
    } else {
        for toolchain in toolchains {
            println!("{}", &toolchain);
        }
    }
    Ok(())
}

pub fn list_overrides(cfg: &Cfg) -> Result<()> {
    let mut overrides = try!(cfg.override_db.list());

    overrides.sort();

    if overrides.is_empty() {
        println!("no overrides");
    } else {
        for o in overrides {
            println!("{}", o);
        }
    }
    Ok(())
}

<|endoftext|>


<commit_before>#![cfg_attr(thiserror_nightly_testing, feature(backtrace))]

#[cfg(thiserror_nightly_testing)]
pub mod structs {
    use std::backtrace::Backtrace;
    use std::error::Error;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct PlainBacktrace {
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ExplicitBacktrace {
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktrace {
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ArcBacktrace {
        #[backtrace]
        backtrace: Arc<Backtrace>,
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = ExplicitBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = OptBacktrace {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = ArcBacktrace {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());
    }
}

#[cfg(thiserror_nightly_testing)]
pub mod enums {
    use std::backtrace::Backtrace;
    use std::error::Error;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    pub enum PlainBacktrace {
        #[error("...")]
        Test { backtrace: Backtrace },
    }

    #[derive(Error, Debug)]
    pub enum ExplicitBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum ArcBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Arc<Backtrace>,
        },
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = ExplicitBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = OptBacktrace::Test {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = ArcBacktrace::Test {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());
    }
}

#[test]
#[cfg_attr(not(thiserror_nightly_testing), ignore)]
fn test_backtrace() {}
<commit_msg>Add test of capturing Backtrace/Option<Backtrace> in a From impl<commit_after>#![cfg_attr(thiserror_nightly_testing, feature(backtrace))]

use thiserror::Error;

#[derive(Error, Debug)]
#[error("...")]
pub struct Inner;

#[cfg(thiserror_nightly_testing)]
pub mod structs {
    use super::Inner;
    use std::backtrace::Backtrace;
    use std::error::Error;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct PlainBacktrace {
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ExplicitBacktrace {
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktrace {
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ArcBacktrace {
        #[backtrace]
        backtrace: Arc<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct BacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = ExplicitBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = OptBacktrace {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = ArcBacktrace {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = BacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = OptBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());
    }
}

#[cfg(thiserror_nightly_testing)]
pub mod enums {
    use super::Inner;
    use std::backtrace::Backtrace;
    use std::error::Error;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    pub enum PlainBacktrace {
        #[error("...")]
        Test { backtrace: Backtrace },
    }

    #[derive(Error, Debug)]
    pub enum ExplicitBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum ArcBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Arc<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum BacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = ExplicitBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(error.backtrace().is_some());

        let error = OptBacktrace::Test {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = ArcBacktrace::Test {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(error.backtrace().is_some());

        let error = BacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());

        let error = OptBacktraceFrom::from(Inner);
        assert!(error.backtrace().is_some());
    }
}

#[test]
#[cfg_attr(not(thiserror_nightly_testing), ignore)]
fn test_backtrace() {}
<|endoftext|>


<commit_before>use super::*;
use redox::*;

impl Editor {
    /// Redraw the window
    pub fn redraw(&mut self) {
        // TODO: Only draw when relevant for the window
        let x = self.x();
        let y = self.y();
        // Redraw window
        self.window.set([0, 0, 0, 255]);

        self.window.rect(8 * (x - self.scroll_y) as isize,
                         16 * (y - self.scroll_x) as isize,
                         8,
                         16,
                         [255, 255, 255, 255]);

        for (y, row) in self.text.iter().enumerate() {
            for (x, c) in row.iter().enumerate() {
                if self.x() == x && self.y() == y {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     *c,
                                     [0, 0, 0, 255]);
                } else {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     *c,
                                     [255, 255, 255, 255]);
                }
            }
        }
        let h = self.window.height();
        let w = self.window.width();
        self.window.rect(0, h as isize - 18, w, 18, [74, 74, 74, 255]);

        for (n, c) in (if self.status_bar.mode.len() > w / (8 * 4) {
            self.status_bar.mode.chars().take(w / (8 * 4) - 5).chain(vec!['.', '.', '.']).collect::<Vec<_>>()
        } else {
            self.status_bar.mode.chars().collect()
        }).into_iter().enumerate() {
            self.window.char(n as isize * 8, h as isize - 16 - 1, c, [255, 255, 255, 255]);
        }

        self.window.sync();
    }
}

/// The statubar (showing various info about the current state of the editor)
pub struct StatusBar {
    /// The current mode
    pub mode: String,
    /// The cureent char
    pub file: String,
    /// The current command
    pub cmd: String,
    /// A message (such as an error or other info to the user)
    pub msg: String,
}

impl StatusBar {
    /// Create new status bar
    pub fn new() -> Self {
        StatusBar {
            mode: "Normal".to_string(),
            file: String::new(),
            cmd: String::new(),
            msg: String::new(),
        }
    }
}
<commit_msg>Fix tab insertion<commit_after>use super::*;
use redox::*;

impl Editor {
    /// Redraw the window
    pub fn redraw(&mut self) {
        // TODO: Only draw when relevant for the window
        let x = self.x();
        let y = self.y();
        // Redraw window
        self.window.set([0, 0, 0, 255]);

        self.window.rect(8 * (x - self.scroll_y) as isize,
                         16 * (y - self.scroll_x) as isize,
                         8,
                         16,
                         [255, 255, 255, 255]);

        for (y, row) in self.text.iter().enumerate() {
            for (x, c) in row.iter().enumerate() {
                if self.x() == x && self.y() == y {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     *c,
                                     [0, 0, 0, 255]);
                } else {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     *c,
                                     [255, 255, 255, 255]);
                }
            }
        }
        let h = self.window.height();
        let w = self.window.width();
        self.window.rect(0, h as isize - 18, w, 18, [74, 74, 74, 255]);

        for (n, c) in (if self.status_bar.mode.len() > w / (8 * 4) {
            self.status_bar.mode.chars().take(w / (8 * 4) - 5).chain(vec!['.', '.', '.']).collect::<Vec<_>>()
        } else {
            self.status_bar.mode.chars().collect()
        }).into_iter().enumerate() {
            self.window.char(n as isize * 8, h as isize - 16 - 1, if c == '\t' { ' ' } else { c }, [255, 255, 255, 255]);
        }

        self.window.sync();
    }
}

/// The statubar (showing various info about the current state of the editor)
pub struct StatusBar {
    /// The current mode
    pub mode: String,
    /// The cureent char
    pub file: String,
    /// The current command
    pub cmd: String,
    /// A message (such as an error or other info to the user)
    pub msg: String,
}

impl StatusBar {
    /// Create new status bar
    pub fn new() -> Self {
        StatusBar {
            mode: "Normal".to_string(),
            file: String::new(),
            cmd: String::new(),
            msg: String::new(),
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::inheritance::Castable;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlheadelement::HTMLHeadElement;
use crate::dom::node::Node;
use js::jsval::UndefinedValue;
use servo_config::opts;
use std::fs::{read_dir, File};
use std::io::Read;
use std::path::PathBuf;

pub fn load_script(head: &HTMLHeadElement) {
    if let Some(ref path_str) = opts::get().userscripts {
        let node = head.upcast::<Node>();
        let doc = node.owner_doc();
        let win = doc.window();
        let cx = win.get_cx();
        rooted!(in(cx) let mut rval = UndefinedValue());

        let path = PathBuf::from(path_str);
        let mut files = read_dir(&path)
            .expect("Bad path passed to --userscripts")
            .filter_map(|e| e.ok())
            .map(|e| e.path())
            .collect::<Vec<_>>();

        files.sort();

        for file in files {
            let mut f = File::open(&file).unwrap();
            let mut contents = vec![];
            f.read_to_end(&mut contents).unwrap();
            let script_text = String::from_utf8_lossy(&contents);
            win.upcast::<GlobalScope>()
                .evaluate_js_on_global_with_result(&script_text, rval.handle_mut());
        }
    }
}
<commit_msg>Auto merge of #22697 - jdm:user-script, r=asajeffrey<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlheadelement::HTMLHeadElement;
use crate::dom::node::document_from_node;
use js::jsval::UndefinedValue;
use servo_config::opts;
use std::fs::{read_dir, File};
use std::io::Read;
use std::path::PathBuf;

pub fn load_script(head: &HTMLHeadElement) {
    let path_str = match opts::get().userscripts.clone() {
        Some(p) => p,
        None => return,
    };
    let doc = document_from_node(head);
    let win = Trusted::new(doc.window());
    doc.add_delayed_task(task!(UserScriptExecute: move || {
        let win = win.root();
        let cx = win.get_cx();
        rooted!(in(cx) let mut rval = UndefinedValue());

        let path = PathBuf::from(&path_str);
        let mut files = read_dir(&path)
            .expect("Bad path passed to --userscripts")
            .filter_map(|e| e.ok())
            .map(|e| e.path())
            .collect::<Vec<_>>();

        files.sort();

        for file in files {
            let mut f = File::open(&file).unwrap();
            let mut contents = vec![];
            f.read_to_end(&mut contents).unwrap();
            let script_text = String::from_utf8_lossy(&contents);
            win.upcast::<GlobalScope>()
                .evaluate_script_on_global_with_result(
                    &script_text,
                    &file.to_string_lossy(),
                    rval.handle_mut(),
                    1,
                );
        }
    }));
}
<|endoftext|>


<commit_before>use std::any::Any;
use std::fmt;
use std::io::Read;
use std::io::Write;
use std::any::TypeId;

#[cfg(feature = "bytes")]
use bytes::Bytes;

use crate::clear::Clear;
use crate::error::ProtobufError;
use crate::error::ProtobufResult;
use crate::reflect::MessageDescriptor;
use crate::reflect::ProtobufValue;
use crate::stream::CodedInputStream;
use crate::stream::CodedOutputStream;
use crate::stream::WithCodedInputStream;
use crate::stream::WithCodedOutputStream;
use crate::unknown::UnknownFields;

/// Trait implemented for all generated structs for protobuf messages.
///
/// Also, generated messages implement `Clone + Default + PartialEq`
pub trait Message: fmt::Debug + Clear + Send + Sync + ProtobufValue {
    /// Message descriptor for this message, used for reflection.
    fn descriptor(&self) -> &'static MessageDescriptor;

    /// True iff all required fields are initialized.
    /// Always returns `true` for protobuf 3.
    fn is_initialized(&self) -> bool;

    /// Update this message object with fields read from given stream.
    fn merge_from(&mut self, is: &mut CodedInputStream) -> ProtobufResult<()>;

    /// Write message to the stream.
    ///
    /// Sizes of this messages and nested messages must be cached
    /// by calling `compute_size` prior to this call.
    fn write_to_with_cached_sizes(&self, os: &mut CodedOutputStream) -> ProtobufResult<()>;

    /// Compute and cache size of this message and all nested messages
    fn compute_size(&self) -> u32;

    /// Get size previously computed by `compute_size`.
    fn get_cached_size(&self) -> u32;

    /// Write the message to the stream.
    ///
    /// Results in error if message is not fully initialized.
    fn write_to(&self, os: &mut CodedOutputStream) -> ProtobufResult<()> {
        self.check_initialized()?;

        // cache sizes
        self.compute_size();
        // TODO: reserve additional
        self.write_to_with_cached_sizes(os)?;

        // TODO: assert we've written same number of bytes as computed

        Ok(())
    }

    /// Write the message to the stream prepending the message with message length
    /// encoded as varint.
    fn write_length_delimited_to(&self, os: &mut CodedOutputStream) -> ProtobufResult<()> {
        let size = self.compute_size();
        os.write_raw_varint32(size)?;
        self.write_to_with_cached_sizes(os)?;

        // TODO: assert we've written same number of bytes as computed

        Ok(())
    }

    /// Write the message to the vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_vec(&self, vec: &mut Vec<u8>) -> ProtobufResult<()> {
        let mut os = CodedOutputStream::vec(vec);
        self.write_length_delimited_to(&mut os)?;
        os.flush()?;
        Ok(())
    }

    /// Update this message object with fields read from given stream.
    fn merge_from_bytes(&mut self, bytes: &[u8]) -> ProtobufResult<()> {
        let mut is = CodedInputStream::from_bytes(bytes);
        self.merge_from(&mut is)
    }

    /// Check if all required fields of this object are initialized.
    fn check_initialized(&self) -> ProtobufResult<()> {
        if !self.is_initialized() {
            Err(ProtobufError::message_not_initialized(
                self.descriptor().name(),
            ))
        } else {
            Ok(())
        }
    }

    /// Write the message to the writer.
    fn write_to_writer(&self, w: &mut Write) -> ProtobufResult<()> {
        w.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    fn write_to_vec(&self, v: &mut Vec<u8>) -> ProtobufResult<()> {
        v.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    ///
    /// > **Note**: You can use `parse_from_bytes` to do the reverse.
    fn write_to_bytes(&self) -> ProtobufResult<Vec<u8>> {
        self.check_initialized()?;

        let size = self.compute_size() as usize;
        let mut v = Vec::with_capacity(size);
        // skip zerofill
        unsafe {
            v.set_len(size);
        }
        {
            let mut os = CodedOutputStream::bytes(&mut v);
            self.write_to_with_cached_sizes(&mut os)?;
            os.check_eof();
        }
        Ok(v)
    }

    /// Write the message to the writer, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_writer(&self, w: &mut Write) -> ProtobufResult<()> {
        w.with_coded_output_stream(|os| self.write_length_delimited_to(os))
    }

    /// Write the message to the bytes vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_bytes(&self) -> ProtobufResult<Vec<u8>> {
        let mut v = Vec::new();
        v.with_coded_output_stream(|os| self.write_length_delimited_to(os))?;
        Ok(v)
    }

    /// Get a reference to unknown fields.
    fn get_unknown_fields(&self) -> &UnknownFields;
    /// Get a mutable reference to unknown fields.
    fn mut_unknown_fields(&mut self) -> &mut UnknownFields;

    /// Create an empty message object.
    fn new() -> Self
    where
        Self: Sized;

    /// Get message descriptor for message type.
    fn descriptor_static() -> &'static MessageDescriptor
    where
        Self: Sized,
    {
        panic!(
            "descriptor_static is not implemented for message, \
             LITE_RUNTIME must be used"
        );
    }

    /// Return a pointer to default immutable message.
    fn default_instance() -> &'static Self
    where
        Self: Sized;
}

impl dyn Message {
    pub fn downcast_box<T: Any>(self: Box<Self>) -> Result<Box<T>, Box<Message>> {
        if Any::type_id(&*self) == TypeId::of::<T>() {
            unsafe {
                let raw: *mut Message = Box::into_raw(self);
                Ok(Box::from_raw(raw as *mut T))
            }
        } else {
            Err(self)
        }
    }

    pub fn downcast_ref<'a, M: Message + 'a>(&'a self) -> Option<&'a M> {
        if Any::type_id(&*self) == TypeId::of::<M>() {
            unsafe {
                Some(&*(self as *const dyn Message as *const M))
            }
        } else {
            None
        }
    }

    pub fn downcast_mut<'a, M: Message + 'a>(&'a mut self) -> Option<&'a mut M> {
        if Any::type_id(&*self) == TypeId::of::<M>() {
            unsafe {
                Some(&mut *(self as *mut dyn Message as *mut M))
            }
        } else {
            None
        }
    }
}

impl Clone for Box<Message> {
    fn clone(&self) -> Self {
        use std::ops::Deref;
        self.descriptor().clone(self.deref())
    }
}

#[cfg(off)] // don't need it
impl PartialEq for Box<Message> {
    fn eq(&self, other: &Box<Message>) -> bool {
        use std::ops::Deref;
        self.descriptor() == other.descriptor() && self.descriptor().eq(self.deref(), other.deref())
    }
}

/// Parse message from stream.
pub fn parse_from<M: Message>(is: &mut CodedInputStream) -> ProtobufResult<M> {
    let mut r: M = Message::new();
    r.merge_from(is)?;
    r.check_initialized()?;
    Ok(r)
}

/// Parse message from reader.
/// Parse stops on EOF or when error encountered.
pub fn parse_from_reader<M: Message>(reader: &mut Read) -> ProtobufResult<M> {
    reader.with_coded_input_stream(|is| parse_from::<M>(is))
}

/// Parse message from byte array.
pub fn parse_from_bytes<M: Message>(bytes: &[u8]) -> ProtobufResult<M> {
    bytes.with_coded_input_stream(|is| parse_from::<M>(is))
}

/// Parse message from `Bytes` object.
/// Resulting message may share references to the passed bytes object.
#[cfg(feature = "bytes")]
pub fn parse_from_carllerche_bytes<M: Message>(bytes: &Bytes) -> ProtobufResult<M> {
    // Call trait explicitly to avoid accidental construction from `&[u8]`
    WithCodedInputStream::with_coded_input_stream(bytes, |is| parse_from::<M>(is))
}
<commit_msg>We don't need that TODO<commit_after>use std::any::Any;
use std::fmt;
use std::io::Read;
use std::io::Write;
use std::any::TypeId;

#[cfg(feature = "bytes")]
use bytes::Bytes;

use crate::clear::Clear;
use crate::error::ProtobufError;
use crate::error::ProtobufResult;
use crate::reflect::MessageDescriptor;
use crate::reflect::ProtobufValue;
use crate::stream::CodedInputStream;
use crate::stream::CodedOutputStream;
use crate::stream::WithCodedInputStream;
use crate::stream::WithCodedOutputStream;
use crate::unknown::UnknownFields;

/// Trait implemented for all generated structs for protobuf messages.
///
/// Also, generated messages implement `Clone + Default + PartialEq`
pub trait Message: fmt::Debug + Clear + Send + Sync + ProtobufValue {
    /// Message descriptor for this message, used for reflection.
    fn descriptor(&self) -> &'static MessageDescriptor;

    /// True iff all required fields are initialized.
    /// Always returns `true` for protobuf 3.
    fn is_initialized(&self) -> bool;

    /// Update this message object with fields read from given stream.
    fn merge_from(&mut self, is: &mut CodedInputStream) -> ProtobufResult<()>;

    /// Write message to the stream.
    ///
    /// Sizes of this messages and nested messages must be cached
    /// by calling `compute_size` prior to this call.
    fn write_to_with_cached_sizes(&self, os: &mut CodedOutputStream) -> ProtobufResult<()>;

    /// Compute and cache size of this message and all nested messages
    fn compute_size(&self) -> u32;

    /// Get size previously computed by `compute_size`.
    fn get_cached_size(&self) -> u32;

    /// Write the message to the stream.
    ///
    /// Results in error if message is not fully initialized.
    fn write_to(&self, os: &mut CodedOutputStream) -> ProtobufResult<()> {
        self.check_initialized()?;

        // cache sizes
        self.compute_size();
        // TODO: reserve additional
        self.write_to_with_cached_sizes(os)?;

        Ok(())
    }

    /// Write the message to the stream prepending the message with message length
    /// encoded as varint.
    fn write_length_delimited_to(&self, os: &mut CodedOutputStream) -> ProtobufResult<()> {
        let size = self.compute_size();
        os.write_raw_varint32(size)?;
        self.write_to_with_cached_sizes(os)?;

        // TODO: assert we've written same number of bytes as computed

        Ok(())
    }

    /// Write the message to the vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_vec(&self, vec: &mut Vec<u8>) -> ProtobufResult<()> {
        let mut os = CodedOutputStream::vec(vec);
        self.write_length_delimited_to(&mut os)?;
        os.flush()?;
        Ok(())
    }

    /// Update this message object with fields read from given stream.
    fn merge_from_bytes(&mut self, bytes: &[u8]) -> ProtobufResult<()> {
        let mut is = CodedInputStream::from_bytes(bytes);
        self.merge_from(&mut is)
    }

    /// Check if all required fields of this object are initialized.
    fn check_initialized(&self) -> ProtobufResult<()> {
        if !self.is_initialized() {
            Err(ProtobufError::message_not_initialized(
                self.descriptor().name(),
            ))
        } else {
            Ok(())
        }
    }

    /// Write the message to the writer.
    fn write_to_writer(&self, w: &mut Write) -> ProtobufResult<()> {
        w.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    fn write_to_vec(&self, v: &mut Vec<u8>) -> ProtobufResult<()> {
        v.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    ///
    /// > **Note**: You can use `parse_from_bytes` to do the reverse.
    fn write_to_bytes(&self) -> ProtobufResult<Vec<u8>> {
        self.check_initialized()?;

        let size = self.compute_size() as usize;
        let mut v = Vec::with_capacity(size);
        // skip zerofill
        unsafe {
            v.set_len(size);
        }
        {
            let mut os = CodedOutputStream::bytes(&mut v);
            self.write_to_with_cached_sizes(&mut os)?;
            os.check_eof();
        }
        Ok(v)
    }

    /// Write the message to the writer, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_writer(&self, w: &mut Write) -> ProtobufResult<()> {
        w.with_coded_output_stream(|os| self.write_length_delimited_to(os))
    }

    /// Write the message to the bytes vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_bytes(&self) -> ProtobufResult<Vec<u8>> {
        let mut v = Vec::new();
        v.with_coded_output_stream(|os| self.write_length_delimited_to(os))?;
        Ok(v)
    }

    /// Get a reference to unknown fields.
    fn get_unknown_fields(&self) -> &UnknownFields;
    /// Get a mutable reference to unknown fields.
    fn mut_unknown_fields(&mut self) -> &mut UnknownFields;

    /// Create an empty message object.
    fn new() -> Self
    where
        Self: Sized;

    /// Get message descriptor for message type.
    fn descriptor_static() -> &'static MessageDescriptor
    where
        Self: Sized,
    {
        panic!(
            "descriptor_static is not implemented for message, \
             LITE_RUNTIME must be used"
        );
    }

    /// Return a pointer to default immutable message.
    fn default_instance() -> &'static Self
    where
        Self: Sized;
}

impl dyn Message {
    pub fn downcast_box<T: Any>(self: Box<Self>) -> Result<Box<T>, Box<Message>> {
        if Any::type_id(&*self) == TypeId::of::<T>() {
            unsafe {
                let raw: *mut Message = Box::into_raw(self);
                Ok(Box::from_raw(raw as *mut T))
            }
        } else {
            Err(self)
        }
    }

    pub fn downcast_ref<'a, M: Message + 'a>(&'a self) -> Option<&'a M> {
        if Any::type_id(&*self) == TypeId::of::<M>() {
            unsafe {
                Some(&*(self as *const dyn Message as *const M))
            }
        } else {
            None
        }
    }

    pub fn downcast_mut<'a, M: Message + 'a>(&'a mut self) -> Option<&'a mut M> {
        if Any::type_id(&*self) == TypeId::of::<M>() {
            unsafe {
                Some(&mut *(self as *mut dyn Message as *mut M))
            }
        } else {
            None
        }
    }
}

impl Clone for Box<Message> {
    fn clone(&self) -> Self {
        use std::ops::Deref;
        self.descriptor().clone(self.deref())
    }
}

#[cfg(off)] // don't need it
impl PartialEq for Box<Message> {
    fn eq(&self, other: &Box<Message>) -> bool {
        use std::ops::Deref;
        self.descriptor() == other.descriptor() && self.descriptor().eq(self.deref(), other.deref())
    }
}

/// Parse message from stream.
pub fn parse_from<M: Message>(is: &mut CodedInputStream) -> ProtobufResult<M> {
    let mut r: M = Message::new();
    r.merge_from(is)?;
    r.check_initialized()?;
    Ok(r)
}

/// Parse message from reader.
/// Parse stops on EOF or when error encountered.
pub fn parse_from_reader<M: Message>(reader: &mut Read) -> ProtobufResult<M> {
    reader.with_coded_input_stream(|is| parse_from::<M>(is))
}

/// Parse message from byte array.
pub fn parse_from_bytes<M: Message>(bytes: &[u8]) -> ProtobufResult<M> {
    bytes.with_coded_input_stream(|is| parse_from::<M>(is))
}

/// Parse message from `Bytes` object.
/// Resulting message may share references to the passed bytes object.
#[cfg(feature = "bytes")]
pub fn parse_from_carllerche_bytes<M: Message>(bytes: &Bytes) -> ProtobufResult<M> {
    // Call trait explicitly to avoid accidental construction from `&[u8]`
    WithCodedInputStream::with_coded_input_stream(bytes, |is| parse_from::<M>(is))
}
<|endoftext|>


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

use crate::bba::idstring::*;

// A tile type from the interchange format perspective
// this is not the same as a database tile type; because the Oxide graph can have
// more than one tile at a grid location whereas the interchange grid is one tile
// per location
struct IcTileType {
	type_idx: u32,
	tile_types: Vec<String>,
	wires: Vec<IcWire>,
	pips: Vec<IcPip>,
	wires_by_name: HashMap<IdString, usize>,
	// TODO: constants, sites, etc
}

struct IcWire {
	name: IdString,
	pips_uphill: Vec<u32>,
	pips_downhill: Vec<u32>,
}

struct IcPip {
	name: IdString,
	from_wire: u32,
	to_wire: u32,
}

// A tile instance
struct IcTileInst {
	type_idx: u32,
	// mapping between wires and nodes
	wire_to_node: Vec<u32>,
}

// A reference to a tile wire
struct IcWireRef {
	tile_idx: u32,
	wire_idx: u32,
}

// A node instance
struct IcNode {
	// list of tile wires in the node
	wires: Vec<IcWireRef>,
}

// The overall routing resource graph
struct IcGraph {
	tile_types: Vec<IcTileType>,
	tiles: Vec<IcTileInst>,
	nodes: Vec<IcNode>,
}<commit_msg>interchange: Grid tile type handling<commit_after>use crate::chip::Chip;
use crate::database::Database;
use std::collections::HashMap;

use crate::bba::idstring::*;

// A tile type from the interchange format perspective
// this is not the same as a database tile type; because the Oxide graph can have
// more than one tile at a grid location whereas the interchange grid is one tile
// per location

#[derive(Clone, Eq, PartialEq, Hash)]
struct TileTypeKey {
    pub tile_types: Vec<String>,
}
impl TileTypeKey {
    pub fn new() -> TileTypeKey {
        TileTypeKey { tile_types: Vec::new() }
    }
}

struct IcTileType {
    key: TileTypeKey,
    type_idx: u32,
    wires: Vec<IcWire>,
    pips: Vec<IcPip>,
    wires_by_name: HashMap<IdString, usize>,
    // TODO: constants, sites, etc
}

impl IcTileType {
    pub fn new(key: TileTypeKey, idx: u32) -> IcTileType {
        IcTileType {
            key: key,
            type_idx: idx,
            wires: Vec::new(),
            pips: Vec::new(),
            wires_by_name: HashMap::new(),
        }
    }
}

struct IcWire {
    name: IdString,
    pips_uphill: Vec<u32>,
    pips_downhill: Vec<u32>,
}

struct IcPip {
    name: IdString,
    from_wire: u32,
    to_wire: u32,
}

// A tile instance
struct IcTileInst {
    type_idx: u32,
    // mapping between wires and nodes
    wire_to_node: Vec<u32>,
}

impl IcTileInst {
    pub fn new() -> IcTileInst {
        IcTileInst {
            type_idx: 0,
            wire_to_node: Vec::new(),
        }
    }
}

// A reference to a tile wire
struct IcWireRef {
    tile_idx: u32,
    wire_idx: u32,
}

// A node instance
struct IcNode {
    // list of tile wires in the node
    wires: Vec<IcWireRef>,
}

impl IcNode {
    pub fn new() -> IcNode {
        IcNode {
            wires: Vec::new(),
        }
    }
}

// The overall routing resource graph
struct IcGraph {
    tile_types: Vec<IcTileType>,
    tiles: Vec<IcTileInst>,
    nodes: Vec<IcNode>,
    width: u32,
    height: u32,
}

impl IcGraph {
    pub fn new(width: u32, height: u32) -> IcGraph {
        IcGraph {
            tile_types: Vec::new(),
            tiles: (0..width*height).map(|_| IcTileInst::new()).collect(),
            nodes: Vec::new(),
            width: width,
            height: height
        }
    }
}

struct GraphBuilder<'a> {
    g: IcGraph,
    ids: &'a mut IdStringDB,
    chip: &'a Chip,
    db: &'a mut Database,
    tiletypes_by_xy: HashMap<(u32, u32), TileTypeKey>,
}

impl <'a> GraphBuilder<'a> {
    pub fn new(ids: &'a mut IdStringDB, chip: &'a Chip, db: &'a mut Database) -> GraphBuilder<'a> {
        let mut width = 0;
        let mut height = 0;
        let mut tiletypes_by_xy = HashMap::new();
        for t in chip.tiles.iter() {
            tiletypes_by_xy.entry((t.x, t.y)).or_insert(TileTypeKey::new()).tile_types.push(t.tiletype.to_string());
            width = std::cmp::max(width, t.x + 1);
            height = std::cmp::max(height, t.y + 1);
        }

        GraphBuilder {
            g: IcGraph::new(width, height),
            ids: ids,
            chip: chip,
            db: db,
            tiletypes_by_xy: tiletypes_by_xy,
        }
    }

}
<|endoftext|>


<commit_before>use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_errors::{pluralize, Applicability};
use rustc_hir as hir;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_parse_format::{ParseMode, Parser, Piece};
use rustc_session::lint::FutureIncompatibilityReason;
use rustc_span::edition::Edition;
use rustc_span::{hygiene, sym, symbol::kw, symbol::SymbolStr, InnerSpan, Span, Symbol};

declare_lint! {
    /// The `non_fmt_panics` lint detects `panic!(..)` invocations where the first
    /// argument is not a formatting string.
    ///
    /// ### Example
    ///
    /// ```rust,no_run
    /// panic!("{}");
    /// panic!(123);
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// In Rust 2018 and earlier, `panic!(x)` directly uses `x` as the message.
    /// That means that `panic!("{}")` panics with the message `"{}"` instead
    /// of using it as a formatting string, and `panic!(123)` will panic with
    /// an `i32` as message.
    ///
    /// Rust 2021 always interprets the first argument as format string.
    NON_FMT_PANICS,
    Warn,
    "detect single-argument panic!() invocations in which the argument is not a format string",
    @future_incompatible = FutureIncompatibleInfo {
        reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
        explain_reason: false,
    };
    report_in_external_macro
}

declare_lint_pass!(NonPanicFmt => [NON_FMT_PANICS]);

impl<'tcx> LateLintPass<'tcx> for NonPanicFmt {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
        if let hir::ExprKind::Call(f, [arg]) = &expr.kind {
            if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
                if Some(def_id) == cx.tcx.lang_items().begin_panic_fn()
                    || Some(def_id) == cx.tcx.lang_items().panic_fn()
                    || Some(def_id) == cx.tcx.lang_items().panic_str()
                {
                    if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
                        if cx.tcx.is_diagnostic_item(sym::std_panic_2015_macro, id)
                            || cx.tcx.is_diagnostic_item(sym::core_panic_2015_macro, id)
                        {
                            check_panic(cx, f, arg);
                        }
                    }
                }
            }
        }
    }
}

fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) {
    if let hir::ExprKind::Lit(lit) = &arg.kind {
        if let ast::LitKind::Str(sym, _) = lit.node {
            // The argument is a string literal.
            check_panic_str(cx, f, arg, &sym.as_str());
            return;
        }
    }

    // The argument is *not* a string literal.

    let (span, panic, symbol_str) = panic_call(cx, f);

    if in_external_macro(cx.sess(), span) {
        // Nothing that can be done about it in the current crate.
        return;
    }

    // Find the span of the argument to `panic!()`, before expansion in the
    // case of `panic!(some_macro!())`.
    // We don't use source_callsite(), because this `panic!(..)` might itself
    // be expanded from another macro, in which case we want to stop at that
    // expansion.
    let mut arg_span = arg.span;
    let mut arg_macro = None;
    while !span.contains(arg_span) {
        let expn = arg_span.ctxt().outer_expn_data();
        if expn.is_root() {
            break;
        }
        arg_macro = expn.macro_def_id;
        arg_span = expn.call_site;
    }

    cx.struct_span_lint(NON_FMT_PANICS, arg_span, |lint| {
        let mut l = lint.build("panic message is not a string literal");
        l.note("this usage of panic!() is deprecated; it will be a hard error in Rust 2021");
        l.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>");
        if !span.contains(arg_span) {
            // No clue where this argument is coming from.
            l.emit();
            return;
        }
        if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
            // A case of `panic!(format!(..))`.
            l.note(format!("the {}!() macro supports formatting, so there's no need for the format!() macro here", symbol_str).as_str());
            if let Some((open, close, _)) = find_delimiters(cx, arg_span) {
                l.multipart_suggestion(
                    "remove the `format!(..)` macro call",
                    vec![
                        (arg_span.until(open.shrink_to_hi()), "".into()),
                        (close.until(arg_span.shrink_to_hi()), "".into()),
                    ],
                    Applicability::MachineApplicable,
                );
            }
        } else {
            let ty = cx.typeck_results().expr_ty(arg);
            // If this is a &str or String, we can confidently give the `"{}", ` suggestion.
            let is_str = matches!(
                ty.kind(),
                ty::Ref(_, r, _) if *r.kind() == ty::Str,
            ) || matches!(
                ty.ty_adt_def(),
                Some(ty_def) if cx.tcx.is_diagnostic_item(sym::string_type, ty_def.did),
            );
            l.span_suggestion_verbose(
                arg_span.shrink_to_lo(),
                "add a \"{}\" format string to Display the message",
                "\"{}\", ".into(),
                if is_str {
                    Applicability::MachineApplicable
                } else {
                    Applicability::MaybeIncorrect
                },
            );
            if !is_str && panic == sym::std_panic_macro {
                if let Some((open, close, del)) = find_delimiters(cx, span) {
                    l.multipart_suggestion(
                        "or use std::panic::panic_any instead",
                        if del == '(' {
                            vec![(span.until(open), "std::panic::panic_any".into())]
                        } else {
                            vec![
                                (span.until(open.shrink_to_hi()), "std::panic::panic_any(".into()),
                                (close, ")".into()),
                            ]
                        },
                        Applicability::MachineApplicable,
                    );
                }
            }
        }
        l.emit();
    });
}

fn check_panic_str<'tcx>(
    cx: &LateContext<'tcx>,
    f: &'tcx hir::Expr<'tcx>,
    arg: &'tcx hir::Expr<'tcx>,
    fmt: &str,
) {
    if !fmt.contains(&['{', '}'][..]) {
        // No brace, no problem.
        return;
    }

    let (span, _, _) = panic_call(cx, f);

    if in_external_macro(cx.sess(), span) && in_external_macro(cx.sess(), arg.span) {
        // Nothing that can be done about it in the current crate.
        return;
    }

    let fmt_span = arg.span.source_callsite();

    let (snippet, style) = match cx.sess().parse_sess.source_map().span_to_snippet(fmt_span) {
        Ok(snippet) => {
            // Count the number of `#`s between the `r` and `"`.
            let style = snippet.strip_prefix('r').and_then(|s| s.find('"'));
            (Some(snippet), style)
        }
        Err(_) => (None, None),
    };

    let mut fmt_parser =
        Parser::new(fmt.as_ref(), style, snippet.clone(), false, ParseMode::Format);
    let n_arguments = (&mut fmt_parser).filter(|a| matches!(a, Piece::NextArgument(_))).count();

    if n_arguments > 0 && fmt_parser.errors.is_empty() {
        let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] {
            [] => vec![fmt_span],
            v => v.iter().map(|span| fmt_span.from_inner(*span)).collect(),
        };
        cx.struct_span_lint(NON_FMT_PANICS, arg_spans, |lint| {
            let mut l = lint.build(match n_arguments {
                1 => "panic message contains an unused formatting placeholder",
                _ => "panic message contains unused formatting placeholders",
            });
            l.note("this message is not used as a format string when given without arguments, but will be in Rust 2021");
            if span.contains(arg.span) {
                l.span_suggestion(
                    arg.span.shrink_to_hi(),
                    &format!("add the missing argument{}", pluralize!(n_arguments)),
                    ", ...".into(),
                    Applicability::HasPlaceholders,
                );
                l.span_suggestion(
                    arg.span.shrink_to_lo(),
                    "or add a \"{}\" format string to use the message literally",
                    "\"{}\", ".into(),
                    Applicability::MachineApplicable,
                );
            }
            l.emit();
        });
    } else {
        let brace_spans: Option<Vec<_>> =
            snippet.filter(|s| s.starts_with('"') || s.starts_with("r#")).map(|s| {
                s.char_indices()
                    .filter(|&(_, c)| c == '{' || c == '}')
                    .map(|(i, _)| fmt_span.from_inner(InnerSpan { start: i, end: i + 1 }))
                    .collect()
            });
        let msg = match &brace_spans {
            Some(v) if v.len() == 1 => "panic message contains a brace",
            _ => "panic message contains braces",
        };
        cx.struct_span_lint(NON_FMT_PANICS, brace_spans.unwrap_or_else(|| vec![span]), |lint| {
            let mut l = lint.build(msg);
            l.note("this message is not used as a format string, but will be in Rust 2021");
            if span.contains(arg.span) {
                l.span_suggestion(
                    arg.span.shrink_to_lo(),
                    "add a \"{}\" format string to use the message literally",
                    "\"{}\", ".into(),
                    Applicability::MachineApplicable,
                );
            }
            l.emit();
        });
    }
}

/// Given the span of `some_macro!(args);`, gives the span of `(` and `)`,
/// and the type of (opening) delimiter used.
fn find_delimiters<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<(Span, Span, char)> {
    let snippet = cx.sess().parse_sess.source_map().span_to_snippet(span).ok()?;
    let (open, open_ch) = snippet.char_indices().find(|&(_, c)| "([{".contains(c))?;
    let close = snippet.rfind(|c| ")]}".contains(c))?;
    Some((
        span.from_inner(InnerSpan { start: open, end: open + 1 }),
        span.from_inner(InnerSpan { start: close, end: close + 1 }),
        open_ch,
    ))
}

fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol, SymbolStr) {
    let mut expn = f.span.ctxt().outer_expn_data();

    let mut panic_macro = kw::Empty;

    // Unwrap more levels of macro expansion, as panic_2015!()
    // was likely expanded from panic!() and possibly from
    // [debug_]assert!().
    for &i in
        &[sym::std_panic_macro, sym::core_panic_macro, sym::assert_macro, sym::debug_assert_macro]
    {
        let parent = expn.call_site.ctxt().outer_expn_data();
        if parent.macro_def_id.map_or(false, |id| cx.tcx.is_diagnostic_item(i, id)) {
            expn = parent;
            panic_macro = i;
        }
    }

    let macro_symbol =
        if let hygiene::ExpnKind::Macro(_, symbol) = expn.kind { symbol } else { sym::panic };
    (expn.call_site, panic_macro, macro_symbol.as_str())
}
<commit_msg>Rollup merge of #87967 - m-ou-se:non-fmt-panic-detect-fake-spans, r=cjgillot<commit_after>use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_errors::{pluralize, Applicability};
use rustc_hir as hir;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_parse_format::{ParseMode, Parser, Piece};
use rustc_session::lint::FutureIncompatibilityReason;
use rustc_span::edition::Edition;
use rustc_span::{hygiene, sym, symbol::kw, symbol::SymbolStr, InnerSpan, Span, Symbol};

declare_lint! {
    /// The `non_fmt_panics` lint detects `panic!(..)` invocations where the first
    /// argument is not a formatting string.
    ///
    /// ### Example
    ///
    /// ```rust,no_run
    /// panic!("{}");
    /// panic!(123);
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// In Rust 2018 and earlier, `panic!(x)` directly uses `x` as the message.
    /// That means that `panic!("{}")` panics with the message `"{}"` instead
    /// of using it as a formatting string, and `panic!(123)` will panic with
    /// an `i32` as message.
    ///
    /// Rust 2021 always interprets the first argument as format string.
    NON_FMT_PANICS,
    Warn,
    "detect single-argument panic!() invocations in which the argument is not a format string",
    @future_incompatible = FutureIncompatibleInfo {
        reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
        explain_reason: false,
    };
    report_in_external_macro
}

declare_lint_pass!(NonPanicFmt => [NON_FMT_PANICS]);

impl<'tcx> LateLintPass<'tcx> for NonPanicFmt {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
        if let hir::ExprKind::Call(f, [arg]) = &expr.kind {
            if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
                if Some(def_id) == cx.tcx.lang_items().begin_panic_fn()
                    || Some(def_id) == cx.tcx.lang_items().panic_fn()
                    || Some(def_id) == cx.tcx.lang_items().panic_str()
                {
                    if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
                        if cx.tcx.is_diagnostic_item(sym::std_panic_2015_macro, id)
                            || cx.tcx.is_diagnostic_item(sym::core_panic_2015_macro, id)
                        {
                            check_panic(cx, f, arg);
                        }
                    }
                }
            }
        }
    }
}

fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) {
    if let hir::ExprKind::Lit(lit) = &arg.kind {
        if let ast::LitKind::Str(sym, _) = lit.node {
            // The argument is a string literal.
            check_panic_str(cx, f, arg, &sym.as_str());
            return;
        }
    }

    // The argument is *not* a string literal.

    let (span, panic, symbol_str) = panic_call(cx, f);

    if in_external_macro(cx.sess(), span) {
        // Nothing that can be done about it in the current crate.
        return;
    }

    // Find the span of the argument to `panic!()`, before expansion in the
    // case of `panic!(some_macro!())`.
    // We don't use source_callsite(), because this `panic!(..)` might itself
    // be expanded from another macro, in which case we want to stop at that
    // expansion.
    let mut arg_span = arg.span;
    let mut arg_macro = None;
    while !span.contains(arg_span) {
        let expn = arg_span.ctxt().outer_expn_data();
        if expn.is_root() {
            break;
        }
        arg_macro = expn.macro_def_id;
        arg_span = expn.call_site;
    }

    cx.struct_span_lint(NON_FMT_PANICS, arg_span, |lint| {
        let mut l = lint.build("panic message is not a string literal");
        l.note("this usage of panic!() is deprecated; it will be a hard error in Rust 2021");
        l.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>");
        if !is_arg_inside_call(arg_span, span) {
            // No clue where this argument is coming from.
            l.emit();
            return;
        }
        if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
            // A case of `panic!(format!(..))`.
            l.note(format!("the {}!() macro supports formatting, so there's no need for the format!() macro here", symbol_str).as_str());
            if let Some((open, close, _)) = find_delimiters(cx, arg_span) {
                l.multipart_suggestion(
                    "remove the `format!(..)` macro call",
                    vec![
                        (arg_span.until(open.shrink_to_hi()), "".into()),
                        (close.until(arg_span.shrink_to_hi()), "".into()),
                    ],
                    Applicability::MachineApplicable,
                );
            }
        } else {
            let ty = cx.typeck_results().expr_ty(arg);
            // If this is a &str or String, we can confidently give the `"{}", ` suggestion.
            let is_str = matches!(
                ty.kind(),
                ty::Ref(_, r, _) if *r.kind() == ty::Str,
            ) || matches!(
                ty.ty_adt_def(),
                Some(ty_def) if cx.tcx.is_diagnostic_item(sym::string_type, ty_def.did),
            );
            l.span_suggestion_verbose(
                arg_span.shrink_to_lo(),
                "add a \"{}\" format string to Display the message",
                "\"{}\", ".into(),
                if is_str {
                    Applicability::MachineApplicable
                } else {
                    Applicability::MaybeIncorrect
                },
            );
            if !is_str && panic == sym::std_panic_macro {
                if let Some((open, close, del)) = find_delimiters(cx, span) {
                    l.multipart_suggestion(
                        "or use std::panic::panic_any instead",
                        if del == '(' {
                            vec![(span.until(open), "std::panic::panic_any".into())]
                        } else {
                            vec![
                                (span.until(open.shrink_to_hi()), "std::panic::panic_any(".into()),
                                (close, ")".into()),
                            ]
                        },
                        Applicability::MachineApplicable,
                    );
                }
            }
        }
        l.emit();
    });
}

fn check_panic_str<'tcx>(
    cx: &LateContext<'tcx>,
    f: &'tcx hir::Expr<'tcx>,
    arg: &'tcx hir::Expr<'tcx>,
    fmt: &str,
) {
    if !fmt.contains(&['{', '}'][..]) {
        // No brace, no problem.
        return;
    }

    let (span, _, _) = panic_call(cx, f);

    if in_external_macro(cx.sess(), span) && in_external_macro(cx.sess(), arg.span) {
        // Nothing that can be done about it in the current crate.
        return;
    }

    let fmt_span = arg.span.source_callsite();

    let (snippet, style) = match cx.sess().parse_sess.source_map().span_to_snippet(fmt_span) {
        Ok(snippet) => {
            // Count the number of `#`s between the `r` and `"`.
            let style = snippet.strip_prefix('r').and_then(|s| s.find('"'));
            (Some(snippet), style)
        }
        Err(_) => (None, None),
    };

    let mut fmt_parser =
        Parser::new(fmt.as_ref(), style, snippet.clone(), false, ParseMode::Format);
    let n_arguments = (&mut fmt_parser).filter(|a| matches!(a, Piece::NextArgument(_))).count();

    if n_arguments > 0 && fmt_parser.errors.is_empty() {
        let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] {
            [] => vec![fmt_span],
            v => v.iter().map(|span| fmt_span.from_inner(*span)).collect(),
        };
        cx.struct_span_lint(NON_FMT_PANICS, arg_spans, |lint| {
            let mut l = lint.build(match n_arguments {
                1 => "panic message contains an unused formatting placeholder",
                _ => "panic message contains unused formatting placeholders",
            });
            l.note("this message is not used as a format string when given without arguments, but will be in Rust 2021");
            if is_arg_inside_call(arg.span, span) {
                l.span_suggestion(
                    arg.span.shrink_to_hi(),
                    &format!("add the missing argument{}", pluralize!(n_arguments)),
                    ", ...".into(),
                    Applicability::HasPlaceholders,
                );
                l.span_suggestion(
                    arg.span.shrink_to_lo(),
                    "or add a \"{}\" format string to use the message literally",
                    "\"{}\", ".into(),
                    Applicability::MachineApplicable,
                );
            }
            l.emit();
        });
    } else {
        let brace_spans: Option<Vec<_>> =
            snippet.filter(|s| s.starts_with('"') || s.starts_with("r#")).map(|s| {
                s.char_indices()
                    .filter(|&(_, c)| c == '{' || c == '}')
                    .map(|(i, _)| fmt_span.from_inner(InnerSpan { start: i, end: i + 1 }))
                    .collect()
            });
        let msg = match &brace_spans {
            Some(v) if v.len() == 1 => "panic message contains a brace",
            _ => "panic message contains braces",
        };
        cx.struct_span_lint(NON_FMT_PANICS, brace_spans.unwrap_or_else(|| vec![span]), |lint| {
            let mut l = lint.build(msg);
            l.note("this message is not used as a format string, but will be in Rust 2021");
            if is_arg_inside_call(arg.span, span) {
                l.span_suggestion(
                    arg.span.shrink_to_lo(),
                    "add a \"{}\" format string to use the message literally",
                    "\"{}\", ".into(),
                    Applicability::MachineApplicable,
                );
            }
            l.emit();
        });
    }
}

/// Given the span of `some_macro!(args);`, gives the span of `(` and `)`,
/// and the type of (opening) delimiter used.
fn find_delimiters<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<(Span, Span, char)> {
    let snippet = cx.sess().parse_sess.source_map().span_to_snippet(span).ok()?;
    let (open, open_ch) = snippet.char_indices().find(|&(_, c)| "([{".contains(c))?;
    let close = snippet.rfind(|c| ")]}".contains(c))?;
    Some((
        span.from_inner(InnerSpan { start: open, end: open + 1 }),
        span.from_inner(InnerSpan { start: close, end: close + 1 }),
        open_ch,
    ))
}

fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol, SymbolStr) {
    let mut expn = f.span.ctxt().outer_expn_data();

    let mut panic_macro = kw::Empty;

    // Unwrap more levels of macro expansion, as panic_2015!()
    // was likely expanded from panic!() and possibly from
    // [debug_]assert!().
    for &i in
        &[sym::std_panic_macro, sym::core_panic_macro, sym::assert_macro, sym::debug_assert_macro]
    {
        let parent = expn.call_site.ctxt().outer_expn_data();
        if parent.macro_def_id.map_or(false, |id| cx.tcx.is_diagnostic_item(i, id)) {
            expn = parent;
            panic_macro = i;
        }
    }

    let macro_symbol =
        if let hygiene::ExpnKind::Macro(_, symbol) = expn.kind { symbol } else { sym::panic };
    (expn.call_site, panic_macro, macro_symbol.as_str())
}

fn is_arg_inside_call(arg: Span, call: Span) -> bool {
    // We only add suggestions if the argument we're looking at appears inside the
    // panic call in the source file, to avoid invalid suggestions when macros are involved.
    // We specifically check for the spans to not be identical, as that happens sometimes when
    // proc_macros lie about spans and apply the same span to all the tokens they produce.
    call.contains(arg) && !call.source_equal(&arg)
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Specified types for SVG properties.

use crate::parser::{Parse, ParserContext};
use crate::values::generics::svg as generic;
use crate::values::specified::color::Color;
use crate::values::specified::url::SpecifiedUrl;
use crate::values::specified::AllowQuirks;
use crate::values::specified::LengthPercentage;
use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
use crate::values::CustomIdent;
use cssparser::{Parser, Token};
use std::fmt::{self, Write};
use style_traits::{CommaWithSpace, CssWriter, ParseError, Separator};
use style_traits::{StyleParseErrorKind, ToCss};

/// Specified SVG Paint value
pub type SVGPaint = generic::GenericSVGPaint<Color, SpecifiedUrl>;

/// <length> | <percentage> | <number> | context-value
pub type SVGLength = generic::SVGLength<LengthPercentage>;

/// A non-negative version of SVGLength.
pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>;

/// [ <length> | <percentage> | <number> ]# | context-value
pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeLengthPercentage>;

/// Whether the `context-value` value is enabled.
#[cfg(feature = "gecko")]
pub fn is_context_value_enabled() -> bool {
    static_prefs::pref!("gfx.font_rendering.opentype_svg.enabled")
}

/// Whether the `context-value` value is enabled.
#[cfg(not(feature = "gecko"))]
pub fn is_context_value_enabled() -> bool {
    false
}

macro_rules! parse_svg_length {
    ($ty:ty, $lp:ty) => {
        impl Parse for $ty {
            fn parse<'i, 't>(
                context: &ParserContext,
                input: &mut Parser<'i, 't>,
            ) -> Result<Self, ParseError<'i>> {
                if let Ok(lp) = input.try(|i| <$lp>::parse_quirky(context, i, AllowQuirks::Always))
                {
                    return Ok(generic::SVGLength::LengthPercentage(lp));
                }

                try_match_ident_ignore_ascii_case! { input,
                    "context-value" if is_context_value_enabled() => {
                        Ok(generic::SVGLength::ContextValue)
                    },
                }
            }
        }
    };
}

parse_svg_length!(SVGLength, LengthPercentage);
parse_svg_length!(SVGWidth, NonNegativeLengthPercentage);

impl Parse for SVGStrokeDashArray {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if let Ok(values) = input.try(|i| {
            CommaWithSpace::parse(i, |i| {
                NonNegativeLengthPercentage::parse_quirky(context, i, AllowQuirks::Always)
            })
        }) {
            return Ok(generic::SVGStrokeDashArray::Values(values.into()));
        }

        try_match_ident_ignore_ascii_case! { input,
            "context-value" if is_context_value_enabled() => {
                Ok(generic::SVGStrokeDashArray::ContextValue)
            },
            "none" => Ok(generic::SVGStrokeDashArray::Values(Default::default())),
        }
    }
}

/// <opacity-value> | context-fill-opacity | context-stroke-opacity
pub type SVGOpacity = generic::SVGOpacity<Opacity>;

/// The specified value for a single CSS paint-order property.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, ToCss)]
pub enum PaintOrder {
    /// `normal` variant
    Normal = 0,
    /// `fill` variant
    Fill = 1,
    /// `stroke` variant
    Stroke = 2,
    /// `markers` variant
    Markers = 3,
}

/// Number of non-normal components
pub const PAINT_ORDER_COUNT: u8 = 3;

/// Number of bits for each component
pub const PAINT_ORDER_SHIFT: u8 = 2;

/// Mask with above bits set
pub const PAINT_ORDER_MASK: u8 = 0b11;

/// The specified value is tree `PaintOrder` values packed into the
/// bitfields below, as a six-bit field, of 3 two-bit pairs
///
/// Each pair can be set to FILL, STROKE, or MARKERS
/// Lowest significant bit pairs are highest priority.
///  `normal` is the empty bitfield. The three pairs are
/// never zero in any case other than `normal`.
///
/// Higher priority values, i.e. the values specified first,
/// will be painted first (and may be covered by paintings of lower priority)
#[derive(
    Clone,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(transparent)]
pub struct SVGPaintOrder(pub u8);

impl SVGPaintOrder {
    /// Get default `paint-order` with `0`
    pub fn normal() -> Self {
        SVGPaintOrder(0)
    }

    /// Get variant of `paint-order`
    pub fn order_at(&self, pos: u8) -> PaintOrder {
        // Safe because PaintOrder covers all possible patterns.
        unsafe { std::mem::transmute((self.0 >> pos * PAINT_ORDER_SHIFT) & PAINT_ORDER_MASK) }
    }
}

impl Parse for SVGPaintOrder {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<SVGPaintOrder, ParseError<'i>> {
        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
            return Ok(SVGPaintOrder::normal());
        }

        let mut value = 0;
        // bitfield representing what we've seen so far
        // bit 1 is fill, bit 2 is stroke, bit 3 is markers
        let mut seen = 0;
        let mut pos = 0;

        loop {
            let result: Result<_, ParseError> = input.try(|input| {
                try_match_ident_ignore_ascii_case! { input,
                    "fill" => Ok(PaintOrder::Fill),
                    "stroke" => Ok(PaintOrder::Stroke),
                    "markers" => Ok(PaintOrder::Markers),
                }
            });

            match result {
                Ok(val) => {
                    if (seen & (1 << val as u8)) != 0 {
                        // don't parse the same ident twice
                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                    }

                    value |= (val as u8) << (pos * PAINT_ORDER_SHIFT);
                    seen |= 1 << (val as u8);
                    pos += 1;
                },
                Err(_) => break,
            }
        }

        if value == 0 {
            // Couldn't find any keyword
            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
        }

        // fill in rest
        for i in pos..PAINT_ORDER_COUNT {
            for paint in 1..(PAINT_ORDER_COUNT + 1) {
                // if not seen, set bit at position, mark as seen
                if (seen & (1 << paint)) == 0 {
                    seen |= 1 << paint;
                    value |= paint << (i * PAINT_ORDER_SHIFT);
                    break;
                }
            }
        }

        Ok(SVGPaintOrder(value))
    }
}

impl ToCss for SVGPaintOrder {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        if self.0 == 0 {
            return dest.write_str("normal");
        }

        let mut last_pos_to_serialize = 0;
        for i in (1..PAINT_ORDER_COUNT).rev() {
            let component = self.order_at(i);
            let earlier_component = self.order_at(i - 1);
            if component < earlier_component {
                last_pos_to_serialize = i - 1;
                break;
            }
        }

        for pos in 0..last_pos_to_serialize + 1 {
            if pos != 0 {
                dest.write_str(" ")?
            }
            self.order_at(pos).to_css(dest)?;
        }
        Ok(())
    }
}

bitflags! {
    /// The context properties we understand.
    #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
    #[repr(C)]
    pub struct ContextPropertyBits: u8 {
        /// `fill`
        const FILL = 1 << 0;
        /// `stroke`
        const STROKE = 1 << 1;
        /// `fill-opacity`
        const FILL_OPACITY = 1 << 2;
        /// `stroke-opacity`
        const STROKE_OPACITY = 1 << 3;
    }
}

/// Specified MozContextProperties value.
/// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)
#[derive(
    Clone,
    Debug,
    Default,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
pub struct MozContextProperties {
    #[css(iterable, if_empty = "none")]
    #[ignore_malloc_size_of = "Arc"]
    idents: crate::ArcSlice<CustomIdent>,
    #[css(skip)]
    bits: ContextPropertyBits,
}

impl Parse for MozContextProperties {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<MozContextProperties, ParseError<'i>> {
        let mut values = vec![];
        let mut bits = ContextPropertyBits::empty();
        loop {
            {
                let location = input.current_source_location();
                let ident = input.expect_ident()?;

                if ident.eq_ignore_ascii_case("none") && values.is_empty() {
                    return Ok(Self::default());
                }

                let ident = CustomIdent::from_ident(location, ident, &["all", "none", "auto"])?;

                if ident.0 == atom!("fill") {
                    bits.insert(ContextPropertyBits::FILL);
                } else if ident.0 == atom!("stroke") {
                    bits.insert(ContextPropertyBits::STROKE);
                } else if ident.0 == atom!("fill-opacity") {
                    bits.insert(ContextPropertyBits::FILL_OPACITY);
                } else if ident.0 == atom!("stroke-opacity") {
                    bits.insert(ContextPropertyBits::STROKE_OPACITY);
                }

                values.push(ident);
            }

            let location = input.current_source_location();
            match input.next() {
                Ok(&Token::Comma) => continue,
                Err(..) => break,
                Ok(other) => return Err(location.new_unexpected_token_error(other.clone())),
            }
        }

        if values.is_empty() {
            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
        }

        Ok(MozContextProperties {
            idents: crate::ArcSlice::from_iter(values.into_iter()),
            bits,
        })
    }
}
<commit_msg>Don't use transmute to create PaintOrder values<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Specified types for SVG properties.

use crate::parser::{Parse, ParserContext};
use crate::values::generics::svg as generic;
use crate::values::specified::color::Color;
use crate::values::specified::url::SpecifiedUrl;
use crate::values::specified::AllowQuirks;
use crate::values::specified::LengthPercentage;
use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
use crate::values::CustomIdent;
use cssparser::{Parser, Token};
use std::fmt::{self, Write};
use style_traits::{CommaWithSpace, CssWriter, ParseError, Separator};
use style_traits::{StyleParseErrorKind, ToCss};

/// Specified SVG Paint value
pub type SVGPaint = generic::GenericSVGPaint<Color, SpecifiedUrl>;

/// <length> | <percentage> | <number> | context-value
pub type SVGLength = generic::SVGLength<LengthPercentage>;

/// A non-negative version of SVGLength.
pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>;

/// [ <length> | <percentage> | <number> ]# | context-value
pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeLengthPercentage>;

/// Whether the `context-value` value is enabled.
#[cfg(feature = "gecko")]
pub fn is_context_value_enabled() -> bool {
    static_prefs::pref!("gfx.font_rendering.opentype_svg.enabled")
}

/// Whether the `context-value` value is enabled.
#[cfg(not(feature = "gecko"))]
pub fn is_context_value_enabled() -> bool {
    false
}

macro_rules! parse_svg_length {
    ($ty:ty, $lp:ty) => {
        impl Parse for $ty {
            fn parse<'i, 't>(
                context: &ParserContext,
                input: &mut Parser<'i, 't>,
            ) -> Result<Self, ParseError<'i>> {
                if let Ok(lp) = input.try(|i| <$lp>::parse_quirky(context, i, AllowQuirks::Always))
                {
                    return Ok(generic::SVGLength::LengthPercentage(lp));
                }

                try_match_ident_ignore_ascii_case! { input,
                    "context-value" if is_context_value_enabled() => {
                        Ok(generic::SVGLength::ContextValue)
                    },
                }
            }
        }
    };
}

parse_svg_length!(SVGLength, LengthPercentage);
parse_svg_length!(SVGWidth, NonNegativeLengthPercentage);

impl Parse for SVGStrokeDashArray {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if let Ok(values) = input.try(|i| {
            CommaWithSpace::parse(i, |i| {
                NonNegativeLengthPercentage::parse_quirky(context, i, AllowQuirks::Always)
            })
        }) {
            return Ok(generic::SVGStrokeDashArray::Values(values.into()));
        }

        try_match_ident_ignore_ascii_case! { input,
            "context-value" if is_context_value_enabled() => {
                Ok(generic::SVGStrokeDashArray::ContextValue)
            },
            "none" => Ok(generic::SVGStrokeDashArray::Values(Default::default())),
        }
    }
}

/// <opacity-value> | context-fill-opacity | context-stroke-opacity
pub type SVGOpacity = generic::SVGOpacity<Opacity>;

/// The specified value for a single CSS paint-order property.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, ToCss)]
pub enum PaintOrder {
    /// `normal` variant
    Normal = 0,
    /// `fill` variant
    Fill = 1,
    /// `stroke` variant
    Stroke = 2,
    /// `markers` variant
    Markers = 3,
}

/// Number of non-normal components
pub const PAINT_ORDER_COUNT: u8 = 3;

/// Number of bits for each component
pub const PAINT_ORDER_SHIFT: u8 = 2;

/// Mask with above bits set
pub const PAINT_ORDER_MASK: u8 = 0b11;

/// The specified value is tree `PaintOrder` values packed into the
/// bitfields below, as a six-bit field, of 3 two-bit pairs
///
/// Each pair can be set to FILL, STROKE, or MARKERS
/// Lowest significant bit pairs are highest priority.
///  `normal` is the empty bitfield. The three pairs are
/// never zero in any case other than `normal`.
///
/// Higher priority values, i.e. the values specified first,
/// will be painted first (and may be covered by paintings of lower priority)
#[derive(
    Clone,
    Copy,
    Debug,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(transparent)]
pub struct SVGPaintOrder(pub u8);

impl SVGPaintOrder {
    /// Get default `paint-order` with `0`
    pub fn normal() -> Self {
        SVGPaintOrder(0)
    }

    /// Get variant of `paint-order`
    pub fn order_at(&self, pos: u8) -> PaintOrder {
        match (self.0 >> pos * PAINT_ORDER_SHIFT) & PAINT_ORDER_MASK {
            0 => PaintOrder::Normal,
            1 => PaintOrder::Fill,
            2 => PaintOrder::Stroke,
            3 => PaintOrder::Markers,
            _ => unreachable!("this cannot happen"),
        }
    }
}

impl Parse for SVGPaintOrder {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<SVGPaintOrder, ParseError<'i>> {
        if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
            return Ok(SVGPaintOrder::normal());
        }

        let mut value = 0;
        // bitfield representing what we've seen so far
        // bit 1 is fill, bit 2 is stroke, bit 3 is markers
        let mut seen = 0;
        let mut pos = 0;

        loop {
            let result: Result<_, ParseError> = input.try(|input| {
                try_match_ident_ignore_ascii_case! { input,
                    "fill" => Ok(PaintOrder::Fill),
                    "stroke" => Ok(PaintOrder::Stroke),
                    "markers" => Ok(PaintOrder::Markers),
                }
            });

            match result {
                Ok(val) => {
                    if (seen & (1 << val as u8)) != 0 {
                        // don't parse the same ident twice
                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                    }

                    value |= (val as u8) << (pos * PAINT_ORDER_SHIFT);
                    seen |= 1 << (val as u8);
                    pos += 1;
                },
                Err(_) => break,
            }
        }

        if value == 0 {
            // Couldn't find any keyword
            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
        }

        // fill in rest
        for i in pos..PAINT_ORDER_COUNT {
            for paint in 1..(PAINT_ORDER_COUNT + 1) {
                // if not seen, set bit at position, mark as seen
                if (seen & (1 << paint)) == 0 {
                    seen |= 1 << paint;
                    value |= paint << (i * PAINT_ORDER_SHIFT);
                    break;
                }
            }
        }

        Ok(SVGPaintOrder(value))
    }
}

impl ToCss for SVGPaintOrder {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        if self.0 == 0 {
            return dest.write_str("normal");
        }

        let mut last_pos_to_serialize = 0;
        for i in (1..PAINT_ORDER_COUNT).rev() {
            let component = self.order_at(i);
            let earlier_component = self.order_at(i - 1);
            if component < earlier_component {
                last_pos_to_serialize = i - 1;
                break;
            }
        }

        for pos in 0..last_pos_to_serialize + 1 {
            if pos != 0 {
                dest.write_str(" ")?
            }
            self.order_at(pos).to_css(dest)?;
        }
        Ok(())
    }
}

bitflags! {
    /// The context properties we understand.
    #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
    #[repr(C)]
    pub struct ContextPropertyBits: u8 {
        /// `fill`
        const FILL = 1 << 0;
        /// `stroke`
        const STROKE = 1 << 1;
        /// `fill-opacity`
        const FILL_OPACITY = 1 << 2;
        /// `stroke-opacity`
        const STROKE_OPACITY = 1 << 3;
    }
}

/// Specified MozContextProperties value.
/// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)
#[derive(
    Clone,
    Debug,
    Default,
    MallocSizeOf,
    PartialEq,
    SpecifiedValueInfo,
    ToComputedValue,
    ToCss,
    ToResolvedValue,
    ToShmem,
)]
#[repr(C)]
pub struct MozContextProperties {
    #[css(iterable, if_empty = "none")]
    #[ignore_malloc_size_of = "Arc"]
    idents: crate::ArcSlice<CustomIdent>,
    #[css(skip)]
    bits: ContextPropertyBits,
}

impl Parse for MozContextProperties {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<MozContextProperties, ParseError<'i>> {
        let mut values = vec![];
        let mut bits = ContextPropertyBits::empty();
        loop {
            {
                let location = input.current_source_location();
                let ident = input.expect_ident()?;

                if ident.eq_ignore_ascii_case("none") && values.is_empty() {
                    return Ok(Self::default());
                }

                let ident = CustomIdent::from_ident(location, ident, &["all", "none", "auto"])?;

                if ident.0 == atom!("fill") {
                    bits.insert(ContextPropertyBits::FILL);
                } else if ident.0 == atom!("stroke") {
                    bits.insert(ContextPropertyBits::STROKE);
                } else if ident.0 == atom!("fill-opacity") {
                    bits.insert(ContextPropertyBits::FILL_OPACITY);
                } else if ident.0 == atom!("stroke-opacity") {
                    bits.insert(ContextPropertyBits::STROKE_OPACITY);
                }

                values.push(ident);
            }

            let location = input.current_source_location();
            match input.next() {
                Ok(&Token::Comma) => continue,
                Err(..) => break,
                Ok(other) => return Err(location.new_unexpected_token_error(other.clone())),
            }
        }

        if values.is_empty() {
            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
        }

        Ok(MozContextProperties {
            idents: crate::ArcSlice::from_iter(values.into_iter()),
            bits,
        })
    }
}
<|endoftext|>