Skip to content

Commit 9381870

Browse files
committed
auto merge of #19964 : pnkfelix/rust/everybody-loops-pprint, r=alexcrichton
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.
2 parents 34d6800 + bf2f84b commit 9381870

File tree

3 files changed

+274
-67
lines changed

3 files changed

+274
-67
lines changed

src/librustc/session/config.rs

+130-45
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ use syntax::parse::token::InternedString;
3434

3535
use std::collections::HashMap;
3636
use std::collections::hash_map::Entry::{Occupied, Vacant};
37-
use getopts::{optopt, optmulti, optflag, optflagopt};
3837
use getopts;
3938
use std::cell::{RefCell};
4039
use std::fmt;
@@ -278,7 +277,8 @@ debugging_opts! {
278277
PRINT_REGION_GRAPH,
279278
PARSE_ONLY,
280279
NO_TRANS,
281-
NO_ANALYSIS
280+
NO_ANALYSIS,
281+
UNSTABLE_OPTIONS
282282
]
283283
0
284284
}
@@ -330,7 +330,8 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
330330
("no-trans", "Run all passes except translation; no output", NO_TRANS),
331331
("no-analysis", "Parse and expand the source, but run no analysis and",
332332
NO_TRANS),
333-
]
333+
("unstable-options", "Adds unstable command line options to rustc interface",
334+
UNSTABLE_OPTIONS)]
334335
}
335336

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

