Skip to content

Commit

Permalink
auto merge of #19964 : pnkfelix/rust/everybody-loops-pprint, r=alexcr…
Browse files Browse the repository at this point in the history
…ichton

NOTE: Not yet ready for merge; see my first comment below.  I will remove this note after I post the appropriate follow-on commit to get `run-make` working too.

This PR adds a new pretty printing mode, `everybody_loops`, which replaces every function body in the input with `loop { }`.  I have found this to be useful for building narrow test cases starting from bugs in static analyses like typeck and borrowck that are only exposed initially from complex input source crates like `librustc`.

The process to follow is:
 1. You first generate the new input with every function body replaced with `loop { }`, then
 2. you cut-and-paste the original function body that exposes the error, 
 3. re-run the compiler on the modified output, to confirm you still see the bug -- but now you should get results faster and with much narrower debug output, since all the other function bodies are trivial.
 4., optional you iteratively delete all the other items that are now found to be no longer necessary since all of the function bodies are now just `loop { }`.

(Strictly speaking, there are bugs in the pretty printer and/or parser that make the process above not as seamless as described.  For example, it seems that `use super::{A, B};` can in some contexts be pretty-printed with whitespace separating `super` and `::` and `{A, B};`, which the parser then errors on.  But that is not an argument against the overall technique, which still appears pretty effective, though perhaps it would be even better if combined with macro-expansion in some way.)

----

Up front: I do believe this sort of local development hack should not want this option to be part of the stable public interface for `rustc`.  So while I could make a `-Z` flag just for this, I am worried that our `-Z` infrastructure is generally too weak to express the kinds of options I want to add (see e.g. #19892 where I use the hack of employing environment variables, when I really would have preferred to leverage `libgetopts`).

Thus, this PR incorporates what I believe to be a reasonable compromise: Add a single new `-Z` flag, `-Z unstable-options`, which expands the set of valid options to `rustc`.  This way, we still leverage all of the `getopts` infrastructure: for example, `rustc -Z unstable-options --help` prints out information about both the stable and unstable options (while `rustc --help` without the `-Z unstable-options` flag just prints out information about the stable options.  The main drawback that I can see is that processing such options is a little bit awkward, since you need to make sure that you check `sess.debugging_opt(config::UNSTABLE_OPTIONS)` before querying the `getopts` matches for the option in question.  But it really is not that bad; all of the ugliest stuff is paid for up front in this PR (e.g. the way I try to ensure that we do not waste time parsing the options twice in the common case where all the provided options are stable).

----

With `-Z unstable-options` in place, it is clear that pretty-print modes like `everybody_loops` belongs under an unstable option, as does the control flow graph visualizing pretty printer.  To deal with that, I added a new unstable-option, `--xpretty` and moved the latter two pretty print modes there.
  • Loading branch information
bors committed Dec 22, 2014
2 parents 34d6800 + bf2f84b commit 9381870
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 67 deletions.
175 changes: 130 additions & 45 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ use syntax::parse::token::InternedString;

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use getopts::{optopt, optmulti, optflag, optflagopt};
use getopts;
use std::cell::{RefCell};
use std::fmt;
Expand Down Expand Up @@ -278,7 +277,8 @@ debugging_opts! {
PRINT_REGION_GRAPH,
PARSE_ONLY,
NO_TRANS,
NO_ANALYSIS
NO_ANALYSIS,
UNSTABLE_OPTIONS
]
0
}
Expand Down Expand Up @@ -330,7 +330,8 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
("no-trans", "Run all passes except translation; no output", NO_TRANS),
("no-analysis", "Parse and expand the source, but run no analysis and",
NO_TRANS),
]
("unstable-options", "Adds unstable command line options to rustc interface",
UNSTABLE_OPTIONS)]
}

#[deriving(Clone)]
Expand Down Expand Up @@ -653,95 +654,179 @@ pub fn build_target_config(opts: &Options, sp: &SpanHandler) -> Config {
}
}

/// Returns the "short" subset of the stable rustc command line options.
pub fn short_optgroups() -> Vec<getopts::OptGroup> {
rustc_short_optgroups().into_iter()
.filter(|g|g.is_stable())
.map(|g|g.opt_group)
.collect()
}

