<commit_before>import std::io;
import std::str;
import std::option;
import std::fs;
import std::os;
import std::vec;
import std::test;

import common::mode_run_pass;
import common::mode_run_fail;
import common::mode_compile_fail;
import common::mode_pretty;
import common::cx;
import common::config;
import header::load_props;
import header::test_props;
import util::logv;

export run;

fn run(cx: &cx, _testfile: -[u8]) {
    let testfile = str::unsafe_from_bytes(_testfile);
    if cx.config.verbose {
        // We're going to be dumping a lot of info. Start on a new line.
        io::stdout().write_str("\n\n");
    }
    log #fmt["running %s", testfile];
    let props = load_props(testfile);
    alt cx.config.mode {
      mode_compile_fail. { run_cfail_test(cx, props, testfile); }
      mode_run_fail. { run_rfail_test(cx, props, testfile); }
      mode_run_pass. { run_rpass_test(cx, props, testfile); }
      mode_pretty. { run_pretty_test(cx, props, testfile); }
    }
}

fn run_cfail_test(cx: &cx, props: &test_props, testfile: &str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status == 0 {
        fatal_procres("compile-fail test compiled successfully!", procres);
    }

    check_error_patterns(props, testfile, procres);
}

fn run_rfail_test(cx: &cx, props: &test_props, testfile: &str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("compilation failed!", procres); }

    procres = exec_compiled_test(cx, props, testfile);

    if procres.status == 0 {
        fatal_procres("run-fail test didn't produce an error!", procres);
    }

    // This is the value valgrind returns on failure
    // FIXME: Why is this value neither the value we pass to
    // valgrind as --error-exitcode (1), nor the value we see as the
    // exit code on the command-line (137)?
    const valgrind_err: int = 9;
    if procres.status == valgrind_err {
        fatal_procres("run-fail test isn't valgrind-clean!", procres);
    }

    check_error_patterns(props, testfile, procres);
}

fn run_rpass_test(cx: &cx, props: &test_props, testfile: &str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("compilation failed!", procres); }

    procres = exec_compiled_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("test run failed!", procres); }
}

fn run_pretty_test(cx: &cx, props: &test_props, testfile: &str) {
    if option::is_some(props.pp_exact) {
        logv(cx.config, "testing for exact pretty-printing");
    } else { logv(cx.config, "testing for converging pretty-printing"); }

    let rounds =
        alt props.pp_exact { option::some(_) { 1 } option::none. { 2 } };

    let srcs = [io::read_whole_file_str(testfile)];

    let round = 0;
    while round < rounds {
        logv(cx.config, #fmt["pretty-printing round %d", round]);
        let procres = print_source(cx, testfile, srcs[round]);

        if procres.status != 0 {
            fatal_procres(#fmt["pretty-printing failed in round %d", round],
                          procres);
        }

        srcs += [procres.stdout];
        round += 1;
    }

    let expected =
        alt props.pp_exact {
          option::some(file) {
            let filepath = fs::connect(fs::dirname(testfile), file);
            io::read_whole_file_str(filepath)
          }
          option::none. { srcs[vec::len(srcs) - 2u] }
        };
    let actual = srcs[vec::len(srcs) - 1u];

    if option::is_some(props.pp_exact) {
        // Now we have to care about line endings
        let cr = "\r";
        check (str::is_not_empty(cr));
        actual = str::replace(actual, cr, "");
        expected = str::replace(expected, cr, "");
    }

    compare_source(expected, actual);

    // Finally, let's make sure it actually appears to remain valid code
    let procres = typecheck_source(cx, testfile, actual);

    if procres.status != 0 {
        fatal_procres("pretty-printed source does not typecheck", procres);
    }

    ret;

    fn print_source(cx: &cx, testfile: &str, src: &str) -> procres {
        compose_and_run(cx, testfile, make_pp_args,
                        cx.config.compile_lib_path, option::some(src))
    }

    fn make_pp_args(config: &config, testfile: &str) -> procargs {
        let prog = config.rustc_path;
        let args = ["-", "--pretty", "normal"];
        ret {prog: prog, args: args};
    }

    fn compare_source(expected: &str, actual: &str) {
        if expected != actual {
            error("pretty-printed source does match expected source");
            let msg =
                #fmt["\n\
expected:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
actual:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
\n",
                     expected, actual];
            io::stdout().write_str(msg);
            fail;
        }
    }

    fn typecheck_source(cx: &cx, testfile: &str, src: &str) -> procres {
        compose_and_run(cx, testfile, make_typecheck_args,
                        cx.config.compile_lib_path, option::some(src))
    }

    fn make_typecheck_args(config: &config, testfile: &str) -> procargs {
        let prog = config.rustc_path;
        let args = ["-", "--no-trans", "--lib"];
        ret {prog: prog, args: args};
    }
}

fn check_error_patterns(props: &test_props, testfile: &str,
                        procres: &procres) {
    if vec::is_empty(props.error_patterns) {
        fatal("no error pattern specified in " + testfile);
    }

    let next_err_idx = 0u;
    let next_err_pat = props.error_patterns[next_err_idx];
    for line: str in str::split(procres.stdout, '\n' as u8) {
        if str::find(line, next_err_pat) > 0 {
            log #fmt["found error pattern %s", next_err_pat];
            next_err_idx += 1u;
            if next_err_idx == vec::len(props.error_patterns) {
                log "found all error patterns";
                ret;
            }
            next_err_pat = props.error_patterns[next_err_idx];
        }
    }

    let missing_patterns =
        vec::slice(props.error_patterns, next_err_idx,
                   vec::len(props.error_patterns));
    if vec::len(missing_patterns) == 1u {
        fatal_procres(#fmt["error pattern '%s' not found!",
                           missing_patterns[0]], procres);
    } else {
        for pattern: str in missing_patterns {
            error(#fmt["error pattern '%s' not found!", pattern]);
        }
        fatal_procres("multiple error patterns not found", procres);
    }
}

type procargs = {prog: str, args: [str]};

type procres = {status: int, stdout: str, stderr: str, cmdline: str};

fn compile_test(cx: &cx, props: &test_props, testfile: &str) -> procres {
    compose_and_run(cx, testfile, bind make_compile_args(_, props, _),
                    cx.config.compile_lib_path, option::none)
}

fn exec_compiled_test(cx: &cx, props: &test_props, testfile: &str) ->
   procres {
    compose_and_run(cx, testfile, bind make_run_args(_, props, _),
                    cx.config.run_lib_path, option::none)
}

fn compose_and_run(cx: &cx, testfile: &str,
                   make_args: fn(&config, &str) -> procargs, lib_path: &str,
                   input: option::t<str>) -> procres {
    let procargs = make_args(cx.config, testfile);
    ret program_output(cx, testfile, lib_path, procargs.prog, procargs.args,
                       input);
}

fn make_compile_args(config: &config, props: &test_props, testfile: &str) ->
   procargs {
    let prog = config.rustc_path;
    let args = [testfile, "-o", make_exe_name(config, testfile)];
    args += split_maybe_args(config.rustcflags);
    args += split_maybe_args(props.compile_flags);
    ret {prog: prog, args: args};
}

fn make_exe_name(config: &config, testfile: &str) -> str {
    output_base_name(config, testfile) + os::exec_suffix()
}

fn make_run_args(config: &config, props: &test_props, testfile: &str) ->
   procargs {
    let toolargs =
        if !props.no_valgrind {

            // If we've got another tool to run under (valgrind),
            // then split apart its command
            split_maybe_args(config.runtool)
        } else { [] };

    let args = toolargs + [make_exe_name(config, testfile)];
    ret {prog: args[0], args: vec::slice(args, 1u, vec::len(args))};
}

fn split_maybe_args(argstr: &option::t<str>) -> [str] {
    fn rm_whitespace(v: &[str]) -> [str] {
        fn flt(s: &str) -> option::t<str> {
            if !is_whitespace(s) { option::some(s) } else { option::none }
        }

        // FIXME: This should be in std
        fn is_whitespace(s: str) -> bool {
            for c: u8 in s { if c != ' ' as u8 { ret false; } }
            ret true;
        }
        vec::filter_map(flt, v)
    }

    alt argstr {
      option::some(s) { rm_whitespace(str::split(s, ' ' as u8)) }
      option::none. { [] }
    }
}

fn program_output(cx: &cx, testfile: &str, lib_path: &str, prog: &str,
                  args: &[str], input: option::t<str>) -> procres {
    let cmdline =
        {
            let cmdline = make_cmdline(lib_path, prog, args);
            logv(cx.config, #fmt["executing %s", cmdline]);
            cmdline
        };
    let res = procsrv::run(cx.procsrv, lib_path, prog, args, input);
    dump_output(cx.config, testfile, res.out, res.err);
    ret {status: res.status,
         stdout: res.out,
         stderr: res.err,
         cmdline: cmdline};
}

fn make_cmdline(libpath: &str, prog: &str, args: &[str]) -> str {
    #fmt["%s %s %s", lib_path_cmd_prefix(libpath), prog,
         str::connect(args, " ")]
}

// Build the LD_LIBRARY_PATH variable as it would be seen on the command line
// for diagnostic purposes
fn lib_path_cmd_prefix(path: &str) -> str {
    #fmt["%s=\"%s\"", util::lib_path_env_var(), util::make_new_path(path)]
}

fn dump_output(config: &config, testfile: &str, out: &str, err: &str) {
    dump_output_file(config, testfile, out, "out");
    dump_output_file(config, testfile, err, "err");
    maybe_dump_to_stdout(config, out, err);
}

#[cfg(target_os = "win32")]
#[cfg(target_os = "linux")]
fn dump_output_file(config: &config, testfile: &str, out: &str,
                    extension: &str) {
    let outfile = make_out_name(config, testfile, extension);
    let writer = io::file_writer(outfile, [io::create, io::truncate]);
    writer.write_str(out);
}

// FIXME (726): Can't use file_writer on mac
#[cfg(target_os = "macos")]
fn dump_output_file(config: &config, testfile: &str, out: &str,
                    extension: &str) {
}

fn make_out_name(config: &config, testfile: &str, extension: &str) -> str {
    output_base_name(config, testfile) + "." + extension
}

fn output_base_name(config: &config, testfile: &str) -> str {
    let base = config.build_base;
    let filename =
        {
            let parts = str::split(fs::basename(testfile), '.' as u8);
            parts = vec::slice(parts, 0u, vec::len(parts) - 1u);
            str::connect(parts, ".")
        };
    #fmt["%s%s.%s", base, filename, config.stage_id]
}

fn maybe_dump_to_stdout(config: &config, out: &str, err: &str) {
    if config.verbose {
        let sep1 = #fmt["------%s------------------------------", "stdout"];
        let sep2 = #fmt["------%s------------------------------", "stderr"];
        let sep3 = "------------------------------------------";
        io::stdout().write_line(sep1);
        io::stdout().write_line(out);
        io::stdout().write_line(sep2);
        io::stdout().write_line(err);
        io::stdout().write_line(sep3);
    }
}