657+
/// Returns the "short" subset of the stable rustc command line options.
656658
pub fn short_optgroups() -> Vec<getopts::OptGroup> {
659+
rustc_short_optgroups().into_iter()
660+
.filter(|g|g.is_stable())
661+
.map(|g|g.opt_group)
662+
.collect()
663+
}
664+
665+
/// Returns all of the stable rustc command line options.
666+
pub fn optgroups() -> Vec<getopts::OptGroup> {
667+
rustc_optgroups().into_iter()
668+
.filter(|g|g.is_stable())
669+
.map(|g|g.opt_group)
670+
.collect()
671+
}
672+
673+
#[deriving(Copy, Clone, PartialEq, Eq, Show)]
674+
pub enum OptionStability { Stable, Unstable }
675+
676+
#[deriving(Clone, PartialEq, Eq)]
677+
pub struct RustcOptGroup {
678+
pub opt_group: getopts::OptGroup,
679+
pub stability: OptionStability,
680+
}
681+
682+
impl RustcOptGroup {
683+
pub fn is_stable(&self) -> bool {
684+
self.stability == OptionStability::Stable
685+
}
686+
687+
fn stable(g: getopts::OptGroup) -> RustcOptGroup {
688+
RustcOptGroup { opt_group: g, stability: OptionStability::Stable }
689+
}
690+
691+
fn unstable(g: getopts::OptGroup) -> RustcOptGroup {
692+
RustcOptGroup { opt_group: g, stability: OptionStability::Unstable }
693+
}
694+
}
695+
696+
// The `opt` local module holds wrappers around the `getopts` API that
697+
// adds extra rustc-specific metadata to each option; such metadata
698+
// is exposed by . The public
699+
// functions below ending with `_u` are the functions that return
700+
// *unstable* options, i.e. options that are only enabled when the
701+
// user also passes the `-Z unstable-options` debugging flag.
702+
mod opt {
703+
// The `fn opt_u` etc below are written so that we can use them
704+
// in the future; do not warn about them not being used right now.
705+
#![allow(dead_code)]
706+
707+
use getopts;
708+
use super::RustcOptGroup;
709+
710+
type R = RustcOptGroup;
711+
type S<'a> = &'a str;
712+
713+
fn stable(g: getopts::OptGroup) -> R { RustcOptGroup::stable(g) }
714+
fn unstable(g: getopts::OptGroup) -> R { RustcOptGroup::unstable(g) }
715+
716+
// FIXME (pnkfelix): We default to stable since the current set of
717+
// options is defacto stable. However, it would be good to revise the
718+
// code so that a stable option is the thing that takes extra effort
719+
// to encode.
720+
721+
pub fn opt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optopt(a, b, c, d)) }
722+
pub fn multi(a: S, b: S, c: S, d: S) -> R { stable(getopts::optmulti(a, b, c, d)) }
723+
pub fn flag(a: S, b: S, c: S) -> R { stable(getopts::optflag(a, b, c)) }
724+
pub fn flagopt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optflagopt(a, b, c, d)) }
725+
726+
pub fn opt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optopt(a, b, c, d)) }
727+
pub fn multi_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optmulti(a, b, c, d)) }
728+
pub fn flag_u(a: S, b: S, c: S) -> R { unstable(getopts::optflag(a, b, c)) }
729+
pub fn flagopt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optflagopt(a, b, c, d)) }
730+
}
731+
732+
/// Returns the "short" subset of the rustc command line options,
733+
/// including metadata for each option, such as whether the option is
734+
/// part of the stable long-term interface for rustc.
735+
pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
657736
vec![
658-
optflag("h", "help", "Display this message"),
659-
optmulti("", "cfg", "Configure the compilation environment", "SPEC"),
660-
optmulti("L", "", "Add a directory to the library search path", "PATH"),
661-
optmulti("l", "", "Link the generated crate(s) to the specified native
737+
opt::flag("h", "help", "Display this message"),
738+
opt::multi("", "cfg", "Configure the compilation environment", "SPEC"),
739+
opt::multi("L", "", "Add a directory to the library search path", "PATH"),
740+
opt::multi("l", "", "Link the generated crate(s) to the specified native
662741
library NAME. The optional KIND can be one of,
663742
static, dylib, or framework. If omitted, dylib is
664743
assumed.", "NAME[:KIND]"),
665-
optmulti("", "crate-type", "Comma separated list of types of crates
744+
opt::multi("", "crate-type", "Comma separated list of types of crates
666745
for the compiler to emit",
667746
"[bin|lib|rlib|dylib|staticlib|dep-info]"),
668-
optopt("", "crate-name", "Specify the name of the crate being built",
747+
opt::opt("", "crate-name", "Specify the name of the crate being built",
669748
"NAME"),
670-
optmulti("", "emit", "Comma separated list of types of output for \
749+
opt::multi("", "emit", "Comma separated list of types of output for \
671750
the compiler to emit",
672751
"[asm|llvm-bc|llvm-ir|obj|link]"),
673-
optmulti("", "print", "Comma separated list of compiler information to \
752+
opt::multi("", "print", "Comma separated list of compiler information to \
674753
print on stdout",
675754
"[crate-name|output-file-names|sysroot]"),
676-
optflag("g", "", "Equivalent to --debuginfo=2"),
677-
optflag("O", "", "Equivalent to --opt-level=2"),
678-
optopt("o", "", "Write output to <filename>", "FILENAME"),
679-
optopt("", "out-dir", "Write output to compiler-chosen filename \
755+
opt::flag("g", "", "Equivalent to --debuginfo=2"),
756+
opt::flag("O", "", "Equivalent to --opt-level=2"),
757+
opt::opt("o", "", "Write output to <filename>", "FILENAME"),
758+
opt::opt("", "out-dir", "Write output to compiler-chosen filename \
680759
in <dir>", "DIR"),
681-
optopt("", "explain", "Provide a detailed explanation of an error \
760+
opt::opt("", "explain", "Provide a detailed explanation of an error \
682761
message", "OPT"),
683-
optflag("", "test", "Build a test harness"),
684-
optopt("", "target", "Target triple cpu-manufacturer-kernel[-os] \
762+
opt::flag("", "test", "Build a test harness"),
763+
opt::opt("", "target", "Target triple cpu-manufacturer-kernel[-os] \
685764
to compile for (see chapter 3.4 of \
686765
http://www.sourceware.org/autobook/
687766
for details)",
688767
"TRIPLE"),
689-
optmulti("W", "warn", "Set lint warnings", "OPT"),
690-
optmulti("A", "allow", "Set lint allowed", "OPT"),
691-
optmulti("D", "deny", "Set lint denied", "OPT"),
692-
optmulti("F", "forbid", "Set lint forbidden", "OPT"),
693-
optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
694-
optflag("V", "version", "Print version info and exit"),
695-
optflag("v", "verbose", "Use verbose output"),
768+
opt::multi("W", "warn", "Set lint warnings", "OPT"),
769+
opt::multi("A", "allow", "Set lint allowed", "OPT"),
770+
opt::multi("D", "deny", "Set lint denied", "OPT"),
771+
opt::multi("F", "forbid", "Set lint forbidden", "OPT"),
772+
opt::multi("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
773+
opt::flag("V", "version", "Print version info and exit"),
774+
opt::flag("v", "verbose", "Use verbose output"),
696775
]
697776
}
698777

699-
// rustc command line options
700-
pub fn optgroups() -> Vec<getopts::OptGroup> {
701-
let mut opts = short_optgroups();
778+
/// Returns all rustc command line options, including metadata for
779+
/// each option, such as whether the option is part of the stable
780+
/// long-term interface for rustc.
781+
pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
782+
let mut opts = rustc_short_optgroups();
702783
opts.push_all(&[
703-
optmulti("", "extern", "Specify where an external rust library is \
784+
opt::multi("", "extern", "Specify where an external rust library is \
704785
located",
705786
"NAME=PATH"),
706-
optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
707-
optopt("", "sysroot", "Override the system root", "PATH"),
708-
optmulti("Z", "", "Set internal debugging options", "FLAG"),
709-
optopt("", "color", "Configure coloring of output:
787+
opt::opt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
788+
opt::opt("", "sysroot", "Override the system root", "PATH"),
789+
opt::multi("Z", "", "Set internal debugging options", "FLAG"),
790+
opt::opt("", "color", "Configure coloring of output:
710791
auto = colorize, if output goes to a tty (default);
711792
always = always colorize output;
712793
never = never colorize output", "auto|always|never"),
713794

714795
// DEPRECATED
715-
optflag("", "print-crate-name", "Output the crate name and exit"),
716-
optflag("", "print-file-name", "Output the file(s) that would be \
796+
opt::flag("", "print-crate-name", "Output the crate name and exit"),
797+
opt::flag("", "print-file-name", "Output the file(s) that would be \
717798
written if compilation \
718799
continued and exit"),
719-
optopt("", "debuginfo", "Emit DWARF debug info to the objects created:
800+
opt::opt("", "debuginfo", "Emit DWARF debug info to the objects created:
720801
0 = no debug info,
721802
1 = line-tables only (for stacktraces and breakpoints),
722803
2 = full debug info with variable and type information \
723804
(same as -g)", "LEVEL"),
724-
optflag("", "no-trans", "Run all passes except translation; no output"),
725-
optflag("", "no-analysis", "Parse and expand the source, but run no \
805+
opt::flag("", "no-trans", "Run all passes except translation; no output"),
806+
opt::flag("", "no-analysis", "Parse and expand the source, but run no \
726807
analysis and produce no output"),
727-
optflag("", "parse-only", "Parse only; do not compile, assemble, \
808+
opt::flag("", "parse-only", "Parse only; do not compile, assemble, \
728809
or link"),
729-
optflagopt("", "pretty",
810+
opt::flagopt("", "pretty",
730811
"Pretty-print the input instead of compiling;
731812
valid types are: `normal` (un-annotated source),
732813
`expanded` (crates expanded),
733-
`typed` (crates expanded, with type annotations),
734-
`expanded,identified` (fully parenthesized, AST nodes with IDs), or
735-
`flowgraph=<nodeid>` (graphviz formatted flowgraph for node)",
814+
`typed` (crates expanded, with type annotations), or
815+
`expanded,identified` (fully parenthesized, AST nodes with IDs).",
736816
"TYPE"),
737-
optflagopt("", "dep-info",
817+
opt::flagopt_u("", "xpretty",
818+
"Pretty-print the input instead of compiling, unstable variants;
819+
valid types are any of the types for `--pretty`, as well as:
820+
`flowgraph=<nodeid>` (graphviz formatted flowgraph for node), or
821+
`everybody_loops` (all function bodies replaced with `loop {}`).",
822+
"TYPE"),
823+
opt::flagopt("", "dep-info",
738824
"Output dependency info to <filename> after compiling, \
739825
in a format suitable for use by Makefiles", "FILENAME"),
740826
]);
741827
opts
742828
}
743829

744-
745830
// Convert strings provided as --cfg [cfgspec] into a crate_cfg
746831
pub fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
747832
cfgspecs.into_iter().map(|s| {

src/librustc_driver/lib.rs

+48-8
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,19 @@ fn run_compiler(args: &[String]) {
138138
}
139139

140140
let pretty = matches.opt_default("pretty", "normal").map(|a| {
141-
pretty::parse_pretty(&sess, a.as_slice())
141+
// stable pretty-print variants only
142+
pretty::parse_pretty(&sess, a.as_slice(), false)
142143
});
144+
let pretty = if pretty.is_none() &&
145+
sess.debugging_opt(config::UNSTABLE_OPTIONS) {
146+
matches.opt_str("xpretty").map(|a| {
147+
// extended with unstable pretty-print variants
148+
pretty::parse_pretty(&sess, a.as_slice(), true)
149+
})
150+
} else {
151+
pretty
152+
};
153+
143154
match pretty.into_iter().next() {
144155
Some((ppm, opt_uii)) => {
145156
pretty::pretty_print_input(sess, cfg, &input, ppm, opt_uii, ofile);
@@ -196,12 +207,16 @@ pub fn version(binary: &str, matches: &getopts::Matches) {
196207
}
197208
}
198209

199-
fn usage(verbose: bool) {
210+
fn usage(verbose: bool, include_unstable_options: bool) {
200211
let groups = if verbose {
201-
config::optgroups()
212+
config::rustc_optgroups()
202213
} else {
203-
config::short_optgroups()
214+
config::rustc_short_optgroups()
204215
};
216+
let groups : Vec<_> = groups.into_iter()
217+
.filter(|x| include_unstable_options || x.is_stable())
218+
.map(|x|x.opt_group)
219+
.collect();
205220
let message = format!("Usage: rustc [OPTIONS] INPUT");
206221
let extra_help = if verbose {
207222
""
@@ -362,20 +377,45 @@ pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
362377
let _binary = args.remove(0).unwrap();
363378

364379
if args.is_empty() {
365-
usage(false);
380+
// user did not write `-v` nor `-Z unstable-options`, so do not
381+
// include that extra information.
382+
usage(false, false);
366383
return None;
367384
}
368385

369386
let matches =
370387
match getopts::getopts(args.as_slice(), config::optgroups().as_slice()) {
371388
Ok(m) => m,
372-
Err(f) => {
373-
early_error(f.to_string().as_slice());
389+
Err(f_stable_attempt) => {
390+
// redo option parsing, including unstable options this time,
391+
// in anticipation that the mishandled option was one of the
392+
// unstable ones.
393+
let all_groups : Vec<getopts::OptGroup>
394+
= config::rustc_optgroups().into_iter().map(|x|x.opt_group).collect();
395+
match getopts::getopts(args.as_slice(), all_groups.as_slice()) {
396+
Ok(m_unstable) => {
397+
let r = m_unstable.opt_strs("Z");
398+
let include_unstable_options = r.iter().any(|x| *x == "unstable-options");
399+
if include_unstable_options {
400+
m_unstable
401+
} else {
402+
early_error(f_stable_attempt.to_string().as_slice());
403+
}
404+
}
405+
Err(_) => {
406+
// ignore the error from the unstable attempt; just
407+
// pass the error we got from the first try.
408+
early_error(f_stable_attempt.to_string().as_slice());
409+
}
410+
}
374411
}
375412
};
376413

414+
let r = matches.opt_strs("Z");
415+
let include_unstable_options = r.iter().any(|x| *x == "unstable-options");
416+
377417
if matches.opt_present("h") || matches.opt_present("help") {
378-
usage(matches.opt_present("verbose"));
418+
usage(matches.opt_present("verbose"), include_unstable_options);
379419
return None;
380420
}
381421

0 commit comments

Comments
 (0)