/// Returns all of the stable rustc command line options.
pub fn optgroups() -> Vec<getopts::OptGroup> {
rustc_optgroups().into_iter()
.filter(|g|g.is_stable())
.map(|g|g.opt_group)
.collect()
}

#[deriving(Copy, Clone, PartialEq, Eq, Show)]
pub enum OptionStability { Stable, Unstable }

#[deriving(Clone, PartialEq, Eq)]
pub struct RustcOptGroup {
pub opt_group: getopts::OptGroup,
pub stability: OptionStability,
}

impl RustcOptGroup {
pub fn is_stable(&self) -> bool {
self.stability == OptionStability::Stable
}

fn stable(g: getopts::OptGroup) -> RustcOptGroup {
RustcOptGroup { opt_group: g, stability: OptionStability::Stable }
}

fn unstable(g: getopts::OptGroup) -> RustcOptGroup {
RustcOptGroup { opt_group: g, stability: OptionStability::Unstable }
}
}

// The `opt` local module holds wrappers around the `getopts` API that
// adds extra rustc-specific metadata to each option; such metadata
// is exposed by . The public
// functions below ending with `_u` are the functions that return
// *unstable* options, i.e. options that are only enabled when the
// user also passes the `-Z unstable-options` debugging flag.
mod opt {
// The `fn opt_u` etc below are written so that we can use them
// in the future; do not warn about them not being used right now.
#![allow(dead_code)]

use getopts;
use super::RustcOptGroup;

type R = RustcOptGroup;
type S<'a> = &'a str;

fn stable(g: getopts::OptGroup) -> R { RustcOptGroup::stable(g) }
fn unstable(g: getopts::OptGroup) -> R { RustcOptGroup::unstable(g) }

// FIXME (pnkfelix): We default to stable since the current set of
// options is defacto stable. However, it would be good to revise the
// code so that a stable option is the thing that takes extra effort
// to encode.

pub fn opt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optopt(a, b, c, d)) }
pub fn multi(a: S, b: S, c: S, d: S) -> R { stable(getopts::optmulti(a, b, c, d)) }
pub fn flag(a: S, b: S, c: S) -> R { stable(getopts::optflag(a, b, c)) }
pub fn flagopt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optflagopt(a, b, c, d)) }

pub fn opt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optopt(a, b, c, d)) }
pub fn multi_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optmulti(a, b, c, d)) }
pub fn flag_u(a: S, b: S, c: S) -> R { unstable(getopts::optflag(a, b, c)) }
pub fn flagopt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optflagopt(a, b, c, d)) }
}