fn error(err: &str) { io::stdout().write_line(#fmt["\nerror: %s", err]); }

fn fatal(err: &str) -> ! { error(err); fail; }

fn fatal_procres(err: &str, procres: procres) -> ! {
    let msg =
        #fmt["\n\
error: %s\n\
command: %s\n\
stdout:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
stderr:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
\n",
             err, procres.cmdline, procres.stdout, procres.stderr];
    io::stdout().write_str(msg);
    fail;
}
<commit_msg>Silence unused variable warnings in compiletest<commit_after>import std::io;
import std::str;
import std::option;
import std::fs;
import std::os;
import std::vec;
import std::test;

import common::mode_run_pass;
import common::mode_run_fail;
import common::mode_compile_fail;
import common::mode_pretty;
import common::cx;
import common::config;
import header::load_props;
import header::test_props;
import util::logv;

export run;

fn run(cx: &cx, _testfile: -[u8]) {
    let testfile = str::unsafe_from_bytes(_testfile);
    if cx.config.verbose {
        // We're going to be dumping a lot of info. Start on a new line.
        io::stdout().write_str("\n\n");
    }
    log #fmt["running %s", testfile];
    let props = load_props(testfile);
    alt cx.config.mode {
      mode_compile_fail. { run_cfail_test(cx, props, testfile); }
      mode_run_fail. { run_rfail_test(cx, props, testfile); }
      mode_run_pass. { run_rpass_test(cx, props, testfile); }
      mode_pretty. { run_pretty_test(cx, props, testfile); }
    }
}

fn run_cfail_test(cx: &cx, props: &test_props, testfile: &str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status == 0 {
        fatal_procres("compile-fail test compiled successfully!", procres);
    }

    check_error_patterns(props, testfile, procres);
}

fn run_rfail_test(cx: &cx, props: &test_props, testfile: &str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("compilation failed!", procres); }

    procres = exec_compiled_test(cx, props, testfile);

    if procres.status == 0 {
        fatal_procres("run-fail test didn't produce an error!", procres);
    }

    // This is the value valgrind returns on failure
    // FIXME: Why is this value neither the value we pass to
    // valgrind as --error-exitcode (1), nor the value we see as the
    // exit code on the command-line (137)?
    const valgrind_err: int = 9;
    if procres.status == valgrind_err {
        fatal_procres("run-fail test isn't valgrind-clean!", procres);
    }

    check_error_patterns(props, testfile, procres);
}

fn run_rpass_test(cx: &cx, props: &test_props, testfile: &str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("compilation failed!", procres); }

    procres = exec_compiled_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("test run failed!", procres); }
}

fn run_pretty_test(cx: &cx, props: &test_props, testfile: &str) {
    if option::is_some(props.pp_exact) {
        logv(cx.config, "testing for exact pretty-printing");
    } else { logv(cx.config, "testing for converging pretty-printing"); }

    let rounds =
        alt props.pp_exact { option::some(_) { 1 } option::none. { 2 } };

    let srcs = [io::read_whole_file_str(testfile)];

    let round = 0;
    while round < rounds {
        logv(cx.config, #fmt["pretty-printing round %d", round]);
        let procres = print_source(cx, testfile, srcs[round]);

        if procres.status != 0 {
            fatal_procres(#fmt["pretty-printing failed in round %d", round],
                          procres);
        }

        srcs += [procres.stdout];
        round += 1;
    }

    let expected =
        alt props.pp_exact {
          option::some(file) {
            let filepath = fs::connect(fs::dirname(testfile), file);
            io::read_whole_file_str(filepath)
          }
          option::none. { srcs[vec::len(srcs) - 2u] }
        };
    let actual = srcs[vec::len(srcs) - 1u];

    if option::is_some(props.pp_exact) {
        // Now we have to care about line endings
        let cr = "\r";
        check (str::is_not_empty(cr));
        actual = str::replace(actual, cr, "");
        expected = str::replace(expected, cr, "");
    }

    compare_source(expected, actual);

    // Finally, let's make sure it actually appears to remain valid code
    let procres = typecheck_source(cx, testfile, actual);

    if procres.status != 0 {
        fatal_procres("pretty-printed source does not typecheck", procres);
    }

    ret;

    fn print_source(cx: &cx, testfile: &str, src: &str) -> procres {
        compose_and_run(cx, testfile, make_pp_args,
                        cx.config.compile_lib_path, option::some(src))
    }

    fn make_pp_args(config: &config, _testfile: &str) -> procargs {
        let prog = config.rustc_path;
        let args = ["-", "--pretty", "normal"];
        ret {prog: prog, args: args};
    }

    fn compare_source(expected: &str, actual: &str) {
        if expected != actual {
            error("pretty-printed source does match expected source");
            let msg =
                #fmt["\n\
expected:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
actual:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
\n",
                     expected, actual];
            io::stdout().write_str(msg);
            fail;
        }
    }

    fn typecheck_source(cx: &cx, testfile: &str, src: &str) -> procres {
        compose_and_run(cx, testfile, make_typecheck_args,
                        cx.config.compile_lib_path, option::some(src))
    }

    fn make_typecheck_args(config: &config, _testfile: &str) -> procargs {
        let prog = config.rustc_path;
        let args = ["-", "--no-trans", "--lib"];
        ret {prog: prog, args: args};
    }
}

fn check_error_patterns(props: &test_props, testfile: &str,
                        procres: &procres) {
    if vec::is_empty(props.error_patterns) {
        fatal("no error pattern specified in " + testfile);
    }

    let next_err_idx = 0u;
    let next_err_pat = props.error_patterns[next_err_idx];
    for line: str in str::split(procres.stdout, '\n' as u8) {
        if str::find(line, next_err_pat) > 0 {
            log #fmt["found error pattern %s", next_err_pat];
            next_err_idx += 1u;
            if next_err_idx == vec::len(props.error_patterns) {
                log "found all error patterns";
                ret;
            }
            next_err_pat = props.error_patterns[next_err_idx];
        }
    }

    let missing_patterns =
        vec::slice(props.error_patterns, next_err_idx,
                   vec::len(props.error_patterns));
    if vec::len(missing_patterns) == 1u {
        fatal_procres(#fmt["error pattern '%s' not found!",
                           missing_patterns[0]], procres);
    } else {
        for pattern: str in missing_patterns {
            error(#fmt["error pattern '%s' not found!", pattern]);
        }
        fatal_procres("multiple error patterns not found", procres);
    }
}

type procargs = {prog: str, args: [str]};

type procres = {status: int, stdout: str, stderr: str, cmdline: str};

fn compile_test(cx: &cx, props: &test_props, testfile: &str) -> procres {
    compose_and_run(cx, testfile, bind make_compile_args(_, props, _),
                    cx.config.compile_lib_path, option::none)
}

fn exec_compiled_test(cx: &cx, props: &test_props, testfile: &str) ->
   procres {
    compose_and_run(cx, testfile, bind make_run_args(_, props, _),
                    cx.config.run_lib_path, option::none)
}

fn compose_and_run(cx: &cx, testfile: &str,
                   make_args: fn(&config, &str) -> procargs, lib_path: &str,
                   input: option::t<str>) -> procres {
    let procargs = make_args(cx.config, testfile);
    ret program_output(cx, testfile, lib_path, procargs.prog, procargs.args,
                       input);
}

fn make_compile_args(config: &config, props: &test_props, testfile: &str) ->
   procargs {
    let prog = config.rustc_path;
    let args = [testfile, "-o", make_exe_name(config, testfile)];
    args += split_maybe_args(config.rustcflags);
    args += split_maybe_args(props.compile_flags);
    ret {prog: prog, args: args};
}

fn make_exe_name(config: &config, testfile: &str) -> str {
    output_base_name(config, testfile) + os::exec_suffix()
}

fn make_run_args(config: &config, props: &test_props, testfile: &str) ->
   procargs {
    let toolargs =
        if !props.no_valgrind {

            // If we've got another tool to run under (valgrind),
            // then split apart its command
            split_maybe_args(config.runtool)
        } else { [] };

    let args = toolargs + [make_exe_name(config, testfile)];
    ret {prog: args[0], args: vec::slice(args, 1u, vec::len(args))};
}

fn split_maybe_args(argstr: &option::t<str>) -> [str] {
    fn rm_whitespace(v: &[str]) -> [str] {
        fn flt(s: &str) -> option::t<str> {
            if !is_whitespace(s) { option::some(s) } else { option::none }
        }

        // FIXME: This should be in std
        fn is_whitespace(s: str) -> bool {
            for c: u8 in s { if c != ' ' as u8 { ret false; } }
            ret true;
        }
        vec::filter_map(flt, v)
    }

    alt argstr {
      option::some(s) { rm_whitespace(str::split(s, ' ' as u8)) }
      option::none. { [] }
    }
}

fn program_output(cx: &cx, testfile: &str, lib_path: &str, prog: &str,
                  args: &[str], input: option::t<str>) -> procres {
    let cmdline =
        {
            let cmdline = make_cmdline(lib_path, prog, args);
            logv(cx.config, #fmt["executing %s", cmdline]);
            cmdline
        };
    let res = procsrv::run(cx.procsrv, lib_path, prog, args, input);
    dump_output(cx.config, testfile, res.out, res.err);
    ret {status: res.status,
         stdout: res.out,
         stderr: res.err,
         cmdline: cmdline};
}

fn make_cmdline(libpath: &str, prog: &str, args: &[str]) -> str {
    #fmt["%s %s %s", lib_path_cmd_prefix(libpath), prog,
         str::connect(args, " ")]
}

// Build the LD_LIBRARY_PATH variable as it would be seen on the command line
// for diagnostic purposes
fn lib_path_cmd_prefix(path: &str) -> str {
    #fmt["%s=\"%s\"", util::lib_path_env_var(), util::make_new_path(path)]
}

fn dump_output(config: &config, testfile: &str, out: &str, err: &str) {
    dump_output_file(config, testfile, out, "out");
    dump_output_file(config, testfile, err, "err");
    maybe_dump_to_stdout(config, out, err);
}

#[cfg(target_os = "win32")]
#[cfg(target_os = "linux")]
fn dump_output_file(config: &config, testfile: &str, out: &str,
                    extension: &str) {
    let outfile = make_out_name(config, testfile, extension);
    let writer = io::file_writer(outfile, [io::create, io::truncate]);
    writer.write_str(out);
}

// FIXME (726): Can't use file_writer on mac
#[cfg(target_os = "macos")]
fn dump_output_file(config: &config, testfile: &str, out: &str,
                    extension: &str) {
}

fn make_out_name(config: &config, testfile: &str, extension: &str) -> str {
    output_base_name(config, testfile) + "." + extension
}

fn output_base_name(config: &config, testfile: &str) -> str {
    let base = config.build_base;
    let filename =
        {
            let parts = str::split(fs::basename(testfile), '.' as u8);
            parts = vec::slice(parts, 0u, vec::len(parts) - 1u);
            str::connect(parts, ".")
        };
    #fmt["%s%s.%s", base, filename, config.stage_id]
}

