<commit_before>extern crate cc;
extern crate fs_extra;
extern crate metadeps;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

macro_rules! t {
    ($e:expr) => {
        match $e {
            Ok(t) => t,
            Err(e) => panic!("{} return the error {}", stringify!($e), e),
        }
    };
}

fn main() {
    // Use the installed libcares if it is available.
    if metadeps::probe().is_ok() {
        return;
    }

    // Rerun if the c-ares source code has changed.
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=c-ares");

    // Clean up previous build, if any.
    let outdir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = outdir.join("build");
    let _ = fs::remove_dir_all(&build);
    fs::create_dir(&build).unwrap();

    // Copy the c-ares source code into $OUT_DIR, where it's safe for the build
    // process to modify it.
    let c_ares_dir = outdir.join("c-ares");
    let _ = fs::remove_dir_all(&c_ares_dir);
    let copy_options = fs_extra::dir::CopyOptions::new();
    let src = env::current_dir().unwrap().join("c-ares");
    fs_extra::dir::copy(&src, &outdir, &copy_options).unwrap();

    // MSVC builds are different.
    let target = env::var("TARGET").unwrap();
    if target.contains("msvc") {
        build_msvc(&target);
        return;
    }

    // Prepare.
    run(Command::new("sh").current_dir(&c_ares_dir).arg("buildconf"));

    // Configure.
    let cfg = cc::Build::new();
    let compiler = cfg.get_compiler();
    let mut cflags = OsString::new();
    for arg in compiler.args() {
        cflags.push(arg);
        cflags.push(" ");
    }

    let mut cmd = Command::new("sh");
    cmd.env("CFLAGS", &cflags)
        .env("CC", compiler.path())
        .current_dir(&build)
        .arg(format!("{}", c_ares_dir.join("configure").display()))
        .arg("--enable-static=yes")
        .arg("--enable-shared=no")
        .arg("--enable-optimize")
        .arg(format!("--prefix={}", outdir.display()));

    // This code fragment copied from curl-rust... c-ares and curl come from
    // the same developer so are usually pretty similar, and this seems to
    // work.
    //
    // NOTE GNU terminology
    // BUILD = machine where we are (cross) compiling
    // HOST = machine where the compiled binary will be used
    // TARGET = only relevant when compiling compilers
    let host = env::var("HOST").unwrap();
    if target != host && (!target.contains("windows") || !host.contains("windows")) {
        if target.contains("windows") {
            cmd.arg(format!("--host={}", host));
            cmd.arg(format!("--target={}", target));
        } else {
            cmd.arg(format!("--build={}", host));
            cmd.arg(format!("--host={}", target));
        }
    }
    run(&mut cmd);

    // Compile.
    run(Command::new(make())
        .arg(format!("-j{}", env::var("NUM_JOBS").unwrap()))
        .current_dir(&build));

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/.libs", build.display());
    println!("cargo:rustc-link-lib=static=cares");
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(t!(cmd.status()).success());
}

fn make() -> &'static str {
    if cfg!(target_os = "freebsd") {
        "gmake"
    } else {
        "make"
    }
}

fn nmake(target: &str) -> Command {
    // cargo messes with the environment in a way that nmake does not like -
    // see https://github.com/rust-lang/cargo/issues/4156.  Explicitly remove
    // the unwanted variables.
    let mut cmd = cc::windows_registry::find(target, "nmake.exe").unwrap();
    cmd.env_remove("MAKEFLAGS").env_remove("MFLAGS");
    cmd
}

fn build_msvc(target: &str) {
    // Prepare.  We've already copied the c-ares source code into the output
    // directory.
    let outdir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let c_ares_dir = outdir.join("c-ares");
    run(Command::new("cmd")
        .current_dir(&c_ares_dir)
        .arg("/c")
        .arg("buildconf.bat"));

    // Compile.
    let mut cmd = nmake(target);
    cmd.current_dir(&c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "CFG=lib-release", "c-ares"]);
    run(&mut cmd);

    // Install library.
    let build = outdir.join("build");
    let mut cmd = nmake(target);
    cmd.current_dir(&c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "/a", "CFG=lib-release", "install"]);
    cmd.env("INSTALL_DIR", format!("{}", build.display()));
    run(&mut cmd);

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/lib", build.display());
    println!("cargo:rustc-link-lib=static=libcares");
}
<commit_msg>fiddling with the build script<commit_after>extern crate cc;
extern crate fs_extra;
extern crate metadeps;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

macro_rules! t {
    ($e:expr) => {
        match $e {
            Ok(t) => t,
            Err(e) => panic!("{} return the error {}", stringify!($e), e),
        }
    };
}

fn main() {
    // Rerun if the c-ares source code has changed.
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=c-ares");

    // Use the installed libcares if it is available.
    if metadeps::probe().is_ok() {
        return;
    }

    // We'll compile from source.  Clean up previous build, if any.
    let outdir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = outdir.join("build");
    let _ = fs::remove_dir_all(&build);
    fs::create_dir(&build).unwrap();

    // Copy the c-ares source code into $OUT_DIR, where it's safe for the build
    // process to modify it.
    let c_ares_dir = outdir.join("c-ares");
    let _ = fs::remove_dir_all(&c_ares_dir);
    let copy_options = fs_extra::dir::CopyOptions::new();
    let src = env::current_dir().unwrap().join("c-ares");
    fs_extra::dir::copy(&src, &outdir, &copy_options).unwrap();

    // MSVC builds are different.
    let target = env::var("TARGET").unwrap();
    if target.contains("msvc") {
        build_msvc(&target);
        return;
    }

    // Prepare.
    run(Command::new("sh").current_dir(&c_ares_dir).arg("buildconf"));

    // Configure.
    let cfg = cc::Build::new();
    let compiler = cfg.get_compiler();
    let mut cflags = OsString::new();
    for arg in compiler.args() {
        cflags.push(arg);
        cflags.push(" ");
    }

    let mut cmd = Command::new("sh");
    cmd.env("CFLAGS", &cflags)
        .env("CC", compiler.path())
        .current_dir(&build)
        .arg(format!("{}", c_ares_dir.join("configure").display()))
        .arg("--enable-static=yes")
        .arg("--enable-shared=no")
        .arg("--enable-optimize")
        .arg(format!("--prefix={}", outdir.display()));

    // This code fragment copied from curl-rust... c-ares and curl come from
    // the same developer so are usually pretty similar, and this seems to
    // work.
    //
    // NOTE GNU terminology
    // BUILD = machine where we are (cross) compiling
    // HOST = machine where the compiled binary will be used
    // TARGET = only relevant when compiling compilers
    let host = env::var("HOST").unwrap();
    if target != host && (!target.contains("windows") || !host.contains("windows")) {
        if target.contains("windows") {
            cmd.arg(format!("--host={}", host));
            cmd.arg(format!("--target={}", target));
        } else {
            cmd.arg(format!("--build={}", host));
            cmd.arg(format!("--host={}", target));
        }
    }
    run(&mut cmd);

    // Compile.
    run(Command::new(make())
        .arg(format!("-j{}", env::var("NUM_JOBS").unwrap()))
        .current_dir(&build));

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/.libs", build.display());
    println!("cargo:rustc-link-lib=static=cares");
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(t!(cmd.status()).success());
}

fn make() -> &'static str {
    if cfg!(target_os = "freebsd") {
        "gmake"
    } else {
        "make"
    }
}

fn nmake(target: &str) -> Command {
    // cargo messes with the environment in a way that nmake does not like -
    // see https://github.com/rust-lang/cargo/issues/4156.  Explicitly remove
    // the unwanted variables.
    let mut cmd = cc::windows_registry::find(target, "nmake.exe").unwrap();
    cmd.env_remove("MAKEFLAGS").env_remove("MFLAGS");
    cmd
}