/// Returns the "short" subset of the rustc command line options,
/// including metadata for each option, such as whether the option is
/// part of the stable long-term interface for rustc.
pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
vec![
optflag("h", "help", "Display this message"),
optmulti("", "cfg", "Configure the compilation environment", "SPEC"),
optmulti("L", "", "Add a directory to the library search path", "PATH"),
optmulti("l", "", "Link the generated crate(s) to the specified native
opt::flag("h", "help", "Display this message"),
opt::multi("", "cfg", "Configure the compilation environment", "SPEC"),
opt::multi("L", "", "Add a directory to the library search path", "PATH"),
opt::multi("l", "", "Link the generated crate(s) to the specified native
library NAME. The optional KIND can be one of,
static, dylib, or framework. If omitted, dylib is
assumed.", "NAME[:KIND]"),
optmulti("", "crate-type", "Comma separated list of types of crates
opt::multi("", "crate-type", "Comma separated list of types of crates
for the compiler to emit",
"[bin|lib|rlib|dylib|staticlib|dep-info]"),
optopt("", "crate-name", "Specify the name of the crate being built",
opt::opt("", "crate-name", "Specify the name of the crate being built",
"NAME"),
optmulti("", "emit", "Comma separated list of types of output for \
opt::multi("", "emit", "Comma separated list of types of output for \
the compiler to emit",
"[asm|llvm-bc|llvm-ir|obj|link]"),
optmulti("", "print", "Comma separated list of compiler information to \
opt::multi("", "print", "Comma separated list of compiler information to \
print on stdout",
"[crate-name|output-file-names|sysroot]"),
optflag("g", "", "Equivalent to --debuginfo=2"),
optflag("O", "", "Equivalent to --opt-level=2"),
optopt("o", "", "Write output to <filename>", "FILENAME"),
optopt("", "out-dir", "Write output to compiler-chosen filename \
opt::flag("g", "", "Equivalent to --debuginfo=2"),
opt::flag("O", "", "Equivalent to --opt-level=2"),
opt::opt("o", "", "Write output to <filename>", "FILENAME"),
opt::opt("", "out-dir", "Write output to compiler-chosen filename \
in <dir>", "DIR"),
optopt("", "explain", "Provide a detailed explanation of an error \
opt::opt("", "explain", "Provide a detailed explanation of an error \
message", "OPT"),
optflag("", "test", "Build a test harness"),
optopt("", "target", "Target triple cpu-manufacturer-kernel[-os] \
opt::flag("", "test", "Build a test harness"),
opt::opt("", "target", "Target triple cpu-manufacturer-kernel[-os] \
to compile for (see chapter 3.4 of \
http://www.sourceware.org/autobook/
for details)",
"TRIPLE"),
optmulti("W", "warn", "Set lint warnings", "OPT"),
optmulti("A", "allow", "Set lint allowed", "OPT"),
optmulti("D", "deny", "Set lint denied", "OPT"),
optmulti("F", "forbid", "Set lint forbidden", "OPT"),
optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
optflag("V", "version", "Print version info and exit"),
optflag("v", "verbose", "Use verbose output"),
opt::multi("W", "warn", "Set lint warnings", "OPT"),
opt::multi("A", "allow", "Set lint allowed", "OPT"),
opt::multi("D", "deny", "Set lint denied", "OPT"),
opt::multi("F", "forbid", "Set lint forbidden", "OPT"),
opt::multi("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
opt::flag("V", "version", "Print version info and exit"),
opt::flag("v", "verbose", "Use verbose output"),
]
}

// rustc command line options
pub fn optgroups() -> Vec<getopts::OptGroup> {
let mut opts = short_optgroups();
/// Returns all rustc command line options, including metadata for
/// each option, such as whether the option is part of the stable
/// long-term interface for rustc.
pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
let mut opts = rustc_short_optgroups();
opts.push_all(&[
optmulti("", "extern", "Specify where an external rust library is \
opt::multi("", "extern", "Specify where an external rust library is \
located",
"NAME=PATH"),
optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
optopt("", "sysroot", "Override the system root", "PATH"),
optmulti("Z", "", "Set internal debugging options", "FLAG"),
optopt("", "color", "Configure coloring of output:
opt::opt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
opt::opt("", "sysroot", "Override the system root", "PATH"),
opt::multi("Z", "", "Set internal debugging options", "FLAG"),
opt::opt("", "color", "Configure coloring of output:
auto = colorize, if output goes to a tty (default);
always = always colorize output;
never = never colorize output", "auto|always|never"),

// DEPRECATED
optflag("", "print-crate-name", "Output the crate name and exit"),
optflag("", "print-file-name", "Output the file(s) that would be \
opt::flag("", "print-crate-name", "Output the crate name and exit"),
opt::flag("", "print-file-name", "Output the file(s) that would be \
written if compilation \
continued and exit"),
optopt("", "debuginfo", "Emit DWARF debug info to the objects created:
opt::opt("", "debuginfo", "Emit DWARF debug info to the objects created:
0 = no debug info,
1 = line-tables only (for stacktraces and breakpoints),
2 = full debug info with variable and type information \
(same as -g)", "LEVEL"),
optflag("", "no-trans", "Run all passes except translation; no output"),
optflag("", "no-analysis", "Parse and expand the source, but run no \
opt::flag("", "no-trans", "Run all passes except translation; no output"),
opt::flag("", "no-analysis", "Parse and expand the source, but run no \
analysis and produce no output"),
optflag("", "parse-only", "Parse only; do not compile, assemble, \
opt::flag("", "parse-only", "Parse only; do not compile, assemble, \
or link"),
optflagopt("", "pretty",
opt::flagopt("", "pretty",
"Pretty-print the input instead of compiling;
valid types are: `normal` (un-annotated source),
`expanded` (crates expanded),
`typed` (crates expanded, with type annotations),
`expanded,identified` (fully parenthesized, AST nodes with IDs), or
`flowgraph=<nodeid>` (graphviz formatted flowgraph for node)",
`typed` (crates expanded, with type annotations), or
`expanded,identified` (fully parenthesized, AST nodes with IDs).",
"TYPE"),
optflagopt("", "dep-info",
opt::flagopt_u("", "xpretty",
"Pretty-print the input instead of compiling, unstable variants;
valid types are any of the types for `--pretty`, as well as:
`flowgraph=<nodeid>` (graphviz formatted flowgraph for node), or
`everybody_loops` (all function bodies replaced with `loop {}`).",
"TYPE"),
opt::flagopt("", "dep-info",
"Output dependency info to <filename> after compiling, \
in a format suitable for use by Makefiles", "FILENAME"),
]);
opts
}


// Convert strings provided as --cfg [cfgspec] into a crate_cfg
pub fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
cfgspecs.into_iter().map(|s| {
Expand Down
56 changes: 48 additions & 8 deletions src/librustc_driver/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,19 @@ fn run_compiler(args: &[String]) {
}

let pretty = matches.opt_default("pretty", "normal").map(|a| {
pretty::parse_pretty(&sess, a.as_slice())
// stable pretty-print variants only
pretty::parse_pretty(&sess, a.as_slice(), false)
});
let pretty = if pretty.is_none() &&
sess.debugging_opt(config::UNSTABLE_OPTIONS) {
matches.opt_str("xpretty").map(|a| {
// extended with unstable pretty-print variants
pretty::parse_pretty(&sess, a.as_slice(), true)
})
} else {
pretty
};

match pretty.into_iter().next() {
Some((ppm, opt_uii)) => {
pretty::pretty_print_input(sess, cfg, &input, ppm, opt_uii, ofile);
Expand Down Expand Up @@ -196,12 +207,16 @@ pub fn version(binary: &str, matches: &getopts::Matches) {
}
}

fn usage(verbose: bool) {
fn usage(verbose: bool, include_unstable_options: bool) {
let groups = if verbose {
config::optgroups()
config::rustc_optgroups()
} else {
config::short_optgroups()
config::rustc_short_optgroups()
};
let groups : Vec<_> = groups.into_iter()
.filter(|x| include_unstable_options || x.is_stable())
.map(|x|x.opt_group)
.collect();
let message = format!("Usage: rustc [OPTIONS] INPUT");
let extra_help = if verbose {
""
Expand Down Expand Up @@ -362,20 +377,45 @@ pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
let _binary = args.remove(0).unwrap();

if args.is_empty() {
usage(false);
// user did not write `-v` nor `-Z unstable-options`, so do not
// include that extra information.
usage(false, false);
return None;
}

let matches =
match getopts::getopts(args.as_slice(), config::optgroups().as_slice()) {
Ok(m) => m,
Err(f) => {
early_error(f.to_string().as_slice());
Err(f_stable_attempt) => {
// redo option parsing, including unstable options this time,
// in anticipation that the mishandled option was one of the
// unstable ones.
let all_groups : Vec<getopts::OptGroup>
= config::rustc_optgroups().into_iter().map(|x|x.opt_group).collect();
match getopts::getopts(args.as_slice(), all_groups.as_slice()) {
Ok(m_unstable) => {
let r = m_unstable.opt_strs("Z");
let include_unstable_options = r.iter().any(|x| *x == "unstable-options");
if include_unstable_options {
m_unstable
} else {
early_error(f_stable_attempt.to_string().as_slice());
}
}
Err(_) => {
// ignore the error from the unstable attempt; just
// pass the error we got from the first try.
early_error(f_stable_attempt.to_string().as_slice());
}
}
}
};

let r = matches.opt_strs("Z");
let include_unstable_options = r.iter().any(|x| *x == "unstable-options");

if matches.opt_present("h") || matches.opt_present("help") {
usage(matches.opt_present("verbose"));
usage(matches.opt_present("verbose"), include_unstable_options);
return None;
}

Expand Down
Loading

0 comments on commit 9381870

Please sign in to comment.