fn maybe_dump_to_stdout(config: &config, out: &str, err: &str) {
    if config.verbose {
        let sep1 = #fmt["------%s------------------------------", "stdout"];
        let sep2 = #fmt["------%s------------------------------", "stderr"];
        let sep3 = "------------------------------------------";
        io::stdout().write_line(sep1);
        io::stdout().write_line(out);
        io::stdout().write_line(sep2);
        io::stdout().write_line(err);
        io::stdout().write_line(sep3);
    }
}

fn error(err: &str) { io::stdout().write_line(#fmt["\nerror: %s", err]); }

fn fatal(err: &str) -> ! { error(err); fail; }

fn fatal_procres(err: &str, procres: procres) -> ! {
    let msg =
        #fmt["\n\
error: %s\n\
command: %s\n\
stdout:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
stderr:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
\n",
             err, procres.cmdline, procres.stdout, procres.stderr];
    io::stdout().write_str(msg);
    fail;
}
<|endoftext|>


<commit_before>// check-pass

// compile-flags: -O

fn foo(_: &'static [&'static str]) {}
fn bar(_: &'static [&'static str; 3]) {}
fn baz_i32(_: &'static i32) {}
fn baz_u32(_: &'static u32) {}

fn main() {
    foo(&["a", "b", "c"]);
    bar(&["d", "e", "f"]);

    // make sure that these do not cause trouble despite overflowing
    baz_u32(&(0-1));
    baz_i32(&-i32::MIN);
}
<commit_msg>Rollup merge of #80696 - RalfJung:failing-promoteds, r=oli-obk<commit_after>// check-pass

// compile-flags: -O

fn foo(_: &'static [&'static str]) {}
fn bar(_: &'static [&'static str; 3]) {}
const fn baz_i32(_: &'static i32) {}
const fn baz_u32(_: &'static u32) {}

const fn fail() -> i32 { 1/0 }
const C: i32 = {
    // Promoted that fails to evaluate in dead code -- this must work
    // (for backwards compatibility reasons).
    if false {
        baz_i32(&fail());
    }
    42
};

fn main() {
    foo(&["a", "b", "c"]);
    bar(&["d", "e", "f"]);
    assert_eq!(C, 42);

    // make sure that these do not cause trouble despite overflowing
    baz_u32(&(0-1));
    baz_i32(&-i32::MIN);
}
<|endoftext|>


<commit_before>// run-pass

#![allow(deprecated)]
// aux-build:typeid-intrinsic-aux1.rs
// aux-build:typeid-intrinsic-aux2.rs

#![feature(core_intrinsics)]

extern crate typeid_intrinsic_aux1 as other1;
extern crate typeid_intrinsic_aux2 as other2;

use std::hash::{SipHasher, Hasher, Hash};
use std::any::TypeId;

struct A;
struct Test;

pub fn main() {
    assert_eq!(TypeId::of::<other1::A>(), other1::id_A());
    assert_eq!(TypeId::of::<other1::B>(), other1::id_B());
    assert_eq!(TypeId::of::<other1::C>(), other1::id_C());
    assert_eq!(TypeId::of::<other1::D>(), other1::id_D());
    assert_eq!(TypeId::of::<other1::E>(), other1::id_E());
    assert_eq!(TypeId::of::<other1::F>(), other1::id_F());
    assert_eq!(TypeId::of::<other1::G>(), other1::id_G());
    assert_eq!(TypeId::of::<other1::H>(), other1::id_H());
    assert_eq!(TypeId::of::<other1::I>(), other1::id_I());

    assert_eq!(TypeId::of::<other2::A>(), other2::id_A());
    assert_eq!(TypeId::of::<other2::B>(), other2::id_B());
    assert_eq!(TypeId::of::<other2::C>(), other2::id_C());
    assert_eq!(TypeId::of::<other2::D>(), other2::id_D());
    assert_eq!(TypeId::of::<other2::E>(), other2::id_E());
    assert_eq!(TypeId::of::<other2::F>(), other2::id_F());
    assert_eq!(TypeId::of::<other2::G>(), other2::id_G());
    assert_eq!(TypeId::of::<other2::H>(), other2::id_H());
    assert_eq!(TypeId::of::<other1::I>(), other2::id_I());

    assert_eq!(other1::id_F(), other2::id_F());
    assert_eq!(other1::id_G(), other2::id_G());
    assert_eq!(other1::id_H(), other2::id_H());
    assert_eq!(other1::id_I(), other2::id_I());

    assert_eq!(TypeId::of::<isize>(), other2::foo::<isize>());
    assert_eq!(TypeId::of::<isize>(), other1::foo::<isize>());
    assert_eq!(other2::foo::<isize>(), other1::foo::<isize>());
    assert_eq!(TypeId::of::<A>(), other2::foo::<A>());
    assert_eq!(TypeId::of::<A>(), other1::foo::<A>());
    assert_eq!(other2::foo::<A>(), other1::foo::<A>());

    // sanity test of TypeId
    let (a, b, c) = (TypeId::of::<usize>(), TypeId::of::<&'static str>(),
                     TypeId::of::<Test>());
    let (d, e, f) = (TypeId::of::<usize>(), TypeId::of::<&'static str>(),
                     TypeId::of::<Test>());

    assert!(a != b);
    assert!(a != c);
    assert!(b != c);

    assert_eq!(a, d);
    assert_eq!(b, e);
    assert_eq!(c, f);

    // check it has a hash
    let (a, b) = (TypeId::of::<usize>(), TypeId::of::<usize>());

    let mut s1 = SipHasher::new();
    a.hash(&mut s1);
    let mut s2 = SipHasher::new();
    b.hash(&mut s2);

    assert_eq!(s1.finish(), s2.finish());

    // Check projections

    assert_eq!(TypeId::of::<other1::I32Iterator>(), other1::id_i32_iterator());
    assert_eq!(TypeId::of::<other1::U32Iterator>(), other1::id_u32_iterator());
    assert_eq!(other1::id_i32_iterator(), other2::id_i32_iterator());
    assert_eq!(other1::id_u32_iterator(), other2::id_u32_iterator());
    assert!(other1::id_i32_iterator() != other1::id_u32_iterator());
    assert!(TypeId::of::<other1::I32Iterator>() != TypeId::of::<other1::U32Iterator>());

    // Check fn pointer against collisions
    assert!(TypeId::of::<fn(fn(A) -> A) -> A>() !=
            TypeId::of::<fn(fn() -> A, A) -> A>());
}
<commit_msg>check against more collisions for TypeId of fn pointer<commit_after>// run-pass

#![allow(deprecated)]
// aux-build:typeid-intrinsic-aux1.rs
// aux-build:typeid-intrinsic-aux2.rs

#![feature(core_intrinsics)]

extern crate typeid_intrinsic_aux1 as other1;
extern crate typeid_intrinsic_aux2 as other2;

use std::hash::{SipHasher, Hasher, Hash};
use std::any::TypeId;

struct A;
struct Test;

pub fn main() {
    assert_eq!(TypeId::of::<other1::A>(), other1::id_A());
    assert_eq!(TypeId::of::<other1::B>(), other1::id_B());
    assert_eq!(TypeId::of::<other1::C>(), other1::id_C());
    assert_eq!(TypeId::of::<other1::D>(), other1::id_D());
    assert_eq!(TypeId::of::<other1::E>(), other1::id_E());
    assert_eq!(TypeId::of::<other1::F>(), other1::id_F());
    assert_eq!(TypeId::of::<other1::G>(), other1::id_G());
    assert_eq!(TypeId::of::<other1::H>(), other1::id_H());
    assert_eq!(TypeId::of::<other1::I>(), other1::id_I());

    assert_eq!(TypeId::of::<other2::A>(), other2::id_A());
    assert_eq!(TypeId::of::<other2::B>(), other2::id_B());
    assert_eq!(TypeId::of::<other2::C>(), other2::id_C());
    assert_eq!(TypeId::of::<other2::D>(), other2::id_D());
    assert_eq!(TypeId::of::<other2::E>(), other2::id_E());
    assert_eq!(TypeId::of::<other2::F>(), other2::id_F());
    assert_eq!(TypeId::of::<other2::G>(), other2::id_G());
    assert_eq!(TypeId::of::<other2::H>(), other2::id_H());
    assert_eq!(TypeId::of::<other1::I>(), other2::id_I());

    assert_eq!(other1::id_F(), other2::id_F());
    assert_eq!(other1::id_G(), other2::id_G());
    assert_eq!(other1::id_H(), other2::id_H());
    assert_eq!(other1::id_I(), other2::id_I());

    assert_eq!(TypeId::of::<isize>(), other2::foo::<isize>());
    assert_eq!(TypeId::of::<isize>(), other1::foo::<isize>());
    assert_eq!(other2::foo::<isize>(), other1::foo::<isize>());
    assert_eq!(TypeId::of::<A>(), other2::foo::<A>());
    assert_eq!(TypeId::of::<A>(), other1::foo::<A>());
    assert_eq!(other2::foo::<A>(), other1::foo::<A>());

    // sanity test of TypeId
    let (a, b, c) = (TypeId::of::<usize>(), TypeId::of::<&'static str>(),
                     TypeId::of::<Test>());
    let (d, e, f) = (TypeId::of::<usize>(), TypeId::of::<&'static str>(),
                     TypeId::of::<Test>());

    assert!(a != b);
    assert!(a != c);
    assert!(b != c);

    assert_eq!(a, d);
    assert_eq!(b, e);
    assert_eq!(c, f);

    // check it has a hash
    let (a, b) = (TypeId::of::<usize>(), TypeId::of::<usize>());

    let mut s1 = SipHasher::new();
    a.hash(&mut s1);
    let mut s2 = SipHasher::new();
    b.hash(&mut s2);

    assert_eq!(s1.finish(), s2.finish());

    // Check projections

    assert_eq!(TypeId::of::<other1::I32Iterator>(), other1::id_i32_iterator());
    assert_eq!(TypeId::of::<other1::U32Iterator>(), other1::id_u32_iterator());
    assert_eq!(other1::id_i32_iterator(), other2::id_i32_iterator());
    assert_eq!(other1::id_u32_iterator(), other2::id_u32_iterator());
    assert_ne!(other1::id_i32_iterator(), other1::id_u32_iterator());
    assert_ne!(TypeId::of::<other1::I32Iterator>(), TypeId::of::<other1::U32Iterator>());

    // Check fn pointer against collisions
    assert_ne!(
        TypeId::of::<fn(fn(A) -> A) -> A>(),
        TypeId::of::<fn(fn() -> A, A) -> A>()
    );
    assert_ne!(
        TypeId::of::<for<'a> fn(&'a i32) -> &'a i32>(),
        TypeId::of::<for<'a> fn(&'a i32) -> &'static i32>()
    );
    assert_ne!(
        TypeId::of::<for<'a, 'b> fn(&'a i32, &'b i32) -> &'a i32>(),
        TypeId::of::<for<'a, 'b> fn(&'b i32, &'a i32) -> &'a i32>()
    );
}
<|endoftext|>