fn build_msvc(target: &str) {
    // Prepare.  We've already copied the c-ares source code into the output
    // directory.
    let outdir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let c_ares_dir = outdir.join("c-ares");
    run(Command::new("cmd")
        .current_dir(&c_ares_dir)
        .arg("/c")
        .arg("buildconf.bat"));

    // Compile.
    let mut cmd = nmake(target);
    cmd.current_dir(&c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "CFG=lib-release", "c-ares"]);
    run(&mut cmd);

    // Install library.
    let build = outdir.join("build");
    let mut cmd = nmake(target);
    cmd.current_dir(&c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "/a", "CFG=lib-release", "install"]);
    cmd.env("INSTALL_DIR", format!("{}", build.display()));
    run(&mut cmd);

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/lib", build.display());
    println!("cargo:rustc-link-lib=static=libcares");
}
<|endoftext|>


<commit_before>// Std
use std::io::Write;

// Internal
use crate::Generator;
use clap::*;

/// Generate bash completion file
pub struct Bash;

impl Generator for Bash {
    fn file_name(name: &str) -> String {
        format!("{}.bash", name)
    }

    fn generate(app: &App, buf: &mut dyn Write) {
        let bin_name = app.get_bin_name().unwrap();

        w!(
            buf,
            format!(
                "_{name}() {{
    local i cur prev opts cmds
    COMPREPLY=()
    cur=\"${{COMP_WORDS[COMP_CWORD]}}\"
    prev=\"${{COMP_WORDS[COMP_CWORD-1]}}\"
    cmd=\"\"
    opts=\"\"

    for i in ${{COMP_WORDS[@]}}
    do
        case \"${{i}}\" in
            {name})
                cmd=\"{name}\"
                ;;
            {subcmds}
            *)
                ;;
        esac
    done

    case \"${{cmd}}\" in
        {name})
            opts=\"{name_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {name_opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
            return 0
            ;;
        {subcmd_details}
    esac
}}

complete -F _{name} -o bashdefault -o default {name}
",
                name = bin_name,
                name_opts = all_options_for_path(app, bin_name),
                name_opts_details = option_details_for_path(app, bin_name),
                subcmds = all_subcommands(app),
                subcmd_details = subcommand_details(app)
            )
            .as_bytes()
        );
    }
}

fn all_subcommands(app: &App) -> String {
    debugln!("Bash::all_subcommands;");

    let mut subcmds = String::new();
    let mut scs = Bash::all_subcommands(app)
        .iter()
        .map(|x| x.0.clone())
        .collect::<Vec<_>>();

    scs.sort();
    scs.dedup();

    for sc in &scs {
        subcmds = format!(
            "{}
            {name})
                cmd+=\"__{fn_name}\"
                ;;",
            subcmds,
            name = sc,
            fn_name = sc.replace("-", "__")
        );
    }

    subcmds
}

fn subcommand_details(app: &App) -> String {
    debugln!("Bash::subcommand_details;");

    let mut subcmd_dets = String::new();
    let mut scs = Bash::all_subcommands(app)
        .iter()
        .map(|x| x.1.replace(" ", "__"))
        .collect::<Vec<_>>();

    scs.sort();

    for sc in &scs {
        subcmd_dets = format!(
            "{}
        {subcmd})
            opts=\"{sc_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
            return 0
            ;;",
            subcmd_dets,
            subcmd = sc.replace("-", "__"),
            sc_opts = all_options_for_path(app, &*sc),
            level = sc.split("__").map(|_| 1).sum::<u64>(),
            opts_details = option_details_for_path(app, &*sc)
        );
    }

    subcmd_dets
}

fn option_details_for_path(app: &App, path: &str) -> String {
    debugln!("Bash::option_details_for_path: path={}", path);

    let p = Bash::find_subcommand_with_path(app, path.split("__").skip(1).collect());
    let mut opts = String::new();

    for o in opts!(p) {
        if let Some(l) = o.long {
            opts = format!(
                "{}
                --{})
                    COMPREPLY=({})
                    return 0
                    ;;",
                opts,
                l,
                vals_for(o)
            );
        }

        if let Some(s) = o.short {
            opts = format!(
                "{}
                    -{})
                    COMPREPLY=({})
                    return 0
                    ;;",
                opts,
                s,
                vals_for(o)
            );
        }
    }

    opts
}

fn vals_for(o: &Arg) -> String {
    debugln!("Bash::vals_for: o={}", o.name);

    if let Some(ref vals) = o.possible_vals {
        format!("$(compgen -W \"{}\" -- ${{cur}})", vals.join(" "))
    } else {
        String::from("$(compgen -f ${cur})")
    }
}

fn all_options_for_path(app: &App, path: &str) -> String {
    debugln!("Bash::all_options_for_path: path={}", path);

    let p = Bash::find_subcommand_with_path(app, path.split("__").skip(1).collect());
    let scs: Vec<_> = Bash::subcommands(p).iter().map(|x| x.0.clone()).collect();

    let opts = format!(
        "{shorts} {longs} {pos} {subcmds}",
        shorts = Bash::shorts(p)
            .iter()
            .fold(String::new(), |acc, s| format!("{} -{}", acc, s)),
        longs = Bash::longs(p)
            .iter()
            .fold(String::new(), |acc, l| format!("{} --{}", acc, l)),
        pos = positionals!(p).fold(String::new(), |acc, p| format!("{} {}", acc, p)),
        subcmds = scs.join(" "),
    );

    opts
}
<commit_msg>fix: bash completions with app name which contains `-`<commit_after>// Std
use std::io::Write;

// Internal
use crate::Generator;
use clap::*;

/// Generate bash completion file
pub struct Bash;

impl Generator for Bash {
    fn file_name(name: &str) -> String {
        format!("{}.bash", name)
    }

    fn generate(app: &App, buf: &mut dyn Write) {
        let bin_name = app.get_bin_name().unwrap();

        w!(
            buf,
            format!(
                "_{name}() {{
    local i cur prev opts cmds
    COMPREPLY=()
    cur=\"${{COMP_WORDS[COMP_CWORD]}}\"
    prev=\"${{COMP_WORDS[COMP_CWORD-1]}}\"
    cmd=\"\"
    opts=\"\"

    for i in ${{COMP_WORDS[@]}}
    do
        case \"${{i}}\" in
            {name})
                cmd=\"{cmd}\"
                ;;
            {subcmds}
            *)
                ;;
        esac
    done

    case \"${{cmd}}\" in
        {cmd})
            opts=\"{name_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {name_opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
            return 0
            ;;
        {subcmd_details}
    esac
}}

complete -F _{name} -o bashdefault -o default {name}
",
                name = bin_name,
                cmd = bin_name.replace("-", "__"),
                name_opts = all_options_for_path(app, bin_name),
                name_opts_details = option_details_for_path(app, bin_name),
                subcmds = all_subcommands(app),
                subcmd_details = subcommand_details(app)
            )
            .as_bytes()
        );
    }
}

fn all_subcommands(app: &App) -> String {
    debugln!("Bash::all_subcommands;");

    let mut subcmds = String::new();
    let mut scs = Bash::all_subcommands(app)
        .iter()
        .map(|x| x.0.clone())
        .collect::<Vec<_>>();

    scs.sort();
    scs.dedup();

    for sc in &scs {
        subcmds = format!(
            "{}
            {name})
                cmd+=\"__{fn_name}\"
                ;;",
            subcmds,
            name = sc,
            fn_name = sc.replace("-", "__")
        );
    }

    subcmds
}

fn subcommand_details(app: &App) -> String {
    debugln!("Bash::subcommand_details;");

    let mut subcmd_dets = String::new();
    let mut scs = Bash::all_subcommands(app)
        .iter()
        .map(|x| x.1.replace(" ", "__"))
        .collect::<Vec<_>>();

    scs.sort();

    for sc in &scs {
        subcmd_dets = format!(
            "{}
        {subcmd})
            opts=\"{sc_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
            return 0
            ;;",
            subcmd_dets,
            subcmd = sc.replace("-", "__"),
            sc_opts = all_options_for_path(app, &*sc),
            level = sc.split("__").map(|_| 1).sum::<u64>(),
            opts_details = option_details_for_path(app, &*sc)
        );
    }

    subcmd_dets
}

