diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 0652645907bc8..e3bd2648588d6 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -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; @@ -278,7 +277,8 @@ debugging_opts! { PRINT_REGION_GRAPH, PARSE_ONLY, NO_TRANS, - NO_ANALYSIS + NO_ANALYSIS, + UNSTABLE_OPTIONS ] 0 } @@ -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)] @@ -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 { + 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 { + 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 { 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"), - 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"), + opt::opt("", "out-dir", "Write output to compiler-chosen filename \ in ", "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 { - 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 { + 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=` (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=` (graphviz formatted flowgraph for node), or + `everybody_loops` (all function bodies replaced with `loop {}`).", + "TYPE"), + opt::flagopt("", "dep-info", "Output dependency info to 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 ) -> ast::CrateConfig { cfgspecs.into_iter().map(|s| { diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 6944c733456f6..64f4d8bdc783f 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -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); @@ -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 { "" @@ -362,20 +377,45 @@ pub fn handle_options(mut args: Vec) -> Option { 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 + = 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; } diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 2eb9d2c67a7cb..3b6ad75243e22 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -30,7 +30,10 @@ use rustc_borrowck::graphviz as borrowck_dot; use syntax::ast; use syntax::ast_map::{mod, blocks, NodePrinter}; +use syntax::codemap; +use syntax::fold::{mod, Folder}; use syntax::print::{pp, pprust}; +use syntax::ptr::P; use graphviz as dot; @@ -42,6 +45,7 @@ use arena::TypedArena; #[deriving(Copy, PartialEq, Show)] pub enum PpSourceMode { PpmNormal, + PpmEveryBodyLoops, PpmExpanded, PpmTyped, PpmIdentified, @@ -55,23 +59,33 @@ pub enum PpMode { PpmFlowGraph, } -pub fn parse_pretty(sess: &Session, name: &str) -> (PpMode, Option) { +pub fn parse_pretty(sess: &Session, + name: &str, + extended: bool) -> (PpMode, Option) { let mut split = name.splitn(1, '='); let first = split.next().unwrap(); let opt_second = split.next(); - let first = match first { - "normal" => PpmSource(PpmNormal), - "expanded" => PpmSource(PpmExpanded), - "typed" => PpmSource(PpmTyped), - "expanded,identified" => PpmSource(PpmExpandedIdentified), - "expanded,hygiene" => PpmSource(PpmExpandedHygiene), - "identified" => PpmSource(PpmIdentified), - "flowgraph" => PpmFlowGraph, + let first = match (first, extended) { + ("normal", _) => PpmSource(PpmNormal), + ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops), + ("expanded", _) => PpmSource(PpmExpanded), + ("typed", _) => PpmSource(PpmTyped), + ("expanded,identified", _) => PpmSource(PpmExpandedIdentified), + ("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene), + ("identified", _) => PpmSource(PpmIdentified), + ("flowgraph", true) => PpmFlowGraph, _ => { - sess.fatal(format!( - "argument to `pretty` must be one of `normal`, \ - `expanded`, `flowgraph=`, `typed`, `identified`, \ - or `expanded,identified`; got {}", name).as_slice()); + if extended { + sess.fatal(format!( + "argument to `xpretty` must be one of `normal`, \ + `expanded`, `flowgraph=`, `typed`, `identified`, \ + `expanded,identified`, or `everybody_loops`; got {}", name).as_slice()); + } else { + sess.fatal(format!( + "argument to `pretty` must be one of `normal`, \ + `expanded`, `typed`, `identified`, \ + or `expanded,identified`; got {}", name).as_slice()); + } } }; let opt_second = opt_second.and_then::(from_str); @@ -105,7 +119,7 @@ impl PpSourceMode { F: FnOnce(&PrinterSupport, B) -> A, { match *self { - PpmNormal | PpmExpanded => { + PpmNormal | PpmEveryBodyLoops | PpmExpanded => { let annotation = NoAnn { sess: sess, ast_map: ast_map }; f(&annotation, payload) } @@ -384,6 +398,7 @@ impl UserIdentifiedItem { fn needs_ast_map(ppm: &PpMode, opt_uii: &Option) -> bool { match *ppm { PpmSource(PpmNormal) | + PpmSource(PpmEveryBodyLoops) | PpmSource(PpmIdentified) => opt_uii.is_some(), PpmSource(PpmExpanded) | @@ -397,6 +412,7 @@ fn needs_ast_map(ppm: &PpMode, opt_uii: &Option) -> bool { fn needs_expansion(ppm: &PpMode) -> bool { match *ppm { PpmSource(PpmNormal) | + PpmSource(PpmEveryBodyLoops) | PpmSource(PpmIdentified) => false, PpmSource(PpmExpanded) | @@ -407,6 +423,64 @@ fn needs_expansion(ppm: &PpMode) -> bool { } } +struct ReplaceBodyWithLoop { + within_static_or_const: bool, +} + +impl ReplaceBodyWithLoop { + fn new() -> ReplaceBodyWithLoop { + ReplaceBodyWithLoop { within_static_or_const: false } + } +} + +impl fold::Folder for ReplaceBodyWithLoop { + fn fold_item_underscore(&mut self, i: ast::Item_) -> ast::Item_ { + match i { + ast::ItemStatic(..) | ast::ItemConst(..) => { + self.within_static_or_const = true; + let ret = fold::noop_fold_item_underscore(i, self); + self.within_static_or_const = false; + return ret; + } + _ => { + fold::noop_fold_item_underscore(i, self) + } + } + } + + + fn fold_block(&mut self, b: P) -> P { + fn expr_to_block(rules: ast::BlockCheckMode, + e: Option>) -> P { + P(ast::Block { + expr: e, + view_items: vec![], stmts: vec![], rules: rules, + id: ast::DUMMY_NODE_ID, span: codemap::DUMMY_SP, + }) + } + + if !self.within_static_or_const { + + let empty_block = expr_to_block(ast::DefaultBlock, None); + let loop_expr = P(ast::Expr { + node: ast::ExprLoop(empty_block, None), + id: ast::DUMMY_NODE_ID, span: codemap::DUMMY_SP + }); + + expr_to_block(b.rules, Some(loop_expr)) + + } else { + fold::noop_fold_block(b, self) + } + } + + // in general the pretty printer processes unexpanded code, so + // we override the default `fold_mac` method which panics. + fn fold_mac(&mut self, _macro: ast::Mac) -> ast::Mac { + fold::noop_fold_mac(_macro, self) + } +} + pub fn pretty_print_input(sess: Session, cfg: ast::CrateConfig, input: &Input, @@ -414,6 +488,14 @@ pub fn pretty_print_input(sess: Session, opt_uii: Option, ofile: Option) { let krate = driver::phase_1_parse_input(&sess, cfg, input); + + let krate = if let PpmSource(PpmEveryBodyLoops) = ppm { + let mut fold = ReplaceBodyWithLoop::new(); + fold.fold_crate(krate) + } else { + krate + }; + let id = link::find_crate_name(Some(&sess), krate.attrs.as_slice(), input); let is_expanded = needs_expansion(&ppm); diff --git a/src/test/run-make/graphviz-flowgraph/Makefile b/src/test/run-make/graphviz-flowgraph/Makefile index 09440949177dd..0562e000e56e9 100644 --- a/src/test/run-make/graphviz-flowgraph/Makefile +++ b/src/test/run-make/graphviz-flowgraph/Makefile @@ -28,7 +28,7 @@ $(TMPDIR)/%.pp: %.rs $(TMPDIR)/%.dot: %.rs $(eval $(call FIND_LAST_BLOCK,$<)) - $(RUSTC_LIB) --pretty flowgraph=$(LASTBLOCKNUM_$<) $< -o $@.tmp + $(RUSTC_LIB) -Z unstable-options --xpretty flowgraph=$(LASTBLOCKNUM_$<) $< -o $@.tmp cat $@.tmp | sed -e 's@ (id=[0-9]*)@@g' \ -e 's@\[label=""\]@@' \ -e 's@digraph [a-zA-Z0-9_]* @digraph block @' \