<commit_before>#![doc(include = "core_arch_docs.md")]
#![cfg_attr(stdsimd_strict, deny(warnings))]
#![allow(dead_code)]
#![allow(unused_features)]
#![feature(
    const_fn,
    const_fn_union,
    link_llvm_intrinsics,
    platform_intrinsics,
    repr_simd,
    simd_ffi,
    asm,
    proc_macro_hygiene,
    stmt_expr_attributes,
    core_intrinsics,
    no_core,
    rustc_attrs,
    stdsimd,
    staged_api,
    maybe_uninit,
    doc_cfg,
    mmx_target_feature,
    tbm_target_feature,
    sse4a_target_feature,
    arm_target_feature,
    aarch64_target_feature,
    cmpxchg16b_target_feature,
    avx512_target_feature,
    mips_target_feature,
    powerpc_target_feature,
    wasm_target_feature,
    abi_unadjusted,
    adx_target_feature,
    rtm_target_feature,
    f16c_target_feature,
    external_doc
)]
#![cfg_attr(test, feature(test, abi_vectorcall, untagged_unions))]
#![deny(clippy::missing_inline_in_public_items)]
#![allow(
    clippy::inline_always,
    clippy::too_many_arguments,
    clippy::cast_sign_loss,
    clippy::cast_lossless,
    clippy::cast_possible_wrap,
    clippy::cast_possible_truncation,
    clippy::cast_precision_loss,
    clippy::shadow_reuse,
    clippy::cognitive_complexity,
    clippy::similar_names,
    clippy::many_single_char_names
)]
#![cfg_attr(test, allow(unused_imports))]
#![no_std]
#![unstable(feature = "stdsimd", issue = "27731")]
#![doc(
    test(attr(deny(warnings))),
    test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
)]

#[cfg(test)]
#[macro_use]
extern crate std;
#[cfg(test)]
#[macro_use]
extern crate std_detect;
#[cfg(test)]
extern crate stdsimd_test;

#[cfg(all(test, target_arch = "wasm32"))]
extern crate wasm_bindgen_test;

#[path = "mod.rs"]
mod core_arch;

pub use self::core_arch::arch::*;

#[allow(unused_imports)]
use core::{ffi, hint, intrinsics, marker, mem, ptr, sync};
<commit_msg>Drop maybe_uninit<commit_after>#![doc(include = "core_arch_docs.md")]
#![cfg_attr(stdsimd_strict, deny(warnings))]
#![allow(dead_code)]
#![allow(unused_features)]
#![feature(
    const_fn,
    const_fn_union,
    link_llvm_intrinsics,
    platform_intrinsics,
    repr_simd,
    simd_ffi,
    asm,
    proc_macro_hygiene,
    stmt_expr_attributes,
    core_intrinsics,
    no_core,
    rustc_attrs,
    stdsimd,
    staged_api,
    doc_cfg,
    mmx_target_feature,
    tbm_target_feature,
    sse4a_target_feature,
    arm_target_feature,
    aarch64_target_feature,
    cmpxchg16b_target_feature,
    avx512_target_feature,
    mips_target_feature,
    powerpc_target_feature,
    wasm_target_feature,
    abi_unadjusted,
    adx_target_feature,
    rtm_target_feature,
    f16c_target_feature,
    external_doc
)]
#![cfg_attr(test, feature(test, abi_vectorcall, untagged_unions))]
#![deny(clippy::missing_inline_in_public_items)]
#![allow(
    clippy::inline_always,
    clippy::too_many_arguments,
    clippy::cast_sign_loss,
    clippy::cast_lossless,
    clippy::cast_possible_wrap,
    clippy::cast_possible_truncation,
    clippy::cast_precision_loss,
    clippy::shadow_reuse,
    clippy::cognitive_complexity,
    clippy::similar_names,
    clippy::many_single_char_names
)]
#![cfg_attr(test, allow(unused_imports))]
#![no_std]
#![unstable(feature = "stdsimd", issue = "27731")]
#![doc(
    test(attr(deny(warnings))),
    test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
)]

#[cfg(test)]
#[macro_use]
extern crate std;
#[cfg(test)]
#[macro_use]
extern crate std_detect;
#[cfg(test)]
extern crate stdsimd_test;

#[cfg(all(test, target_arch = "wasm32"))]
extern crate wasm_bindgen_test;

#[path = "mod.rs"]
mod core_arch;

pub use self::core_arch::arch::*;

#[allow(unused_imports)]
use core::{ffi, hint, intrinsics, marker, mem, ptr, sync};
<|endoftext|>


<commit_before>//! Simple hierarchical profiler
use std::{
    cell::RefCell,
    collections::{BTreeMap, HashSet},
    env,
    io::{stderr, Write},
    sync::{
        atomic::{AtomicBool, Ordering},
        RwLock,
    },
    time::{Duration, Instant},
};

use once_cell::sync::Lazy;

use crate::tree::{Idx, Tree};

/// Filtering syntax
/// env RA_PROFILE=*             // dump everything
/// env RA_PROFILE=foo|bar|baz   // enabled only selected entries
/// env RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more than 10 ms
pub fn init() {
    countme::enable(env::var("RA_COUNT").is_ok());
    let spec = env::var("RA_PROFILE").unwrap_or_default();
    init_from(&spec);
}

pub fn init_from(spec: &str) {
    let filter = if spec.is_empty() { Filter::disabled() } else { Filter::from_spec(spec) };
    filter.install();
}

type Label = &'static str;

/// This function starts a profiling scope in the current execution stack with a given description.
/// It returns a `Profile` struct that measures elapsed time between this method invocation and `Profile` struct drop.
/// It supports nested profiling scopes in case when this function is invoked multiple times at the execution stack.
/// In this case the profiling information will be nested at the output.
/// Profiling information is being printed in the stderr.
///
/// # Example
/// ```
/// profile::init_from("profile1|profile2@2");
/// profiling_function1();
///
/// fn profiling_function1() {
///     let _p = profile::span("profile1");
///     profiling_function2();
/// }
///
/// fn profiling_function2() {
///     let _p = profile::span("profile2");
/// }
/// ```
/// This will print in the stderr the following:
/// ```text
///  0ms - profile
///      0ms - profile2
/// ```
#[inline]
pub fn span(label: Label) -> ProfileSpan {
    debug_assert!(!label.is_empty());

    let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
    if enabled && with_profile_stack(|stack| stack.push(label)) {
        ProfileSpan(Some(ProfilerImpl { label, detail: None }))
    } else {
        ProfileSpan(None)
    }
}

#[inline]
pub fn heartbeat_span() -> HeartbeatSpan {
    let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
    HeartbeatSpan::new(enabled)
}

#[inline]
pub fn heartbeat() {
    let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
    if enabled {
        with_profile_stack(|it| it.heartbeat(1));
    }
}

pub struct ProfileSpan(Option<ProfilerImpl>);

struct ProfilerImpl {
    label: Label,
    detail: Option<String>,
}

impl ProfileSpan {
    pub fn detail(mut self, detail: impl FnOnce() -> String) -> ProfileSpan {
        if let Some(profiler) = &mut self.0 {
            profiler.detail = Some(detail())
        }
        self
    }
}

impl Drop for ProfilerImpl {
    #[inline]
    fn drop(&mut self) {
        with_profile_stack(|it| it.pop(self.label, self.detail.take()));
    }
}

pub struct HeartbeatSpan {
    enabled: bool,
}

impl HeartbeatSpan {
    #[inline]
    pub fn new(enabled: bool) -> Self {
        if enabled {
            with_profile_stack(|it| it.heartbeats(true))
        }
        Self { enabled }
    }
}

impl Drop for HeartbeatSpan {
    fn drop(&mut self) {
        if self.enabled {
            with_profile_stack(|it| it.heartbeats(false))
        }
    }
}

static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default);

fn with_profile_stack<T>(f: impl FnOnce(&mut ProfileStack) -> T) -> T {
    thread_local!(static STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new()));
    STACK.with(|it| f(&mut *it.borrow_mut()))
}

#[derive(Default, Clone, Debug)]
struct Filter {
    depth: usize,
    allowed: HashSet<String>,
    longer_than: Duration,
    heartbeat_longer_than: Duration,
    version: usize,
}

impl Filter {
    fn disabled() -> Filter {
        Filter::default()
    }

    fn from_spec(mut spec: &str) -> Filter {
        let longer_than = if let Some(idx) = spec.rfind('>') {
            let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than");
            spec = &spec[..idx];
            Duration::from_millis(longer_than)
        } else {
            Duration::new(0, 0)
        };
        let heartbeat_longer_than = longer_than;

        let depth = if let Some(idx) = spec.rfind('@') {
            let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
            spec = &spec[..idx];
            depth
        } else {
            999
        };
        let allowed =
            if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() };
        Filter { depth, allowed, longer_than, heartbeat_longer_than, version: 0 }
    }

    fn install(mut self) {
        PROFILING_ENABLED.store(self.depth > 0, Ordering::SeqCst);
        let mut old = FILTER.write().unwrap();
        self.version = old.version + 1;
        *old = self;
    }
}

struct ProfileStack {
    frames: Vec<Frame>,
    filter: Filter,
    messages: Tree<Message>,
    heartbeats: bool,
}

struct Frame {
    t: Instant,
    heartbeats: u32,
}

#[derive(Default)]
struct Message {
    duration: Duration,
    label: Label,
    detail: Option<String>,
}

impl ProfileStack {
    fn new() -> ProfileStack {
        ProfileStack {
            frames: Vec::new(),
            messages: Tree::default(),
            filter: Default::default(),
            heartbeats: false,
        }
    }

    fn push(&mut self, label: Label) -> bool {
        if self.frames.is_empty() {
            if let Ok(f) = FILTER.try_read() {
                if f.version > self.filter.version {
                    self.filter = f.clone();
                }
            };
        }
        if self.frames.len() > self.filter.depth {
            return false;
        }
        let allowed = &self.filter.allowed;
        if self.frames.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
            return false;
        }

        self.frames.push(Frame { t: Instant::now(), heartbeats: 0 });
        self.messages.start();
        true
    }

    fn pop(&mut self, label: Label, detail: Option<String>) {
        let frame = self.frames.pop().unwrap();
        let duration = frame.t.elapsed();

        if self.heartbeats {
            self.heartbeat(frame.heartbeats);
            let avg_span = duration / (frame.heartbeats + 1);
            if avg_span > self.filter.heartbeat_longer_than {
                eprintln!("Too few heartbeats {} ({}/{:?})?", label, frame.heartbeats, duration)
            }
        }

        self.messages.finish(Message { duration, label, detail });
        if self.frames.is_empty() {
            let longer_than = self.filter.longer_than;
            // Convert to millis for comparison to avoid problems with rounding
            // (otherwise we could print `0ms` despite user's `>0` filter when
            // `duration` is just a few nanos).
            if duration.as_millis() > longer_than.as_millis() {
                if let Some(root) = self.messages.root() {
                    print(&self.messages, root, 0, longer_than, &mut stderr().lock());
                }
            }
            self.messages.clear();
        }
    }

    fn heartbeats(&mut self, yes: bool) {
        self.heartbeats = yes;
    }
    fn heartbeat(&mut self, n: u32) {
        if let Some(frame) = self.frames.last_mut() {
            frame.heartbeats += n;
        }
    }
}