fn option_details_for_path(app: &App, path: &str) -> String {
    debugln!("Bash::option_details_for_path: path={}", path);

    let p = Bash::find_subcommand_with_path(app, path.split("__").skip(1).collect());
    let mut opts = String::new();

    for o in opts!(p) {
        if let Some(l) = o.long {
            opts = format!(
                "{}
                --{})
                    COMPREPLY=({})
                    return 0
                    ;;",
                opts,
                l,
                vals_for(o)
            );
        }

        if let Some(s) = o.short {
            opts = format!(
                "{}
                    -{})
                    COMPREPLY=({})
                    return 0
                    ;;",
                opts,
                s,
                vals_for(o)
            );
        }
    }

    opts
}

fn vals_for(o: &Arg) -> String {
    debugln!("Bash::vals_for: o={}", o.name);

    if let Some(ref vals) = o.possible_vals {
        format!("$(compgen -W \"{}\" -- ${{cur}})", vals.join(" "))
    } else {
        String::from("$(compgen -f ${cur})")
    }
}

fn all_options_for_path(app: &App, path: &str) -> String {
    debugln!("Bash::all_options_for_path: path={}", path);

    let p = Bash::find_subcommand_with_path(app, path.split("__").skip(1).collect());
    let scs: Vec<_> = Bash::subcommands(p).iter().map(|x| x.0.clone()).collect();

    let opts = format!(
        "{shorts} {longs} {pos} {subcmds}",
        shorts = Bash::shorts(p)
            .iter()
            .fold(String::new(), |acc, s| format!("{} -{}", acc, s)),
        longs = Bash::longs(p)
            .iter()
            .fold(String::new(), |acc, l| format!("{} --{}", acc, l)),
        pos = positionals!(p).fold(String::new(), |acc, p| format!("{} {}", acc, p)),
        subcmds = scs.join(" "),
    );

    opts
}
<|endoftext|>


<commit_before>use crate::consts::{constant, Constant};
use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call, snippet_opt, span_lint_and_help};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind, PatKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
    ///
    /// **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a
    /// `panic!()` or `unreachable!()`
    ///
    /// **Known problems:** None
    ///
    /// **Example:**
    /// ```rust,ignore
    /// assert!(false)
    /// assert!(true)
    /// const B: bool = false;
    /// assert!(B)
    /// ```
    pub ASSERTIONS_ON_CONSTANTS,
    style,
    "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
}

declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);

impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
        let lint_true = |is_debug: bool| {
            span_lint_and_help(
                cx,
                ASSERTIONS_ON_CONSTANTS,
                e.span,
                if is_debug {
                    "`debug_assert!(true)` will be optimized out by the compiler"
                } else {
                    "`assert!(true)` will be optimized out by the compiler"
                },
                None,
                "remove it",
            );
        };
        let lint_false_without_message = || {
            span_lint_and_help(
                cx,
                ASSERTIONS_ON_CONSTANTS,
                e.span,
                "`assert!(false)` should probably be replaced",
                None,
                "use `panic!()` or `unreachable!()`",
            );
        };
        let lint_false_with_message = |panic_message: String| {
            span_lint_and_help(
                cx,
                ASSERTIONS_ON_CONSTANTS,
                e.span,
                &format!("`assert!(false, {})` should probably be replaced", panic_message),
                None,
                &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message),
            )
        };

        if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") {
            if debug_assert_span.from_expansion() {
                return;
            }
            if_chain! {
                if let ExprKind::Unary(_, ref lit) = e.kind;
                if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit);
                if is_true;
                then {
                    lint_true(true);
                }
            };
        } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
            if assert_span.from_expansion() {
                return;
            }
            if let Some(assert_match) = match_assert_with_message(&cx, e) {
                match assert_match {
                    // matched assert but not message
                    AssertKind::WithoutMessage(false) => lint_false_without_message(),
                    AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false),
                    AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message),
                };
            }
        }
    }
}

/// Result of calling `match_assert_with_message`.
enum AssertKind {
    WithMessage(String, bool),
    WithoutMessage(bool),
}

/// Check if the expression matches
///
/// ```rust,ignore
/// match { let _t = !c; _t } {
///     true => {
///         {
///             ::std::rt::begin_panic(message, _)
///         }
///     }
///     _ => { }
/// };
/// ```
///
/// where `message` is any expression and `c` is a constant bool.
fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
    if_chain! {
        if let ExprKind::Match(ref expr, ref arms, _) = expr.kind;
        // matches { let _t = expr; _t }
        if let ExprKind::DropTemps(ref expr) = expr.kind;
        if let ExprKind::Unary(UnOp::UnNot, ref expr) = expr.kind;
        // bind the first argument of the `assert!` macro
        if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
        // arm 1 pattern
        if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind;
        if let ExprKind::Lit(ref lit) = lit_expr.kind;
        if let LitKind::Bool(true) = lit.node;
        // arm 1 block
        if let ExprKind::Block(ref block, _) = arms[0].body.kind;
        if block.stmts.is_empty();
        if let Some(block_expr) = &block.expr;
        if let ExprKind::Block(ref inner_block, _) = block_expr.kind;
        if let Some(begin_panic_call) = &inner_block.expr;
        // function call
        if let Some(args) = match_panic_call(cx, begin_panic_call);
        if args.len() == 1;
        // bind the second argument of the `assert!` macro if it exists
        if let panic_message = snippet_opt(cx, args[0].span);
        // second argument of begin_panic is irrelevant
        // as is the second match arm
        then {
            // an empty message occurs when it was generated by the macro
            // (and not passed by the user)
            return panic_message
                .filter(|msg| !msg.is_empty())
                .map(|msg| AssertKind::WithMessage(msg, is_true))
                .or(Some(AssertKind::WithoutMessage(is_true)));
        }
    }
    None
}
<commit_msg>Clippy: Match on assert!() expansions without an inner block.<commit_after>use crate::consts::{constant, Constant};
use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call, snippet_opt, span_lint_and_help};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind, PatKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
    ///
    /// **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a
    /// `panic!()` or `unreachable!()`
    ///
    /// **Known problems:** None
    ///
    /// **Example:**
    /// ```rust,ignore
    /// assert!(false)
    /// assert!(true)
    /// const B: bool = false;
    /// assert!(B)
    /// ```
    pub ASSERTIONS_ON_CONSTANTS,
    style,
    "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
}

declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);

impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
        let lint_true = |is_debug: bool| {
            span_lint_and_help(
                cx,
                ASSERTIONS_ON_CONSTANTS,
                e.span,
                if is_debug {
                    "`debug_assert!(true)` will be optimized out by the compiler"
                } else {
                    "`assert!(true)` will be optimized out by the compiler"
                },
                None,
                "remove it",
            );
        };
        let lint_false_without_message = || {
            span_lint_and_help(
                cx,
                ASSERTIONS_ON_CONSTANTS,
                e.span,
                "`assert!(false)` should probably be replaced",
                None,
                "use `panic!()` or `unreachable!()`",
            );
        };
        let lint_false_with_message = |panic_message: String| {
            span_lint_and_help(
                cx,
                ASSERTIONS_ON_CONSTANTS,
                e.span,
                &format!("`assert!(false, {})` should probably be replaced", panic_message),
                None,
                &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message),
            )
        };

        if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") {
            if debug_assert_span.from_expansion() {
                return;
            }
            if_chain! {
                if let ExprKind::Unary(_, ref lit) = e.kind;
                if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit);
                if is_true;
                then {
                    lint_true(true);
                }
            };
        } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
            if assert_span.from_expansion() {
                return;
            }
            if let Some(assert_match) = match_assert_with_message(&cx, e) {
                match assert_match {
                    // matched assert but not message
                    AssertKind::WithoutMessage(false) => lint_false_without_message(),
                    AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false),
                    AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message),
                };
            }
        }
    }
}

/// Result of calling `match_assert_with_message`.
enum AssertKind {
    WithMessage(String, bool),
    WithoutMessage(bool),
}