fn print(
    tree: &Tree<Message>,
    curr: Idx<Message>,
    level: u32,
    longer_than: Duration,
    out: &mut impl Write,
) {
    let current_indent = "    ".repeat(level as usize);
    let detail = tree[curr].detail.as_ref().map(|it| format!(" @ {}", it)).unwrap_or_default();
    writeln!(
        out,
        "{}{:5}ms - {}{}",
        current_indent,
        tree[curr].duration.as_millis(),
        tree[curr].label,
        detail,
    )
    .expect("printing profiling info");

    let mut accounted_for = Duration::default();
    let mut short_children = BTreeMap::new(); // Use `BTreeMap` to get deterministic output.
    for child in tree.children(curr) {
        accounted_for += tree[child].duration;

        if tree[child].duration.as_millis() > longer_than.as_millis() {
            print(tree, child, level + 1, longer_than, out)
        } else {
            let (total_duration, cnt) =
                short_children.entry(tree[child].label).or_insert((Duration::default(), 0));
            *total_duration += tree[child].duration;
            *cnt += 1;
        }
    }

    for (child_msg, (duration, count)) in short_children.iter() {
        let millis = duration.as_millis();
        writeln!(out, "    {}{:5}ms - {} ({} calls)", current_indent, millis, child_msg, count)
            .expect("printing profiling info");
    }

    let unaccounted = tree[curr].duration - accounted_for;
    if tree.children(curr).next().is_some() && unaccounted > longer_than {
        writeln!(out, "    {}{:5}ms - ???", current_indent, unaccounted.as_millis())
            .expect("printing profiling info");
    }
}
<commit_msg>internal: make non-zero times stand out in profile<commit_after>//! Simple hierarchical profiler
use std::{
    cell::RefCell,
    collections::{BTreeMap, HashSet},
    env, fmt,
    io::{stderr, Write},
    sync::{
        atomic::{AtomicBool, Ordering},
        RwLock,
    },
    time::{Duration, Instant},
};

use once_cell::sync::Lazy;

use crate::tree::{Idx, Tree};

/// Filtering syntax
/// env RA_PROFILE=*             // dump everything
/// env RA_PROFILE=foo|bar|baz   // enabled only selected entries
/// env RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more than 10 ms
pub fn init() {
    countme::enable(env::var("RA_COUNT").is_ok());
    let spec = env::var("RA_PROFILE").unwrap_or_default();
    init_from(&spec);
}

pub fn init_from(spec: &str) {
    let filter = if spec.is_empty() { Filter::disabled() } else { Filter::from_spec(spec) };
    filter.install();
}

type Label = &'static str;

/// This function starts a profiling scope in the current execution stack with a given description.
/// It returns a `Profile` struct that measures elapsed time between this method invocation and `Profile` struct drop.
/// It supports nested profiling scopes in case when this function is invoked multiple times at the execution stack.
/// In this case the profiling information will be nested at the output.
/// Profiling information is being printed in the stderr.
///
/// # Example
/// ```
/// profile::init_from("profile1|profile2@2");
/// profiling_function1();
///
/// fn profiling_function1() {
///     let _p = profile::span("profile1");
///     profiling_function2();
/// }
///
/// fn profiling_function2() {
///     let _p = profile::span("profile2");
/// }
/// ```
/// This will print in the stderr the following:
/// ```text
///  0ms - profile
///      0ms - profile2
/// ```
#[inline]
pub fn span(label: Label) -> ProfileSpan {
    debug_assert!(!label.is_empty());

    let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
    if enabled && with_profile_stack(|stack| stack.push(label)) {
        ProfileSpan(Some(ProfilerImpl { label, detail: None }))
    } else {
        ProfileSpan(None)
    }
}

#[inline]
pub fn heartbeat_span() -> HeartbeatSpan {
    let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
    HeartbeatSpan::new(enabled)
}

#[inline]
pub fn heartbeat() {
    let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
    if enabled {
        with_profile_stack(|it| it.heartbeat(1));
    }
}

pub struct ProfileSpan(Option<ProfilerImpl>);

struct ProfilerImpl {
    label: Label,
    detail: Option<String>,
}

impl ProfileSpan {
    pub fn detail(mut self, detail: impl FnOnce() -> String) -> ProfileSpan {
        if let Some(profiler) = &mut self.0 {
            profiler.detail = Some(detail())
        }
        self
    }
}

impl Drop for ProfilerImpl {
    #[inline]
    fn drop(&mut self) {
        with_profile_stack(|it| it.pop(self.label, self.detail.take()));
    }
}

pub struct HeartbeatSpan {
    enabled: bool,
}

impl HeartbeatSpan {
    #[inline]
    pub fn new(enabled: bool) -> Self {
        if enabled {
            with_profile_stack(|it| it.heartbeats(true))
        }
        Self { enabled }
    }
}

impl Drop for HeartbeatSpan {
    fn drop(&mut self) {
        if self.enabled {
            with_profile_stack(|it| it.heartbeats(false))
        }
    }
}

static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default);

fn with_profile_stack<T>(f: impl FnOnce(&mut ProfileStack) -> T) -> T {
    thread_local!(static STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new()));
    STACK.with(|it| f(&mut *it.borrow_mut()))
}

#[derive(Default, Clone, Debug)]
struct Filter {
    depth: usize,
    allowed: HashSet<String>,
    longer_than: Duration,
    heartbeat_longer_than: Duration,
    version: usize,
}

impl Filter {
    fn disabled() -> Filter {
        Filter::default()
    }

    fn from_spec(mut spec: &str) -> Filter {
        let longer_than = if let Some(idx) = spec.rfind('>') {
            let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than");
            spec = &spec[..idx];
            Duration::from_millis(longer_than)
        } else {
            Duration::new(0, 0)
        };
        let heartbeat_longer_than = longer_than;

        let depth = if let Some(idx) = spec.rfind('@') {
            let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
            spec = &spec[..idx];
            depth
        } else {
            999
        };
        let allowed =
            if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() };
        Filter { depth, allowed, longer_than, heartbeat_longer_than, version: 0 }
    }

    fn install(mut self) {
        PROFILING_ENABLED.store(self.depth > 0, Ordering::SeqCst);
        let mut old = FILTER.write().unwrap();
        self.version = old.version + 1;
        *old = self;
    }
}

struct ProfileStack {
    frames: Vec<Frame>,
    filter: Filter,
    messages: Tree<Message>,
    heartbeats: bool,
}

struct Frame {
    t: Instant,
    heartbeats: u32,
}

#[derive(Default)]
struct Message {
    duration: Duration,
    label: Label,
    detail: Option<String>,
}

impl ProfileStack {
    fn new() -> ProfileStack {
        ProfileStack {
            frames: Vec::new(),
            messages: Tree::default(),
            filter: Default::default(),
            heartbeats: false,
        }
    }

    fn push(&mut self, label: Label) -> bool {
        if self.frames.is_empty() {
            if let Ok(f) = FILTER.try_read() {
                if f.version > self.filter.version {
                    self.filter = f.clone();
                }
            };
        }
        if self.frames.len() > self.filter.depth {
            return false;
        }
        let allowed = &self.filter.allowed;
        if self.frames.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
            return false;
        }

        self.frames.push(Frame { t: Instant::now(), heartbeats: 0 });
        self.messages.start();
        true
    }

    fn pop(&mut self, label: Label, detail: Option<String>) {
        let frame = self.frames.pop().unwrap();
        let duration = frame.t.elapsed();

        if self.heartbeats {
            self.heartbeat(frame.heartbeats);
            let avg_span = duration / (frame.heartbeats + 1);
            if avg_span > self.filter.heartbeat_longer_than {
                eprintln!("Too few heartbeats {} ({}/{:?})?", label, frame.heartbeats, duration)
            }
        }

        self.messages.finish(Message { duration, label, detail });
        if self.frames.is_empty() {
            let longer_than = self.filter.longer_than;
            // Convert to millis for comparison to avoid problems with rounding
            // (otherwise we could print `0ms` despite user's `>0` filter when
            // `duration` is just a few nanos).
            if duration.as_millis() > longer_than.as_millis() {
                if let Some(root) = self.messages.root() {
                    print(&self.messages, root, 0, longer_than, &mut stderr().lock());
                }
            }
            self.messages.clear();
        }
    }

    fn heartbeats(&mut self, yes: bool) {
        self.heartbeats = yes;
    }
    fn heartbeat(&mut self, n: u32) {
        if let Some(frame) = self.frames.last_mut() {
            frame.heartbeats += n;
        }
    }
}

fn print(
    tree: &Tree<Message>,
    curr: Idx<Message>,
    level: u32,
    longer_than: Duration,
    out: &mut impl Write,
) {
    let current_indent = "    ".repeat(level as usize);
    let detail = tree[curr].detail.as_ref().map(|it| format!(" @ {}", it)).unwrap_or_default();
    writeln!(
        out,
        "{}{} - {}{}",
        current_indent,
        ms(tree[curr].duration),
        tree[curr].label,
        detail,
    )
    .expect("printing profiling info");

    let mut accounted_for = Duration::default();
    let mut short_children = BTreeMap::new(); // Use `BTreeMap` to get deterministic output.
    for child in tree.children(curr) {
        accounted_for += tree[child].duration;

        if tree[child].duration.as_millis() > longer_than.as_millis() {
            print(tree, child, level + 1, longer_than, out)
        } else {
            let (total_duration, cnt) =
                short_children.entry(tree[child].label).or_insert((Duration::default(), 0));
            *total_duration += tree[child].duration;
            *cnt += 1;
        }
    }

    for (child_msg, (duration, count)) in short_children.iter() {
        writeln!(out, "    {}{} - {} ({} calls)", current_indent, ms(*duration), child_msg, count)
            .expect("printing profiling info");
    }

    let unaccounted = tree[curr].duration - accounted_for;
    if tree.children(curr).next().is_some() && unaccounted > longer_than {
        writeln!(out, "    {}{} - ???", current_indent, ms(unaccounted))
            .expect("printing profiling info");
    }
}

#[allow(non_camel_case_types)]
struct ms(Duration);

impl fmt::Display for ms {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.0.as_millis() {
            0 => f.write_str("    0  "),
            n => write!(f, "{:5}ms", n),
        }
    }
}
<|endoftext|>


<commit_before>//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.

use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::path::Path;

// A few of those error codes can't be tested but all the others can and *should* be tested!
const WHITELIST: &[&str] = &[
    "E0183", "E0227", "E0279", "E0280", "E0311", "E0313", "E0314", "E0315", "E0377", "E0456",
    "E0461", "E0462", "E0464", "E0465", "E0472", "E0473", "E0474", "E0475", "E0476", "E0479",
    "E0480", "E0481", "E0482", "E0483", "E0484", "E0485", "E0486", "E0487", "E0488", "E0489",
    "E0514", "E0519", "E0523", "E0553", "E0554", "E0570", "E0629", "E0630", "E0640", "E0717",
    "E0727", "E0729",
];

fn check_error_code_explanation(
    f: &str,
    error_codes: &mut HashMap<String, bool>,
    err_code: String,
) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("```") && s.contains("compile_fail") && s.contains('E') {
            error_codes.insert(err_code, true);
            return;
        } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            error_codes.get_mut(&err_code).map(|x| *x = true);
            return;
        }
    }
}

macro_rules! some_or_continue {
    ($e:expr) => {
        match $e {
            Some(e) => e,
            None => continue,
        }
    };
}

fn extract_error_codes(f: &str, error_codes: &mut HashMap<String, bool>, path: &Path) {
    let mut reached_no_explanation = false;

    for line in f.lines() {
        let s = line.trim();
        if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
            if let Some(err_code) = s.splitn(2, ':').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) {
                    error_codes.insert(err_code.clone(), false);
                }
                // Now we extract the tests from the markdown file!
                let md = some_or_continue!(s.splitn(2, "include_str!(\"").nth(1));
                let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
                let path = some_or_continue!(path.parent()).join(md_file_name);
                match read_to_string(&path) {
                    Ok(content) => {
                        check_error_code_explanation(&content, error_codes, err_code);
                    }
                    Err(e) => {
                        eprintln!("Couldn't read `{}`: {}", path.display(), e);
                    }
                }
            }
        } else if reached_no_explanation && s.starts_with('E') {
            if let Some(err_code) = s.splitn(2, ',').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) {
                    // this check should *never* fail!
                    error_codes.insert(err_code, false);
                }
            }
        } else if s == ";" {
            reached_no_explanation = true;
        }
    }
}

fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("error[E") || s.starts_with("warning[E") {
            if let Some(err_code) = s.splitn(2, ']').next() {
                if let Some(err_code) = err_code.splitn(2, '[').nth(1) {
                    let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
                    *nb = true;
                }
            }
        }
    }
}

pub fn check(path: &Path, bad: &mut bool) {
    println!("Checking which error codes lack tests...");
    let mut error_codes: HashMap<String, bool> = HashMap::new();
    super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| {
        let file_name = entry.file_name();
        if file_name == "error_codes.rs" {
            extract_error_codes(contents, &mut error_codes, entry.path());
        } else if entry.path().extension() == Some(OsStr::new("stderr")) {
            extract_error_codes_from_tests(contents, &mut error_codes);
        }
    });
    println!("Found {} error codes", error_codes.len());

    let mut errors = Vec::new();
    for (err_code, nb) in &error_codes {
        if !*nb && !WHITELIST.contains(&err_code.as_str()) {
            errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
        }
    }
    errors.sort();
    for err in &errors {
        eprintln!("{}", err);
    }
    println!("Found {} error codes with no tests", errors.len());
    if !errors.is_empty() {
        *bad = true;
    }
    println!("Done!");
}
<commit_msg>Add checks to ensure that explanations have code examples using the error code and also that 'compile_fail' isn't mispelled<commit_after>//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.

use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::path::Path;

// A few of those error codes can't be tested but all the others can and *should* be tested!
const WHITELIST: &[&str] = &[
    "E0183", "E0227", "E0279", "E0280", "E0311", "E0313", "E0314", "E0315", "E0377", "E0456",
    "E0461", "E0462", "E0464", "E0465", "E0472", "E0473", "E0474", "E0475", "E0476", "E0479",
    "E0480", "E0481", "E0482", "E0483", "E0484", "E0485", "E0486", "E0487", "E0488", "E0489",
    "E0514", "E0519", "E0523", "E0553", "E0554", "E0570", "E0629", "E0630", "E0640", "E0717",
    "E0727", "E0729",
];

// Some error codes don't have any tests apparently...
const IGNORE_EXPLANATION_CHECK: &[&str] =
    &["E0570", "E0601", "E0602", "E0639", "E0729", "E0749", "E0750", "E0751"];

fn check_error_code_explanation(
    f: &str,
    error_codes: &mut HashMap<String, bool>,
    err_code: String,
) -> bool {
    let mut invalid_compile_fail_format = false;
    let mut found_error_code = false;

    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("```") {
            if s.contains("compile_fail") && s.contains('E') {
                if !found_error_code {
                    error_codes.insert(err_code.clone(), true);
                    found_error_code = true;
                }
            } else if s.contains("compile-fail") {
                invalid_compile_fail_format = true;
            }
        } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            if !found_error_code {
                error_codes.get_mut(&err_code).map(|x| *x = true);
                found_error_code = true;
            }
        }
    }
    invalid_compile_fail_format
}

fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &String) -> bool {
    let mut can_be_ignored = false;

    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            return true;
        }
        if s.starts_with("```") {
            if s.contains("compile_fail") && s.contains(err_code) {
                return true;
            } else if s.contains("(") {
                // It's very likely that we can't actually make it fail compilation...
                can_be_ignored = true;
            }
        }
    }
    can_be_ignored
}

macro_rules! some_or_continue {
    ($e:expr) => {
        match $e {
            Some(e) => e,
            None => continue,
        }
    };
}

fn extract_error_codes(
    f: &str,
    error_codes: &mut HashMap<String, bool>,
    path: &Path,
    errors: &mut Vec<String>,
) {
    let mut reached_no_explanation = false;

    for line in f.lines() {
        let s = line.trim();
        if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
            if let Some(err_code) = s.splitn(2, ':').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) {
                    error_codes.insert(err_code.clone(), false);
                }
                // Now we extract the tests from the markdown file!
                let md = some_or_continue!(s.splitn(2, "include_str!(\"").nth(1));
                let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
                let path = some_or_continue!(path.parent())
                    .join(md_file_name)
                    .canonicalize()
                    .expect("failed to canonicalize error explanation file path");
                match read_to_string(&path) {
                    Ok(content) => {
                        if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str())
                            && !check_if_error_code_is_test_in_explanation(&content, &err_code)
                        {
                            errors.push(format!(
                                "`{}` doesn't use its own error code in compile_fail example",
                                path.display(),
                            ));
                        }
                        if check_error_code_explanation(&content, error_codes, err_code) {
                            errors.push(format!(
                                "`{}` uses invalid tag `compile-fail` instead of `compile_fail`",
                                path.display(),
                            ));
                        }
                    }
                    Err(e) => {
                        eprintln!("Couldn't read `{}`: {}", path.display(), e);
                    }
                }
            }
        } else if reached_no_explanation && s.starts_with('E') {
            if let Some(err_code) = s.splitn(2, ',').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) {
                    // this check should *never* fail!
                    error_codes.insert(err_code, false);
                }
            }
        } else if s == ";" {
            reached_no_explanation = true;
        }
    }
}

fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("error[E") || s.starts_with("warning[E") {
            if let Some(err_code) = s.splitn(2, ']').next() {
                if let Some(err_code) = err_code.splitn(2, '[').nth(1) {
                    let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
                    *nb = true;
                }
            }
        }
    }
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut errors = Vec::new();
    println!("Checking which error codes lack tests...");
    let mut error_codes: HashMap<String, bool> = HashMap::new();
    super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| {
        let file_name = entry.file_name();
        if file_name == "error_codes.rs" {
            extract_error_codes(contents, &mut error_codes, entry.path(), &mut errors);
        } else if entry.path().extension() == Some(OsStr::new("stderr")) {
            extract_error_codes_from_tests(contents, &mut error_codes);
        }
    });
    if errors.is_empty() {
        println!("Found {} error codes", error_codes.len());

        for (err_code, nb) in &error_codes {
            if !*nb && !WHITELIST.contains(&err_code.as_str()) {
                errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
            }
        }
    }
    errors.sort();
    for err in &errors {
        eprintln!("{}", err);
    }
    println!("Found {} error codes with no tests", errors.len());
    if !errors.is_empty() {
        *bad = true;
    }
    println!("Done!");
}
<|endoftext|>


<commit_before>//! Room object parsing.
//!
//! If you just want to use the module, reading the rustdocs documentation is very recommended.
//! All types generated with macros will also have documentation for them available.
//!
//! Reading the source code is definitely possible. But there may be some investment in reading
//! each of the macros defined and used here, and it will be much easier to just read the documentation.

pub mod container;
pub mod controller;
pub mod creep;
pub mod extension;
pub mod keeper_lair;
pub mod lab;
pub mod link;
pub mod mineral;
pub mod nuker;
pub mod observer;
pub mod portal;
pub mod power_bank;
pub mod power_spawn;
pub mod rampart;
pub mod resource;
pub mod road;
pub mod shared;
pub mod source;
pub mod spawn;
pub mod storage;
pub mod terminal;
pub mod tower;
pub mod wall;

pub use self::{
    container::{StructureContainer, StructureContainerUpdate},
    controller::{StructureController, StructureControllerUpdate},
    creep::{Creep, CreepUpdate},
    extension::{StructureExtension, StructureExtensionUpdate},
    keeper_lair::{StructureKeeperLair, StructureKeeperLairUpdate},
    lab::{StructureLab, StructureLabUpdate},
    link::{StructureLink, StructureLinkUpdate},
    mineral::{Mineral, MineralUpdate},
    nuker::{StructureNuker, StructureNukerUpdate},
    observer::{StructureObserver, StructureObserverUpdate},
    portal::{StructurePortal, StructurePortalUpdate},
    power_bank::{StructurePowerBank, StructurePowerBankUpdate},
    power_spawn::{StructurePowerSpawn, StructurePowerSpawnUpdate},
    rampart::{StructureRampart, StructureRampartUpdate},
    resource::{Resource, ResourceUpdate},
    road::{StructureRoad, StructureRoadUpdate},
    source::{Source, SourceUpdate},
    spawn::{StructureSpawn, StructureSpawnUpdate},
    storage::{StructureStorage, StructureStorageUpdate},
    terminal::{StructureTerminal, StructureTerminalUpdate},
    tower::{StructureTower, StructureTowerUpdate},
    wall::{StructureWall, StructureWallUpdate},
};

use self::shared::ActionLogTarget;