/// Check if the expression matches
///
/// ```rust,ignore
/// match { let _t = !c; _t } {
///     true => {
///         {
///             ::std::rt::begin_panic(message, _)
///         }
///     }
///     _ => { }
/// };
/// ```
///
/// where `message` is any expression and `c` is a constant bool.
fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
    if_chain! {
        if let ExprKind::Match(ref expr, ref arms, _) = expr.kind;
        // matches { let _t = expr; _t }
        if let ExprKind::DropTemps(ref expr) = expr.kind;
        if let ExprKind::Unary(UnOp::UnNot, ref expr) = expr.kind;
        // bind the first argument of the `assert!` macro
        if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
        // arm 1 pattern
        if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind;
        if let ExprKind::Lit(ref lit) = lit_expr.kind;
        if let LitKind::Bool(true) = lit.node;
        // arm 1 block
        if let ExprKind::Block(ref block, _) = arms[0].body.kind;
        if block.stmts.is_empty();
        if let Some(block_expr) = &block.expr;
        // inner block is optional. unwarp it if it exists, or use the expression as is otherwise.
        if let Some(begin_panic_call) = match block_expr.kind {
            ExprKind::Block(ref inner_block, _) => &inner_block.expr,
            _ => &block.expr,
        };
        // function call
        if let Some(args) = match_panic_call(cx, begin_panic_call);
        if args.len() == 1;
        // bind the second argument of the `assert!` macro if it exists
        if let panic_message = snippet_opt(cx, args[0].span);
        // second argument of begin_panic is irrelevant
        // as is the second match arm
        then {
            // an empty message occurs when it was generated by the macro
            // (and not passed by the user)
            return panic_message
                .filter(|msg| !msg.is_empty())
                .map(|msg| AssertKind::WithMessage(msg, is_true))
                .or(Some(AssertKind::WithoutMessage(is_true)));
        }
    }
    None
}
<|endoftext|>


<commit_before>use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::query::outlives_bounds::InferCtxtExt as _;
use crate::traits::{self, TraitEngine, TraitEngineExt};

use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::traits::ObligationCause;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::infer::canonical::{Canonical, CanonicalizedQueryResponse, QueryResponse};
use rustc_middle::traits::query::Fallible;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::WithConstness;
use rustc_middle::ty::{self, Ty, TypeFoldable};
use rustc_span::{Span, DUMMY_SP};

use std::fmt::Debug;

pub use rustc_infer::infer::*;

pub trait InferCtxtExt<'tcx> {
    fn type_is_copy_modulo_regions(
        &self,
        param_env: ty::ParamEnv<'tcx>,
        ty: Ty<'tcx>,
        span: Span,
    ) -> bool;

    fn partially_normalize_associated_types_in<T>(
        &self,
        cause: ObligationCause<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
        value: T,
    ) -> InferOk<'tcx, T>
    where
        T: TypeFoldable<'tcx>;

    /// Check whether a `ty` implements given trait(trait_def_id).
    /// The inputs are:
    ///
    /// - the def-id of the trait
    /// - the self type
    /// - the *other* type parameters of the trait, excluding the self-type
    /// - the parameter environment
    ///
    /// Invokes `evaluate_obligation`, so in the event that evaluating
    /// `Ty: Trait` causes overflow, EvaluatedToRecur (or EvaluatedToUnknown)
    /// will be returned.
    fn type_implements_trait(
        &self,
        trait_def_id: DefId,
        ty: Ty<'tcx>,
        params: SubstsRef<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    ) -> traits::EvaluationResult;
}
impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
    fn type_is_copy_modulo_regions(
        &self,
        param_env: ty::ParamEnv<'tcx>,
        ty: Ty<'tcx>,
        span: Span,
    ) -> bool {
        let ty = self.resolve_vars_if_possible(ty);

        if !(param_env, ty).needs_infer() {
            return ty.is_copy_modulo_regions(self.tcx.at(span), param_env);
        }

        let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None);

        // This can get called from typeck (by euv), and `moves_by_default`
        // rightly refuses to work with inference variables, but
        // moves_by_default has a cache, which we want to use in other
        // cases.
        traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span)
    }

    /// Normalizes associated types in `value`, potentially returning
    /// new obligations that must further be processed.
    fn partially_normalize_associated_types_in<T>(
        &self,
        cause: ObligationCause<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
        value: T,
    ) -> InferOk<'tcx, T>
    where
        T: TypeFoldable<'tcx>,
    {
        debug!("partially_normalize_associated_types_in(value={:?})", value);
        let mut selcx = traits::SelectionContext::new(self);
        let traits::Normalized { value, obligations } =
            traits::normalize(&mut selcx, param_env, cause, value);
        debug!(
            "partially_normalize_associated_types_in: result={:?} predicates={:?}",
            value, obligations
        );
        InferOk { value, obligations }
    }

    fn type_implements_trait(
        &self,
        trait_def_id: DefId,
        ty: Ty<'tcx>,
        params: SubstsRef<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    ) -> traits::EvaluationResult {
        debug!(
            "type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}",
            trait_def_id, ty, params, param_env
        );

        let trait_ref =
            ty::TraitRef { def_id: trait_def_id, substs: self.tcx.mk_substs_trait(ty, params) };

        let obligation = traits::Obligation {
            cause: traits::ObligationCause::dummy(),
            param_env,
            recursion_depth: 0,
            predicate: trait_ref.without_const().to_predicate(self.tcx),
        };
        self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr)
    }
}

pub trait InferCtxtBuilderExt<'tcx> {
    fn enter_canonical_trait_query<K, R>(
        &mut self,
        canonical_key: &Canonical<'tcx, K>,
        operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>,
    ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>>
    where
        K: TypeFoldable<'tcx>,
        R: Debug + TypeFoldable<'tcx>,
        Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>;
}

impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
    /// The "main method" for a canonicalized trait query. Given the
    /// canonical key `canonical_key`, this method will create a new
    /// inference context, instantiate the key, and run your operation
    /// `op`. The operation should yield up a result (of type `R`) as
    /// well as a set of trait obligations that must be fully
    /// satisfied. These obligations will be processed and the
    /// canonical result created.
    ///
    /// Returns `NoSolution` in the event of any error.
    ///
    /// (It might be mildly nicer to implement this on `TyCtxt`, and
    /// not `InferCtxtBuilder`, but that is a bit tricky right now.
    /// In part because we would need a `for<'tcx>` sort of
    /// bound for the closure and in part because it is convenient to
    /// have `'tcx` be free on this function so that we can talk about
    /// `K: TypeFoldable<'tcx>`.)
    fn enter_canonical_trait_query<K, R>(
        &mut self,
        canonical_key: &Canonical<'tcx, K>,
        operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>,
    ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>>
    where
        K: TypeFoldable<'tcx>,
        R: Debug + TypeFoldable<'tcx>,
        Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
    {
        self.enter_with_canonical(
            DUMMY_SP,
            canonical_key,
            |ref infcx, key, canonical_inference_vars| {
                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
                let value = operation(infcx, &mut *fulfill_cx, key)?;
                infcx.make_canonicalized_query_response(
                    canonical_inference_vars,
                    value,
                    &mut *fulfill_cx,
                )
            },
        )
    }
}

pub trait OutlivesEnvironmentExt<'tcx> {
    fn add_implied_bounds(
        &mut self,
        infcx: &InferCtxt<'a, 'tcx>,
        fn_sig_tys: &[Ty<'tcx>],
        body_id: hir::HirId,
        span: Span,
    );
}

impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> {
    /// This method adds "implied bounds" into the outlives environment.
    /// Implied bounds are outlives relationships that we can deduce
    /// on the basis that certain types must be well-formed -- these are
    /// either the types that appear in the function signature or else
    /// the input types to an impl. For example, if you have a function
    /// like
    ///
    /// ```
    /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { }
    /// ```
    ///
    /// we can assume in the caller's body that `'b: 'a` and that `T:
    /// 'b` (and hence, transitively, that `T: 'a`). This method would
    /// add those assumptions into the outlives-environment.
    ///
    /// Tests: `src/test/ui/regions/regions-free-region-ordering-*.rs`
    fn add_implied_bounds(
        &mut self,
        infcx: &InferCtxt<'a, 'tcx>,
        fn_sig_tys: &[Ty<'tcx>],
        body_id: hir::HirId,
        span: Span,
    ) {
        debug!("add_implied_bounds()");

        for &ty in fn_sig_tys {
            let ty = infcx.resolve_vars_if_possible(ty);
            debug!("add_implied_bounds: ty = {}", ty);
            let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span);
            self.add_outlives_bounds(Some(infcx), implied_bounds)
        }
    }
}
<commit_msg>trailing whitespace<commit_after>use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::query::outlives_bounds::InferCtxtExt as _;
use crate::traits::{self, TraitEngine, TraitEngineExt};

use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::traits::ObligationCause;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::infer::canonical::{Canonical, CanonicalizedQueryResponse, QueryResponse};
use rustc_middle::traits::query::Fallible;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::WithConstness;
use rustc_middle::ty::{self, Ty, TypeFoldable};
use rustc_span::{Span, DUMMY_SP};

use std::fmt::Debug;

pub use rustc_infer::infer::*;

pub trait InferCtxtExt<'tcx> {
    fn type_is_copy_modulo_regions(
        &self,
        param_env: ty::ParamEnv<'tcx>,
        ty: Ty<'tcx>,
        span: Span,
    ) -> bool;

    fn partially_normalize_associated_types_in<T>(
        &self,
        cause: ObligationCause<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
        value: T,
    ) -> InferOk<'tcx, T>
    where
        T: TypeFoldable<'tcx>;

    /// Check whether a `ty` implements given trait(trait_def_id).
    /// The inputs are:
    ///
    /// - the def-id of the trait
    /// - the self type
    /// - the *other* type parameters of the trait, excluding the self-type
    /// - the parameter environment
    ///
    /// Invokes `evaluate_obligation`, so in the event that evaluating
    /// `Ty: Trait` causes overflow, EvaluatedToRecur (or EvaluatedToUnknown)
    /// will be returned.
    fn type_implements_trait(
        &self,
        trait_def_id: DefId,
        ty: Ty<'tcx>,
        params: SubstsRef<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    ) -> traits::EvaluationResult;
}
impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
    fn type_is_copy_modulo_regions(
        &self,
        param_env: ty::ParamEnv<'tcx>,
        ty: Ty<'tcx>,
        span: Span,
    ) -> bool {
        let ty = self.resolve_vars_if_possible(ty);

        if !(param_env, ty).needs_infer() {
            return ty.is_copy_modulo_regions(self.tcx.at(span), param_env);
        }

        let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None);

        // This can get called from typeck (by euv), and `moves_by_default`
        // rightly refuses to work with inference variables, but
        // moves_by_default has a cache, which we want to use in other
        // cases.
        traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span)
    }

    /// Normalizes associated types in `value`, potentially returning
    /// new obligations that must further be processed.
    fn partially_normalize_associated_types_in<T>(
        &self,
        cause: ObligationCause<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
        value: T,
    ) -> InferOk<'tcx, T>
    where
        T: TypeFoldable<'tcx>,
    {
        debug!("partially_normalize_associated_types_in(value={:?})", value);
        let mut selcx = traits::SelectionContext::new(self);
        let traits::Normalized { value, obligations } =
            traits::normalize(&mut selcx, param_env, cause, value);
        debug!(
            "partially_normalize_associated_types_in: result={:?} predicates={:?}",
            value, obligations
        );
        InferOk { value, obligations }
    }

    fn type_implements_trait(
        &self,
        trait_def_id: DefId,
        ty: Ty<'tcx>,
        params: SubstsRef<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    ) -> traits::EvaluationResult {
        debug!(
            "type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}",
            trait_def_id, ty, params, param_env
        );

        let trait_ref =
            ty::TraitRef { def_id: trait_def_id, substs: self.tcx.mk_substs_trait(ty, params) };

        let obligation = traits::Obligation {
            cause: traits::ObligationCause::dummy(),
            param_env,
            recursion_depth: 0,
            predicate: trait_ref.without_const().to_predicate(self.tcx),
        };
        self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr)
    }
}

pub trait InferCtxtBuilderExt<'tcx> {
    fn enter_canonical_trait_query<K, R>(
        &mut self,
        canonical_key: &Canonical<'tcx, K>,
        operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>,
    ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>>
    where
        K: TypeFoldable<'tcx>,
        R: Debug + TypeFoldable<'tcx>,
        Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>;
}

impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
    /// The "main method" for a canonicalized trait query. Given the
    /// canonical key `canonical_key`, this method will create a new
    /// inference context, instantiate the key, and run your operation
    /// `op`. The operation should yield up a result (of type `R`) as
    /// well as a set of trait obligations that must be fully
    /// satisfied. These obligations will be processed and the
    /// canonical result created.
    ///
    /// Returns `NoSolution` in the event of any error.
    ///
    /// (It might be mildly nicer to implement this on `TyCtxt`, and
    /// not `InferCtxtBuilder`, but that is a bit tricky right now.
    /// In part because we would need a `for<'tcx>` sort of
    /// bound for the closure and in part because it is convenient to
    /// have `'tcx` be free on this function so that we can talk about
    /// `K: TypeFoldable<'tcx>`.)
    fn enter_canonical_trait_query<K, R>(
        &mut self,
        canonical_key: &Canonical<'tcx, K>,
        operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>,
    ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>>
    where
        K: TypeFoldable<'tcx>,
        R: Debug + TypeFoldable<'tcx>,
        Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
    {
        self.enter_with_canonical(
            DUMMY_SP,
            canonical_key,
            |ref infcx, key, canonical_inference_vars| {
                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
                let value = operation(infcx, &mut *fulfill_cx, key)?;
                infcx.make_canonicalized_query_response(
                    canonical_inference_vars,
                    value,
                    &mut *fulfill_cx,
                )
            },
        )
    }
}

pub trait OutlivesEnvironmentExt<'tcx> {
    fn add_implied_bounds(
        &mut self,
        infcx: &InferCtxt<'a, 'tcx>,
        fn_sig_tys: &[Ty<'tcx>],
        body_id: hir::HirId,
        span: Span,
    );
}

impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> {
    /// This method adds "implied bounds" into the outlives environment.
    /// Implied bounds are outlives relationships that we can deduce
    /// on the basis that certain types must be well-formed -- these are
    /// either the types that appear in the function signature or else
    /// the input types to an impl. For example, if you have a function
    /// like
    ///
    /// ```
    /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { }
    /// ```
    ///
    /// we can assume in the caller's body that `'b: 'a` and that `T:
    /// 'b` (and hence, transitively, that `T: 'a`). This method would
    /// add those assumptions into the outlives-environment.
    ///
    /// Tests: `src/test/ui/regions/regions-free-region-ordering-*.rs`
    fn add_implied_bounds(
        &mut self,
        infcx: &InferCtxt<'a, 'tcx>,
        fn_sig_tys: &[Ty<'tcx>],
        body_id: hir::HirId,
        span: Span,
    ) {
        debug!("add_implied_bounds()");

        for &ty in fn_sig_tys {
            let ty = infcx.resolve_vars_if_possible(ty);
            debug!("add_implied_bounds: ty = {}", ty);
            let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span);
            self.add_outlives_bounds(Some(infcx), implied_bounds)
        }
    }
}
<|endoftext|>


<commit_before>//! A Rust interface for the functionality of the Objective-C runtime.
//!
//! For more information on foreign functions, see Apple's documentation:
//! https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html

use std::c_str::{CString, ToCStr};
use std::mem;
use std::ptr;
use std::raw;
use std::str::from_c_str;
use libc::{c_char, c_int, c_uint, c_void, ptrdiff_t, size_t};
use libc;

use {encode, Encode};

/// A type that represents a `malloc`'d chunk of memory.
pub struct MallocBuffer<T> {
    ptr: *mut T,
    len: uint,
}

impl<T> MallocBuffer<T> {
    /// Constructs a new `MallocBuffer` for a `malloc`'d buffer
    /// with the given length at the given pointer.
    /// Returns `None` if the given pointer is null.
    /// When this `MallocBuffer` drops, the buffer will be `free`'d.
    ///
    /// Unsafe because there must be `len` contiguous, valid instances of `T`
    /// at `ptr`, and `T` must not be a type that implements `Drop`
    /// (because the `MallocBuffer` makes no attempt to drop its elements,
    /// just the buffer containing them).
    pub unsafe fn new(ptr: *mut T, len: uint) -> Option<MallocBuffer<T>> {
        if ptr.is_null() {
            None
        } else {
            Some(MallocBuffer { ptr: ptr, len: len })
        }
    }