/// Enum describing all known room objects.
#[derive(serde_derive::Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum KnownRoomObject {
    /// Source object.
    Source(Source),
    /// Mineral object.
    Mineral(Mineral),
    /// Spawn owned structure.
    Spawn(StructureSpawn),
    /// Extension owned structure.
    Extension(StructureExtension),
    /// Wall unowned structure.
    #[serde(rename = "constructedWall")]
    Wall(StructureWall),
    /// Road unowned structure.
    Road(StructureRoad),
    /// Rampart owned structure.
    Rampart(StructureRampart),
    /// Keeper Lair NPC structure.
    KeeperLair(StructureKeeperLair),
    /// Controller permanent structure.
    Controller(StructureController),
    /// Portal naturally occurring structure.
    Portal(StructurePortal),
    /// Link owned structure.
    Link(StructureLink),
    /// Storage owned structure.
    Storage(StructureStorage),
    /// Tower owned structure.
    Tower(StructureTower),
    /// Observer owned structure.
    Observer(StructureObserver),
    /// Power bank naturally occurring structure.
    PowerBank(StructurePowerBank),
    /// Power spawn owned structure.
    PowerSpawn(StructurePowerSpawn),
    /// Lab owned structure.
    Lab(StructureLab),
    /// Terminal owned structure.
    Terminal(StructureTerminal),
    /// Container unowned structure.
    Container(StructureContainer),
    /// Nuker owned structure.
    Nuker(StructureNuker),
    /// Creep owned creature.
    Creep(Creep),
    /// Resource dropped object.
    #[serde(rename = "energy")]
    Resource(Resource),
}

impl KnownRoomObject {
    /// Update this room object with a JSON update string.
    pub fn update(&mut self, input: serde_json::Value) -> Result<(), serde_json::Error> {
        use self::KnownRoomObject::*;

        macro_rules! large_match {
            (
                $(
                    $enum_name:ident
                ),*
            ) => (
                match *self {
                    $(
                        $enum_name(ref mut value) => value.update(serde_json::from_value(input)?),
                    )*
                }
            )
        }

        large_match!(
            Source, Mineral, Spawn, Extension, Wall, Road, Rampart, KeeperLair, Controller, Portal,
            Link, Storage, Tower, Observer, PowerBank, PowerSpawn, Lab, Terminal, Container, Nuker,
            Creep, Resource
        );

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use std::collections::hash_map::Entry::*;
    use std::collections::HashMap;

    use serde_json;

    use super::KnownRoomObject;

    #[test]
    fn parse_a_room_update_chain() {
        // This is a full bunch of messages from a real websocket stream.
        let json: serde_json::Value = serde_json::from_slice(include_bytes!("test-stream.json"))
            .expect("expected saved json file to parse successfully.");

        let array = match json {
            serde_json::Value::Array(v) => v,
            other => panic!("expected Array(), found {:?}", other),
        };

        let mut iter = array.into_iter().enumerate();

        let first_value = match iter.next().unwrap().1 {
            serde_json::Value::Object(m) => m,
            other => panic!("expected Map, found {:?}", other),
        };

        let mut id_to_thing = first_value
            .into_iter()
            .map(|(id, value)| Ok((id, serde_json::from_value(value)?)))
            .collect::<Result<HashMap<String, KnownRoomObject>, serde_json::Error>>()
            .expect("expected initial json map to parse.");

        for (update_index, update) in iter {
            let update = match update {
                serde_json::Value::Object(m) => m,
                other => panic!("expected Map, found {:?}", other),
            };

            for (id, value) in update.into_iter() {
                if value.is_null() {
                    id_to_thing.remove(&id);
                } else {
                    match id_to_thing.entry(id.clone()) {
                        Occupied(entry) => {
                            entry.into_mut().update(value).expect(&format!(
                                "expected {} in update #{} to succeed",
                                id, update_index
                            ));
                        }
                        Vacant(entry) => {
                            entry.insert(serde_json::from_value(value).expect(&format!(
                                "expected {} in update #{} to succeed",
                                id, update_index
                            )));
                        }
                    }
                }
            }
        }
    }
}
<commit_msg>Add some util functions to KnownRoomObject<commit_after>//! Room object parsing.
//!
//! If you just want to use the module, reading the rustdocs documentation is very recommended.
//! All types generated with macros will also have documentation for them available.
//!
//! Reading the source code is definitely possible. But there may be some investment in reading
//! each of the macros defined and used here, and it will be much easier to just read the documentation.

pub mod container;
pub mod controller;
pub mod creep;
pub mod extension;
pub mod keeper_lair;
pub mod lab;
pub mod link;
pub mod mineral;
pub mod nuker;
pub mod observer;
pub mod portal;
pub mod power_bank;
pub mod power_spawn;
pub mod rampart;
pub mod resource;
pub mod road;
pub mod shared;
pub mod source;
pub mod spawn;
pub mod storage;
pub mod terminal;
pub mod tower;
pub mod wall;

pub use self::{
    container::{StructureContainer, StructureContainerUpdate},
    controller::{StructureController, StructureControllerUpdate},
    creep::{Creep, CreepUpdate},
    extension::{StructureExtension, StructureExtensionUpdate},
    keeper_lair::{StructureKeeperLair, StructureKeeperLairUpdate},
    lab::{StructureLab, StructureLabUpdate},
    link::{StructureLink, StructureLinkUpdate},
    mineral::{Mineral, MineralUpdate},
    nuker::{StructureNuker, StructureNukerUpdate},
    observer::{StructureObserver, StructureObserverUpdate},
    portal::{StructurePortal, StructurePortalUpdate},
    power_bank::{StructurePowerBank, StructurePowerBankUpdate},
    power_spawn::{StructurePowerSpawn, StructurePowerSpawnUpdate},
    rampart::{StructureRampart, StructureRampartUpdate},
    resource::{Resource, ResourceUpdate},
    road::{StructureRoad, StructureRoadUpdate},
    source::{Source, SourceUpdate},
    spawn::{StructureSpawn, StructureSpawnUpdate},
    storage::{StructureStorage, StructureStorageUpdate},
    terminal::{StructureTerminal, StructureTerminalUpdate},
    tower::{StructureTower, StructureTowerUpdate},
    wall::{StructureWall, StructureWallUpdate},
};

use crate::RoomName;

use self::shared::ActionLogTarget;

/// Enum describing all known room objects.
#[derive(serde_derive::Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum KnownRoomObject {
    /// Source object.
    Source(Source),
    /// Mineral object.
    Mineral(Mineral),
    /// Spawn owned structure.
    Spawn(StructureSpawn),
    /// Extension owned structure.
    Extension(StructureExtension),
    /// Wall unowned structure.
    #[serde(rename = "constructedWall")]
    Wall(StructureWall),
    /// Road unowned structure.
    Road(StructureRoad),
    /// Rampart owned structure.
    Rampart(StructureRampart),
    /// Keeper Lair NPC structure.
    KeeperLair(StructureKeeperLair),
    /// Controller permanent structure.
    Controller(StructureController),
    /// Portal naturally occurring structure.
    Portal(StructurePortal),
    /// Link owned structure.
    Link(StructureLink),
    /// Storage owned structure.
    Storage(StructureStorage),
    /// Tower owned structure.
    Tower(StructureTower),
    /// Observer owned structure.
    Observer(StructureObserver),
    /// Power bank naturally occurring structure.
    PowerBank(StructurePowerBank),
    /// Power spawn owned structure.
    PowerSpawn(StructurePowerSpawn),
    /// Lab owned structure.
    Lab(StructureLab),
    /// Terminal owned structure.
    Terminal(StructureTerminal),
    /// Container unowned structure.
    Container(StructureContainer),
    /// Nuker owned structure.
    Nuker(StructureNuker),
    /// Creep owned creature.
    Creep(Creep),
    /// Resource dropped object.
    #[serde(rename = "energy")]
    Resource(Resource),
}

macro_rules! match_many_variants {
    (
        $src:ident, ($(
            $enum_name:ident
        ),*) ($name:ident) => $code:expr
    ) => (
        match $src {
            $(
                KnownRoomObject::$enum_name($name) => $code,
            )*
        }
    )
}

macro_rules! match_obj_variants {
    (
        $src:ident, $name:ident => $code:expr
    ) => (
        match_many_variants!(
            $src,
            (Source, Mineral, Spawn, Extension, Wall, Road, Rampart, KeeperLair, Controller, Portal,
            Link, Storage, Tower, Observer, PowerBank, PowerSpawn, Lab, Terminal, Container, Nuker,
            Creep, Resource)
            ($name) => $code
        )
    )
}

impl KnownRoomObject {
    /// Update this room object with a JSON update string.
    pub fn update(&mut self, input: serde_json::Value) -> Result<(), serde_json::Error> {
        match_obj_variants!(
            self, value => value.update(serde_json::from_value(input)?)
        );

        Ok(())
    }

    /// Get this object's x position
    pub fn x(&self) -> u16 {
        match_obj_variants!(self, v => v.x)
    }

    /// Get this object's y position
    pub fn y(&self) -> u16 {
        match_obj_variants!(self, v => v.y)
    }

    /// Get this object's id
    pub fn id(&self) -> &str {
        match_obj_variants!(self, v => &v.id)
    }

    /// Get this object's room name
    pub fn room(&self) -> RoomName {
        match_obj_variants!(self, v => v.room)
    }
}

#[cfg(test)]
mod test {
    use std::collections::hash_map::Entry::*;
    use std::collections::HashMap;

    use serde_json;

    use super::KnownRoomObject;