    /// Returns the number of items in self.
    pub fn len(&self) -> uint {
        self.len
    }
}

#[unsafe_destructor]
impl<T> Drop for MallocBuffer<T> {
    fn drop(&mut self) {
        unsafe {
            libc::free(self.ptr as *mut c_void);
        }
    }
}

impl<T> AsSlice<T> for MallocBuffer<T> {
    fn as_slice(&self) -> &[T] {
        let raw_slice = raw::Slice {
            data: self.ptr as *const T,
            len: self.len,
        };
        unsafe {
            mem::transmute(raw_slice)
        }
    }
}

/// A type that represents a method selector.
#[repr(C)]
pub struct Sel {
    ptr: *const c_void,
}

/// A type that represents an instance variable.
#[allow(missing_copy_implementations)]
pub enum Ivar { }

/// A type that represents a method in a class definition.
#[allow(missing_copy_implementations)]
pub enum Method { }

/// A type that represents an Objective-C class.
#[allow(missing_copy_implementations)]
pub enum Class { }

/// A type that represents an instance of a class.
#[allow(missing_copy_implementations)]
pub enum Object { }

/// A pointer to the start of a method implementation.
pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
    pub fn sel_registerName(name: *const c_char) -> Sel;
    pub fn sel_getName(sel: Sel) -> *const c_char;

    pub fn class_getName(cls: *const Class) -> *const c_char;
    pub fn class_getInstanceSize(cls: *const Class) -> size_t;
    pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
    pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
    pub fn class_copyMethodList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Method;
    pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
    pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
    pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: size_t, alignment: u8, types: *const c_char) -> bool;

    pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: size_t) -> *mut Class;
    pub fn objc_disposeClassPair(cls: *mut Class);
    pub fn objc_registerClassPair(cls: *mut Class);

    pub fn object_getClass(obj: *const Object) -> *const Class;

    pub fn objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int;
    pub fn objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class;
    pub fn objc_getClass(name: *const c_char) -> *const Class;

    pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
    pub fn ivar_getOffset(ivar: *const Ivar) -> ptrdiff_t;
    pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;

    pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;

    pub fn method_getName(method: *const Method) -> Sel;
    pub fn method_getImplementation(method: *const Method) -> Imp;
    pub fn method_getTypeEncoding(method: *const Method) -> *const c_char;
    pub fn method_copyReturnType(method: *const Method) -> *mut c_char;
    pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char;
    pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
    pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
    pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
}

impl Sel {
    /// Registers a method with the Objective-C runtime system,
    /// maps the method name to a selector, and returns the selector value.
    pub fn register(name: &str) -> Sel {
        name.with_c_str(|name| unsafe {
            sel_registerName(name)
        })
    }

    /// Returns the name of the method specified by self.
    pub fn name(&self) -> &str {
        unsafe {
            let name = sel_getName(*self);
            from_c_str(name)
        }
    }
}

impl PartialEq for Sel {
    fn eq(&self, other: &Sel) -> bool {
        self.ptr == other.ptr
    }
}

impl Eq for Sel { }

impl Copy for Sel { }

impl Clone for Sel {
    fn clone(&self) -> Sel { *self }
}

impl Ivar {
    /// Returns the name of self.
    pub fn name(&self) -> &str {
        unsafe {
            let name = ivar_getName(self);
            from_c_str(name)
        }
    }

    /// Returns the offset of self.
    pub fn offset(&self) -> int {
        let offset = unsafe {
            ivar_getOffset(self)
        };
        offset as int
    }

    /// Returns the type string of self.
    pub fn type_encoding(&self) -> &str {
        unsafe {
            let encoding = ivar_getTypeEncoding(self);
            from_c_str(encoding)
        }
    }
}

impl Method {
    /// Returns the name of self.
    pub fn name(&self) -> Sel {
        unsafe {
            method_getName(self)
        }
    }

    /// Returns a string describing self's parameter and return types.
    pub fn type_encoding(&self) -> &str {
        unsafe {
            let encoding = method_getTypeEncoding(self);
            from_c_str(encoding)
        }
    }

    /// Returns a string describing self's return type.
    pub fn return_type(&self) -> CString {
        unsafe {
            let encoding = method_copyReturnType(self);
            CString::new(encoding, true)
        }
    }

    /// Returns a string describing a single parameter type of self, or
    /// `None` if self has no parameter at the given index.
    pub fn argument_type(&self, index: uint) -> Option<CString> {
        unsafe {
            let encoding = method_copyArgumentType(self, index as c_uint);
            if encoding.is_null() {
                None
            } else {
                Some(CString::new(encoding, true))
            }
        }
    }

    /// Returns the number of arguments accepted by self.
    pub fn arguments_count(&self) -> uint {
        unsafe {
            method_getNumberOfArguments(self) as uint
        }
    }

    /// Returns the implementation of self.
    pub fn implementation(&self) -> Imp {
        unsafe {
            method_getImplementation(self)
        }
    }

    /// Sets the implementation of self.
    /// Unsafe because the caller must ensure the implementation has the
    /// correct self, return, and argument types for the method.
    pub unsafe fn set_implementation(&mut self, imp: Imp) -> Imp {
        method_setImplementation(self, imp)
    }

    /// Exchanges the implementations of self with another `Method`.
    /// Unsafe because the caller must ensure the two methods have the same
    /// self, return, and argument types.
    pub unsafe fn exchange_implementation(&mut self, other: &mut Method) {
        method_exchangeImplementations(self, other);
    }
}

impl Class {
    /// Returns the class definition of a specified class, or `None` if the
    /// class is not registered with the Objective-C runtime.
    pub fn get(name: &str) -> Option<&'static Class> {
        name.with_c_str(|name| unsafe {
            objc_getClass(name).as_ref()
        })
    }

    /// Obtains the list of registered class definitions.
    pub fn classes() -> MallocBuffer<&'static Class> {
        unsafe {
            let mut count: c_uint = 0;
            let classes = objc_copyClassList(&mut count);
            MallocBuffer::new(classes as *mut _, count as uint).unwrap()
        }
    }

    /// Returns the total number of registered classes.
    pub fn classes_count() -> uint {
        unsafe {
            objc_getClassList(ptr::null_mut(), 0) as uint
        }
    }

    /// Returns the name of self.
    pub fn name(&self) -> &str {
        unsafe {
            let name = class_getName(self);
            from_c_str(name)
        }
    }

    /// Returns the size of instances of self.
    pub fn instance_size(&self) -> uint {
        unsafe {
            class_getInstanceSize(self) as uint
        }
    }

    /// Returns a specified instance method for self, or `None` if self and
    /// its superclasses do not contain an instance method with the
    /// specified selector.
    pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
        unsafe {
            class_getInstanceMethod(self, sel).as_ref()
        }
    }

    /// Returns the ivar for a specified instance variable of self, or `None`
    /// if self has no ivar with the given name.
    pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
        name.with_c_str(|name| unsafe {
            class_getInstanceVariable(self, name).as_ref()
        })
    }

    /// Describes the instance methods implemented by self.
    pub fn instance_methods(&self) -> MallocBuffer<&Method> {
        unsafe {
            let mut count: c_uint = 0;
            let methods = class_copyMethodList(self, &mut count);
            MallocBuffer::new(methods as *mut _, count as uint).unwrap()
        }

    }

    /// Describes the instance variables declared by self.
    pub fn instance_variables(&self) -> MallocBuffer<&Ivar> {
        unsafe {
            let mut count: c_uint = 0;
            let ivars = class_copyIvarList(self, &mut count);
            MallocBuffer::new(ivars as *mut _, count as uint).unwrap()
        }
    }
}

impl PartialEq for Class {
    fn eq(&self, other: &Class) -> bool {
        self as *const Class == other as *const Class
    }
}

impl Eq for Class { }