    #[test]
    fn parse_a_room_update_chain() {
        // This is a full bunch of messages from a real websocket stream.
        let json: serde_json::Value = serde_json::from_slice(include_bytes!("test-stream.json"))
            .expect("expected saved json file to parse successfully.");

        let array = match json {
            serde_json::Value::Array(v) => v,
            other => panic!("expected Array(), found {:?}", other),
        };

        let mut iter = array.into_iter().enumerate();

        let first_value = match iter.next().unwrap().1 {
            serde_json::Value::Object(m) => m,
            other => panic!("expected Map, found {:?}", other),
        };

        let mut id_to_thing = first_value
            .into_iter()
            .map(|(id, value)| Ok((id, serde_json::from_value(value)?)))
            .collect::<Result<HashMap<String, KnownRoomObject>, serde_json::Error>>()
            .expect("expected initial json map to parse.");

        for (update_index, update) in iter {
            let update = match update {
                serde_json::Value::Object(m) => m,
                other => panic!("expected Map, found {:?}", other),
            };

            for (id, value) in update.into_iter() {
                if value.is_null() {
                    id_to_thing.remove(&id);
                } else {
                    match id_to_thing.entry(id.clone()) {
                        Occupied(entry) => {
                            entry.into_mut().update(value).expect(&format!(
                                "expected {} in update #{} to succeed",
                                id, update_index
                            ));
                        }
                        Vacant(entry) => {
                            entry.insert(serde_json::from_value(value).expect(&format!(
                                "expected {} in update #{} to succeed",
                                id, update_index
                            )));
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate pkg_config;

use std::env;
use std::fs::{self, File};
use std::io::ErrorKind;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(n) => n,
        Err(e) => fail(&format!("\n{} failed with {}\n", stringify!($e), e)),
    })
}

fn main() {
    register_dep("SSH2");
    register_dep("OPENSSL");

    if env::var("LIBSSH2_SYS_USE_PKG_CONFIG").is_ok() {
        if pkg_config::find_library("libgit2").is_ok() {
            return
        }
    }

    let mut cflags = env::var("CFLAGS").unwrap_or(String::new());
    let target = env::var("TARGET").unwrap();
    let mingw = target.contains("windows-gnu");
    let msvc = target.contains("msvc");

    if msvc {
        // libgit2 passes the /GL flag to enable whole program optimization, but
        // this requires that the /LTCG flag is passed to the linker later on,
        // and currently the compiler does not do that, so we disable whole
        // program optimization entirely.
        cflags.push_str(" /GL-");
    } else {
        cflags.push_str(" -ffunction-sections -fdata-sections");

        if target.contains("i686") {
            cflags.push_str(" -m32");
        } else if target.contains("x86_64") {
            cflags.push_str(" -m64");
        }
        if !target.contains("i686") {
            cflags.push_str(" -fPIC");
        }
    }

    // libgit2 uses pkg-config to discover libssh2, but this doesn't work on
    // windows as libssh2 doesn't come with a libssh2.pc file in that install.
    // As a result we just manually turn on SSH support in libgit2 (a little
    // jankily) here...
    if mingw {
        cflags.push_str(" -DGIT_SSH");
    } else if msvc {
        cflags.push_str(" /DGIT_SSH");
    }
    if let Ok(libssh2_include) = env::var("DEP_SSH2_INCLUDE") {
        if mingw {
            cflags.push_str(&format!(" -I{}", libssh2_include));
        } else if msvc {
            cflags.push_str(&format!(" /I{}", libssh2_include));
        }
    }

    let src = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap());
    let dst = PathBuf::from(&env::var("OUT_DIR").unwrap());
    let _ = fs::create_dir(&dst.join("build"));

    let mut cmd = Command::new("cmake");
    cmd.arg(&src.join("libgit2"))
       .current_dir(&dst.join("build"));
    if mingw {
        cmd.arg("-G").arg("Unix Makefiles");
    } else if msvc {
        // If we don't pass this unfortunately cmake produces 32-bit builds
        cmd.arg("-G").arg("Visual Studio 12 2013 Win64");

        // Currently liblibc links to msvcrt which apparently is a dynamic CRT,
        // so we need to turn this off to get it to link right.
        cmd.arg("-DSTATIC_CRT=OFF");
    }
    let profile = match &env::var("PROFILE").unwrap()[..] {
        "bench" | "release" => "Release",
        _ if msvc => "Release", // currently we need to always use the same CRT
        _ => "Debug",
    };
    run(cmd.arg("-DBUILD_SHARED_LIBS=OFF")
           .arg("-DBUILD_CLAR=OFF")
           .arg(&format!("-DCMAKE_BUILD_TYPE={}", profile))
           .arg(&format!("-DCMAKE_INSTALL_PREFIX={}", dst.display()))
           .arg(&format!("-DCMAKE_C_FLAGS={}", cflags)), "cmake");

    let flags = dst.join("build/CMakeFiles/git2.dir/flags.make");
    let mut contents = String::new();

    // Make sure libssh2 was detected on unix systems, because it definitely
    // should have been!
    if !msvc {
        t!(t!(File::open(flags)).read_to_string(&mut contents));
        if !contents.contains("-DGIT_SSH") {
            fail("libgit2 failed to find libssh2, and SSH support is required");
        }
    }

    run(Command::new("cmake")
                .arg("--build").arg(".")
                .arg("--target").arg("install")
                .arg("--config").arg(profile)
                .current_dir(&dst.join("build")), "cmake");

    println!("cargo:root={}", dst.display());
    if target.contains("windows") {
        println!("cargo:rustc-link-lib=winhttp");
        println!("cargo:rustc-link-lib=rpcrt4");
        println!("cargo:rustc-link-lib=ole32");
        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=static=git2");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
        return
    }

    if env::var("HOST") == env::var("TARGET") {
        prepend("PKG_CONFIG_PATH", dst.join("lib/pkgconfig"));
        if pkg_config::Config::new().statik(true).find("libgit2").is_ok() {
            return
        }
    }

    println!("cargo:rustc-link-lib=static=git2");
    println!("cargo:rustc-link-search=native={}", dst.join("lib").display());
    if target.contains("apple") {
        println!("cargo:rustc-link-lib=iconv");
    }
}

fn run(cmd: &mut Command, program: &str) {
    println!("running: {:?}", cmd);
    let status = match cmd.status() {
        Ok(status) => status,
        Err(ref e) if e.kind() == ErrorKind::NotFound => {
            fail(&format!("failed to execute command: {}\nis `{}` not installed?",
                          e, program));
        }
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully, got: {}", status));
    }
}

fn register_dep(dep: &str) {
    match env::var(&format!("DEP_{}_ROOT", dep)) {
        Ok(s) => {
            prepend("CMAKE_PREFIX_PATH", PathBuf::from(&s));
            prepend("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        }
        Err(..) => {}
    }
}

fn prepend(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let mut v = vec![val];
    v.extend(env::split_paths(&prefix));
    env::set_var(var, &env::join_paths(v).unwrap());
}

fn fail(s: &str) -> ! {
    panic!("\n{}\n\nbuild script failed, must exit now", s)
}
<commit_msg>Force SSH support if pkg-config isn't found<commit_after>extern crate pkg_config;

use std::env;
use std::fs::{self, File};
use std::io::ErrorKind;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(n) => n,
        Err(e) => fail(&format!("\n{} failed with {}\n", stringify!($e), e)),
    })
}

fn main() {
    register_dep("SSH2");
    register_dep("OPENSSL");
    let has_pkgconfig = Command::new("pkg-config").output().is_ok();

    if env::var("LIBSSH2_SYS_USE_PKG_CONFIG").is_ok() {
        if pkg_config::find_library("libgit2").is_ok() {
            return
        }
    }

    let mut cflags = env::var("CFLAGS").unwrap_or(String::new());
    let target = env::var("TARGET").unwrap();
    let windows = target.contains("windows");
    let mingw = target.contains("windows-gnu");
    let msvc = target.contains("msvc");

    if msvc {
        // libgit2 passes the /GL flag to enable whole program optimization, but
        // this requires that the /LTCG flag is passed to the linker later on,
        // and currently the compiler does not do that, so we disable whole
        // program optimization entirely.
        cflags.push_str(" /GL-");
    } else {
        cflags.push_str(" -ffunction-sections -fdata-sections");

        if target.contains("i686") {
            cflags.push_str(" -m32");
        } else if target.contains("x86_64") {
            cflags.push_str(" -m64");
        }
        if !target.contains("i686") {
            cflags.push_str(" -fPIC");
        }
    }

    // libgit2 uses pkg-config to discover libssh2, but this doesn't work on
    // windows as libssh2 doesn't come with a libssh2.pc file in that install
    // (or when pkg-config isn't found). As a result we just manually turn on
    // SSH support in libgit2 (a little jankily) here...
    if windows || !has_pkgconfig {
        if msvc {
            cflags.push_str(" /DGIT_SSH");
        } else {
            cflags.push_str(" -DGIT_SSH");
        }
        if let Ok(libssh2_include) = env::var("DEP_SSH2_INCLUDE") {
            if msvc {
                cflags.push_str(&format!(" /I{}", libssh2_include));
            } else {
                cflags.push_str(&format!(" -I{}", libssh2_include));
            }
        }
    }

    let src = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap());
    let dst = PathBuf::from(&env::var("OUT_DIR").unwrap());
    let _ = fs::create_dir(&dst.join("build"));

    let mut cmd = Command::new("cmake");
    cmd.arg(&src.join("libgit2"))
       .current_dir(&dst.join("build"));
    if mingw {
        cmd.arg("-G").arg("Unix Makefiles");
    } else if msvc {
        // If we don't pass this unfortunately cmake produces 32-bit builds
        cmd.arg("-G").arg("Visual Studio 12 2013 Win64");

        // Currently liblibc links to msvcrt which apparently is a dynamic CRT,
        // so we need to turn this off to get it to link right.
        cmd.arg("-DSTATIC_CRT=OFF");
    }
    let profile = match &env::var("PROFILE").unwrap()[..] {
        "bench" | "release" => "Release",
        _ if msvc => "Release", // currently we need to always use the same CRT
        _ => "Debug",
    };
    run(cmd.arg("-DBUILD_SHARED_LIBS=OFF")
           .arg("-DBUILD_CLAR=OFF")
           .arg(&format!("-DCMAKE_BUILD_TYPE={}", profile))
           .arg(&format!("-DCMAKE_INSTALL_PREFIX={}", dst.display()))
           .arg(&format!("-DCMAKE_C_FLAGS={}", cflags)), "cmake");

    let flags = dst.join("build/CMakeFiles/git2.dir/flags.make");
    let mut contents = String::new();

    // Make sure libssh2 was detected on unix systems, because it definitely
    // should have been!
    if !msvc {
        t!(t!(File::open(flags)).read_to_string(&mut contents));
        if !contents.contains("-DGIT_SSH") {
            fail("libgit2 failed to find libssh2, and SSH support is required");
        }
    }

    run(Command::new("cmake")
                .arg("--build").arg(".")
                .arg("--target").arg("install")
                .arg("--config").arg(profile)
                .current_dir(&dst.join("build")), "cmake");

    println!("cargo:root={}", dst.display());
    if target.contains("windows") {
        println!("cargo:rustc-link-lib=winhttp");
        println!("cargo:rustc-link-lib=rpcrt4");
        println!("cargo:rustc-link-lib=ole32");
        println!("cargo:rustc-link-lib=crypt32");
        println!("cargo:rustc-link-lib=static=git2");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
        return
    }

    if env::var("HOST") == env::var("TARGET") {
        prepend("PKG_CONFIG_PATH", dst.join("lib/pkgconfig"));
        if pkg_config::Config::new().statik(true).find("libgit2").is_ok() {
            return
        }
    }

    println!("cargo:rustc-link-lib=static=git2");
    println!("cargo:rustc-link-search=native={}", dst.join("lib").display());
    if target.contains("apple") {
        println!("cargo:rustc-link-lib=iconv");
    }
}

fn run(cmd: &mut Command, program: &str) {
    println!("running: {:?}", cmd);
    let status = match cmd.status() {
        Ok(status) => status,
        Err(ref e) if e.kind() == ErrorKind::NotFound => {
            fail(&format!("failed to execute command: {}\nis `{}` not installed?",
                          e, program));
        }
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully, got: {}", status));
    }
}

fn register_dep(dep: &str) {
    match env::var(&format!("DEP_{}_ROOT", dep)) {
        Ok(s) => {
            prepend("CMAKE_PREFIX_PATH", PathBuf::from(&s));
            prepend("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        }
        Err(..) => {}
    }
}

fn prepend(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let mut v = vec![val];
    v.extend(env::split_paths(&prefix));
    env::set_var(var, &env::join_paths(v).unwrap());
}

fn fail(s: &str) -> ! {
    panic!("\n{}\n\nbuild script failed, must exit now", s)
}
<|endoftext|>