impl Object {
    /// Returns the class of self.
    pub fn class(&self) -> &'static Class {
        unsafe {
            &*object_getClass(self)
        }
    }

    /// Returns a reference to the ivar of self with the given name.
    /// Panics if self has no ivar with the given name.
    /// Unsafe because the caller must ensure that the ivar is actually
    /// of type `T`.
    pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
        let cls = self.class();
        let ptr = match cls.instance_variable(name) {
            Some(ivar) => {
                assert!(ivar.type_encoding() == encode::<T>());
                let offset = ivar.offset();
                let self_ptr = self as *const Object;
                (self_ptr as *const u8).offset(offset) as *const T
            }
            None => panic!("Ivar {} not found on class {}", name, cls.name()),
        };
        &*ptr
    }

    /// Returns a mutable reference to the ivar of self with the given name.
    /// Panics if self has no ivar with the given name.
    /// Unsafe because the caller must ensure that the ivar is actually
    /// of type `T`.
    pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
        let cls = self.class();
        let ptr = match cls.instance_variable(name) {
            Some(ivar) => {
                assert!(ivar.type_encoding() == encode::<T>());
                let offset = ivar.offset();
                let self_ptr = self as *mut Object;
                (self_ptr as *mut u8).offset(offset) as *mut T
            }
            None => panic!("Ivar {} not found on class {}", name, cls.name()),
        };
        &mut *ptr
    }

    /// Sets the value of the ivar of self with the given name.
    /// Panics if self has no ivar with the given name.
    /// Unsafe because the caller must ensure that the ivar is actually
    /// of type `T`.
    pub unsafe fn set_ivar<T: Encode>(&mut self, name: &str, value: T) {
        *self.get_mut_ivar::<T>(name) = value;
    }
}

#[cfg(test)]
mod tests {
    use std::mem;
    use super::{Class, Sel};

    #[test]
    fn test_ivar() {
        let cls = Class::get("NSObject").unwrap();
        let ivar = cls.instance_variable("isa").unwrap();
        assert!(ivar.name() == "isa");
        assert!(ivar.type_encoding() == "#");
        assert!(ivar.offset() == 0);

        let ivars = cls.instance_variables();
        assert!(ivars.len() > 0);
    }

    #[test]
    fn test_method() {
        let cls = Class::get("NSObject").unwrap();
        let sel = Sel::register("description");
        let method = cls.instance_method(sel).unwrap();
        assert!(method.name().name() == "description");
        assert!(method.type_encoding() != "");
        assert!(method.arguments_count() == 2);
        assert!(method.return_type().as_bytes_no_nul() == "@".as_bytes());
        assert!(method.argument_type(1).unwrap().as_bytes_no_nul() ==
            ":".as_bytes());

        let methods = cls.instance_methods();
        assert!(methods.len() > 0);
    }

    #[test]
    fn test_class() {
        let cls = Class::get("NSObject").unwrap();
        assert!(cls.name() == "NSObject");
        assert!(cls.instance_size() == mem::size_of::<*const Class>());
    }

    #[test]
    fn test_classes() {
        let classes_count = Class::classes_count();
        assert!(classes_count > 0);

        let classes = Class::classes();
        assert!(classes.len() == classes_count);
    }

    #[test]
    fn test_object() {
        let cls = Class::get("NSObject").unwrap();
        let obj = unsafe {
            let obj = msg_send![cls alloc];
            let obj = msg_send![obj init];
            &*obj
        };
        assert!(obj.class() == cls);
        unsafe {
            msg_send![obj release];
        }
    }
}
<commit_msg>Removed unnecessary len method from MallocBuffer.<commit_after>//! A Rust interface for the functionality of the Objective-C runtime.
//!
//! For more information on foreign functions, see Apple's documentation:
//! https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html

use std::c_str::{CString, ToCStr};
use std::mem;
use std::ptr;
use std::raw;
use std::str::from_c_str;
use libc::{c_char, c_int, c_uint, c_void, ptrdiff_t, size_t};
use libc;

use {encode, Encode};

/// A type that represents a `malloc`'d chunk of memory.
pub struct MallocBuffer<T> {
    ptr: *mut T,
    len: uint,
}

impl<T> MallocBuffer<T> {
    /// Constructs a new `MallocBuffer` for a `malloc`'d buffer
    /// with the given length at the given pointer.
    /// Returns `None` if the given pointer is null.
    /// When this `MallocBuffer` drops, the buffer will be `free`'d.
    ///
    /// Unsafe because there must be `len` contiguous, valid instances of `T`
    /// at `ptr`, and `T` must not be a type that implements `Drop`
    /// (because the `MallocBuffer` makes no attempt to drop its elements,
    /// just the buffer containing them).
    pub unsafe fn new(ptr: *mut T, len: uint) -> Option<MallocBuffer<T>> {
        if ptr.is_null() {
            None
        } else {
            Some(MallocBuffer { ptr: ptr, len: len })
        }
    }
}

#[unsafe_destructor]
impl<T> Drop for MallocBuffer<T> {
    fn drop(&mut self) {
        unsafe {
            libc::free(self.ptr as *mut c_void);
        }
    }
}

impl<T> AsSlice<T> for MallocBuffer<T> {
    fn as_slice(&self) -> &[T] {
        let raw_slice = raw::Slice {
            data: self.ptr as *const T,
            len: self.len,
        };
        unsafe {
            mem::transmute(raw_slice)
        }
    }
}

/// A type that represents a method selector.
#[repr(C)]
pub struct Sel {
    ptr: *const c_void,
}

/// A type that represents an instance variable.
#[allow(missing_copy_implementations)]
pub enum Ivar { }

/// A type that represents a method in a class definition.
#[allow(missing_copy_implementations)]
pub enum Method { }

/// A type that represents an Objective-C class.
#[allow(missing_copy_implementations)]
pub enum Class { }

/// A type that represents an instance of a class.
#[allow(missing_copy_implementations)]
pub enum Object { }

/// A pointer to the start of a method implementation.
pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
    pub fn sel_registerName(name: *const c_char) -> Sel;
    pub fn sel_getName(sel: Sel) -> *const c_char;

    pub fn class_getName(cls: *const Class) -> *const c_char;
    pub fn class_getInstanceSize(cls: *const Class) -> size_t;
    pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
    pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
    pub fn class_copyMethodList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Method;
    pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
    pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
    pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: size_t, alignment: u8, types: *const c_char) -> bool;

    pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: size_t) -> *mut Class;
    pub fn objc_disposeClassPair(cls: *mut Class);
    pub fn objc_registerClassPair(cls: *mut Class);

    pub fn object_getClass(obj: *const Object) -> *const Class;

    pub fn objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int;
    pub fn objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class;
    pub fn objc_getClass(name: *const c_char) -> *const Class;

    pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
    pub fn ivar_getOffset(ivar: *const Ivar) -> ptrdiff_t;
    pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;

    pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;

    pub fn method_getName(method: *const Method) -> Sel;
    pub fn method_getImplementation(method: *const Method) -> Imp;
    pub fn method_getTypeEncoding(method: *const Method) -> *const c_char;
    pub fn method_copyReturnType(method: *const Method) -> *mut c_char;
    pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char;
    pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
    pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
    pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
}

impl Sel {
    /// Registers a method with the Objective-C runtime system,
    /// maps the method name to a selector, and returns the selector value.
    pub fn register(name: &str) -> Sel {
        name.with_c_str(|name| unsafe {
            sel_registerName(name)
        })
    }

    /// Returns the name of the method specified by self.
    pub fn name(&self) -> &str {
        unsafe {
            let name = sel_getName(*self);
            from_c_str(name)
        }
    }
}

impl PartialEq for Sel {
    fn eq(&self, other: &Sel) -> bool {
        self.ptr == other.ptr
    }
}

impl Eq for Sel { }

impl Copy for Sel { }

impl Clone for Sel {
    fn clone(&self) -> Sel { *self }
}

impl Ivar {
    /// Returns the name of self.
    pub fn name(&self) -> &str {
        unsafe {
            let name = ivar_getName(self);
            from_c_str(name)
        }
    }

    /// Returns the offset of self.
    pub fn offset(&self) -> int {
        let offset = unsafe {
            ivar_getOffset(self)
        };
        offset as int
    }

    /// Returns the type string of self.
    pub fn type_encoding(&self) -> &str {
        unsafe {
            let encoding = ivar_getTypeEncoding(self);
            from_c_str(encoding)
        }
    }
}

impl Method {
    /// Returns the name of self.
    pub fn name(&self) -> Sel {
        unsafe {
            method_getName(self)
        }
    }

    /// Returns a string describing self's parameter and return types.
    pub fn type_encoding(&self) -> &str {
        unsafe {
            let encoding = method_getTypeEncoding(self);
            from_c_str(encoding)
        }
    }

    /// Returns a string describing self's return type.
    pub fn return_type(&self) -> CString {
        unsafe {
            let encoding = method_copyReturnType(self);
            CString::new(encoding, true)
        }
    }

    /// Returns a string describing a single parameter type of self, or
    /// `None` if self has no parameter at the given index.
    pub fn argument_type(&self, index: uint) -> Option<CString> {
        unsafe {
            let encoding = method_copyArgumentType(self, index as c_uint);
            if encoding.is_null() {
                None
            } else {
                Some(CString::new(encoding, true))
            }
        }
    }

    /// Returns the number of arguments accepted by self.
    pub fn arguments_count(&self) -> uint {
        unsafe {
            method_getNumberOfArguments(self) as uint
        }
    }

    /// Returns the implementation of self.
    pub fn implementation(&self) -> Imp {
        unsafe {
            method_getImplementation(self)
        }
    }

    /// Sets the implementation of self.
    /// Unsafe because the caller must ensure the implementation has the
    /// correct self, return, and argument types for the method.
    pub unsafe fn set_implementation(&mut self, imp: Imp) -> Imp {
        method_setImplementation(self, imp)
    }

    /// Exchanges the implementations of self with another `Method`.
    /// Unsafe because the caller must ensure the two methods have the same
    /// self, return, and argument types.
    pub unsafe fn exchange_implementation(&mut self, other: &mut Method) {
        method_exchangeImplementations(self, other);
    }
}

impl Class {
    /// Returns the class definition of a specified class, or `None` if the
    /// class is not registered with the Objective-C runtime.
    pub fn get(name: &str) -> Option<&'static Class> {
        name.with_c_str(|name| unsafe {
            objc_getClass(name).as_ref()
        })
    }

    /// Obtains the list of registered class definitions.
    pub fn classes() -> MallocBuffer<&'static Class> {
        unsafe {
            let mut count: c_uint = 0;
            let classes = objc_copyClassList(&mut count);
            MallocBuffer::new(classes as *mut _, count as uint).unwrap()
        }
    }

    /// Returns the total number of registered classes.
    pub fn classes_count() -> uint {
        unsafe {
            objc_getClassList(ptr::null_mut(), 0) as uint
        }
    }

    /// Returns the name of self.
    pub fn name(&self) -> &str {
        unsafe {
            let name = class_getName(self);
            from_c_str(name)
        }
    }

    /// Returns the size of instances of self.
    pub fn instance_size(&self) -> uint {
        unsafe {
            class_getInstanceSize(self) as uint
        }
    }

    /// Returns a specified instance method for self, or `None` if self and
    /// its superclasses do not contain an instance method with the
    /// specified selector.
    pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
        unsafe {
            class_getInstanceMethod(self, sel).as_ref()
        }
    }

    /// Returns the ivar for a specified instance variable of self, or `None`
    /// if self has no ivar with the given name.
    pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
        name.with_c_str(|name| unsafe {
            class_getInstanceVariable(self, name).as_ref()
        })
    }

    /// Describes the instance methods implemented by self.
    pub fn instance_methods(&self) -> MallocBuffer<&Method> {
        unsafe {
            let mut count: c_uint = 0;
            let methods = class_copyMethodList(self, &mut count);
            MallocBuffer::new(methods as *mut _, count as uint).unwrap()
        }

    }

    /// Describes the instance variables declared by self.
    pub fn instance_variables(&self) -> MallocBuffer<&Ivar> {
        unsafe {
            let mut count: c_uint = 0;
            let ivars = class_copyIvarList(self, &mut count);
            MallocBuffer::new(ivars as *mut _, count as uint).unwrap()
        }
    }
}

impl PartialEq for Class {
    fn eq(&self, other: &Class) -> bool {
        self as *const Class == other as *const Class
    }
}

impl Eq for Class { }

impl Object {
    /// Returns the class of self.
    pub fn class(&self) -> &'static Class {
        unsafe {
            &*object_getClass(self)
        }
    }

    /// Returns a reference to the ivar of self with the given name.
    /// Panics if self has no ivar with the given name.
    /// Unsafe because the caller must ensure that the ivar is actually
    /// of type `T`.
    pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
        let cls = self.class();
        let ptr = match cls.instance_variable(name) {
            Some(ivar) => {
                assert!(ivar.type_encoding() == encode::<T>());
                let offset = ivar.offset();
                let self_ptr = self as *const Object;
                (self_ptr as *const u8).offset(offset) as *const T
            }
            None => panic!("Ivar {} not found on class {}", name, cls.name()),
        };
        &*ptr
    }

    /// Returns a mutable reference to the ivar of self with the given name.
    /// Panics if self has no ivar with the given name.
    /// Unsafe because the caller must ensure that the ivar is actually
    /// of type `T`.
    pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
        let cls = self.class();
        let ptr = match cls.instance_variable(name) {
            Some(ivar) => {
                assert!(ivar.type_encoding() == encode::<T>());
                let offset = ivar.offset();
                let self_ptr = self as *mut Object;
                (self_ptr as *mut u8).offset(offset) as *mut T
            }
            None => panic!("Ivar {} not found on class {}", name, cls.name()),
        };
        &mut *ptr
    }

    /// Sets the value of the ivar of self with the given name.
    /// Panics if self has no ivar with the given name.
    /// Unsafe because the caller must ensure that the ivar is actually
    /// of type `T`.
    pub unsafe fn set_ivar<T: Encode>(&mut self, name: &str, value: T) {
        *self.get_mut_ivar::<T>(name) = value;
    }
}

#[cfg(test)]
mod tests {
    use std::mem;
    use super::{Class, Sel};

    #[test]
    fn test_ivar() {
        let cls = Class::get("NSObject").unwrap();
        let ivar = cls.instance_variable("isa").unwrap();
        assert!(ivar.name() == "isa");
        assert!(ivar.type_encoding() == "#");
        assert!(ivar.offset() == 0);

        let ivars = cls.instance_variables();
        assert!(ivars.as_slice().len() > 0);
    }

    #[test]
    fn test_method() {
        let cls = Class::get("NSObject").unwrap();
        let sel = Sel::register("description");
        let method = cls.instance_method(sel).unwrap();
        assert!(method.name().name() == "description");
        assert!(method.type_encoding() != "");
        assert!(method.arguments_count() == 2);
        assert!(method.return_type().as_bytes_no_nul() == "@".as_bytes());
        assert!(method.argument_type(1).unwrap().as_bytes_no_nul() ==
            ":".as_bytes());

        let methods = cls.instance_methods();
        assert!(methods.as_slice().len() > 0);
    }

    #[test]
    fn test_class() {
        let cls = Class::get("NSObject").unwrap();
        assert!(cls.name() == "NSObject");
        assert!(cls.instance_size() == mem::size_of::<*const Class>());
    }

    #[test]
    fn test_classes() {
        let classes_count = Class::classes_count();
        assert!(classes_count > 0);

        let classes = Class::classes();
        assert!(classes.as_slice().len() == classes_count);
    }

    #[test]
    fn test_object() {
        let cls = Class::get("NSObject").unwrap();
        let obj = unsafe {
            let obj = msg_send![cls alloc];
            let obj = msg_send![obj init];
            &*obj
        };
        assert!(obj.class() == cls);
        unsafe {
            msg_send![obj release];
        }
    }
}
<|endoftext|>


<commit_before>#![crate_name = "mnemosyne"]
#![crate_type = "lib"]
#![feature(rustc_private)]
extern crate rustc;
<commit_msg>Add combine dependency.<commit_after>#![crate_name = "mnemosyne"]
#![crate_type = "lib"]
#![feature(rustc_private)]

extern crate rustc;
extern crate combine;
<|endoftext|>