]> git.lizzy.rs Git - rust.git/commitdiff
Store the registered lints in the Session
authorKeegan McAllister <kmcallister@mozilla.com>
Tue, 10 Jun 2014 21:03:19 +0000 (14:03 -0700)
committerKeegan McAllister <kmcallister@mozilla.com>
Tue, 24 Jun 2014 18:36:27 +0000 (11:36 -0700)
src/librustc/driver/driver.rs
src/librustc/driver/mod.rs
src/librustc/driver/session.rs
src/librustc/lint/builtin.rs
src/librustc/lint/mod.rs
src/librustc/middle/typeck/infer/test.rs
src/librustdoc/core.rs
src/librustdoc/test.rs

index 2aa2746959fcbbd037f3103380de4391070da751..9a36ea1e65cdbbf991d0fb018901d918b7104d88 100644 (file)
@@ -79,8 +79,12 @@ pub fn compile_input(sess: Session,
                                                  &sess);
             let id = link::find_crate_id(krate.attrs.as_slice(),
                                          outputs.out_filestem.as_slice());
-            let (expanded_crate, ast_map) =
-                phase_2_configure_and_expand(&sess, krate, &id);
+            let (expanded_crate, ast_map)
+                = match phase_2_configure_and_expand(&sess, krate, &id) {
+                    None => return,
+                    Some(p) => p,
+                };
+
             (outputs, expanded_crate, ast_map)
         };
         write_out_deps(&sess, input, &outputs, &expanded_crate);
@@ -173,10 +177,12 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
 /// syntax expansion, secondary `cfg` expansion, synthesis of a test
 /// harness if one is to be provided and injection of a dependency on the
 /// standard library and prelude.
+///
+/// Returns `None` if we're aborting after handling -W help.
 pub fn phase_2_configure_and_expand(sess: &Session,
                                     mut krate: ast::Crate,
                                     crate_id: &CrateId)
-                                    -> (ast::Crate, syntax::ast_map::Map) {
+                                    -> Option<(ast::Crate, syntax::ast_map::Map)> {
     let time_passes = sess.time_passes();
 
     *sess.crate_types.borrow_mut() = collect_crate_types(sess, krate.attrs.as_slice());
@@ -212,6 +218,17 @@ pub fn phase_2_configure_and_expand(sess: &Session,
 
     let Registry { syntax_exts, .. } = registry;
 
+    // Process command line flags for lints.
+    // Do this here because we will have lint plugins eventually.
+    if sess.opts.describe_lints {
+        super::describe_lints(&*sess.lint_store.borrow());
+        return None;
+    }
+    sess.lint_store.borrow_mut().process_command_line(sess);
+
+    // Abort if there are errors from lint processing or a plugin registrar.
+    sess.abort_if_errors();
+
     krate = time(time_passes, "expansion", (krate, macros, syntax_exts),
         |(krate, macros, syntax_exts)| {
             // Windows dlls do not have rpaths, so they don't know how to find their
@@ -254,7 +271,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         krate.encode(&mut json).unwrap();
     }
 
-    (krate, map)
+    Some((krate, map))
 }
 
 pub struct CrateAnalysis {
@@ -631,9 +648,11 @@ pub fn pretty_print_input(sess: Session,
 
     let (krate, ast_map, is_expanded) = match ppm {
         PpmExpanded | PpmExpandedIdentified | PpmTyped | PpmFlowGraph(_) => {
-            let (krate, ast_map) = phase_2_configure_and_expand(&sess,
-                                                                krate,
-                                                                &id);
+            let (krate, ast_map)
+                = match phase_2_configure_and_expand(&sess, krate, &id) {
+                    None => return,
+                    Some(p) => p,
+                };
             (krate, Some(ast_map), true)
         }
         _ => (krate, None, false)
index 623698f6c71ae7a596bb0132f0db8e303e5e7594..7c8f5a90b5a80a4cad53dec0bafb0f91f89993a0 100644 (file)
@@ -13,6 +13,7 @@
 use back::link;
 use driver::driver::{Input, FileInput, StrInput};
 use driver::session::{Session, build_session};
+use lint::Lint;
 use lint;
 use metadata;
 
@@ -48,15 +49,18 @@ fn run_compiler(args: &[String]) {
         Some(matches) => matches,
         None => return
     };
-
     let sopts = config::build_session_options(&matches);
-    if sopts.describe_lints {
-        describe_lints();
-        return;
-    }
 
     let (input, input_file_path) = match matches.free.len() {
-        0u => early_error("no input filename given"),
+        0u => {
+            if sopts.describe_lints {
+                let mut ls = lint::LintStore::new();
+                ls.register_builtin(None);
+                describe_lints(&ls);
+                return;
+            }
+            early_error("no input filename given");
+        }
         1u => {
             let ifile = matches.free.get(0).as_slice();
             if ifile == "-" {
@@ -128,43 +132,56 @@ fn usage() {
                              config::optgroups().as_slice()));
 }
 
-fn describe_lints() {
+fn describe_lints(lint_store: &lint::LintStore) {
     println!("
 Available lint options:
     -W <foo>           Warn about <foo>
     -A <foo>           Allow <foo>
     -D <foo>           Deny <foo>
     -F <foo>           Forbid <foo> (deny, and deny all overrides)
+
 ");
 
-    let mut builtin_specs = lint::builtin_lint_specs();
-    builtin_specs.sort_by(|x, y| {
-        match x.default_level.cmp(&y.default_level) {
-            Equal => x.name.cmp(&y.name),
-            r => r,
-        }
-    });
+    fn sort_lints(lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> {
+        let mut lints: Vec<_> = lints.move_iter().map(|(x, _)| x).collect();
+        lints.sort_by(|x: &&Lint, y: &&Lint| {
+            match x.default_level.cmp(&y.default_level) {
+                Equal => x.name.cmp(&y.name),
+                r => r,
+            }
+        });
+        lints
+    }
 
-    // FIXME: What if someone uses combining characters or East Asian fullwidth
-    // characters in a lint name?!?!?
-    let max_name_len = builtin_specs.iter()
+    let (_plugin, builtin) = lint_store.get_lints().partitioned(|&(_, p)| p);
+    // let plugin = sort_lints(plugin);
+    let builtin = sort_lints(builtin);
+
+    // FIXME (#7043): We should use the width in character cells rather than
+    // the number of codepoints.
+    let max_name_len = builtin.iter()
         .map(|&s| s.name.char_len())
         .max().unwrap_or(0);
     let padded = |x: &str| {
-        format!("{}{}", " ".repeat(max_name_len - x.char_len()), x)
+        " ".repeat(max_name_len - x.char_len()).append(x)
     };
 
-    println!("\nAvailable lint checks:\n");
+    println!("Lint checks provided by rustc:\n");
     println!("    {}  {:7.7s}  {}", padded("name"), "default", "meaning");
     println!("    {}  {:7.7s}  {}", padded("----"), "-------", "-------");
-    println!("");
 
-    for spec in builtin_specs.move_iter() {
-        let name = spec.name.replace("_", "-");
-        println!("    {}  {:7.7s}  {}",
-            padded(name.as_slice()), spec.default_level.as_str(), spec.desc);
-    }
-    println!("");
+    let print_lints = |lints: Vec<&Lint>| {
+        for lint in lints.move_iter() {
+            let name = lint.name.replace("_", "-");
+            println!("    {}  {:7.7s}  {}",
+                padded(name.as_slice()), lint.default_level.as_str(), lint.desc);
+        }
+        println!("\n");
+    };
+
+    print_lints(builtin);
+
+    // Describe lint plugins here once they exist.
 }
 
 fn describe_debug_flags() {
index 7f20059b65703eb778f0e7b5eb96e4a799207301..07366f34c4e03d7018c1821d408645025f59ccee 100644 (file)
@@ -43,6 +43,7 @@ pub struct Session {
     // expected to be absolute. `None` means that there is no source file.
     pub local_crate_source_file: Option<Path>,
     pub working_dir: Path,
+    pub lint_store: RefCell<lint::LintStore>,
     pub lints: RefCell<NodeMap<Vec<(lint::LintId, codemap::Span, String)>>>,
     pub node_id: Cell<ast::NodeId>,
     pub crate_types: RefCell<Vec<config::CrateType>>,
@@ -226,7 +227,7 @@ pub fn build_session_(sopts: config::Options,
         }
     );
 
-    Session {
+    let sess = Session {
         targ_cfg: target_cfg,
         opts: sopts,
         cstore: CStore::new(token::get_ident_interner()),
@@ -238,12 +239,16 @@ pub fn build_session_(sopts: config::Options,
         default_sysroot: default_sysroot,
         local_crate_source_file: local_crate_source_file,
         working_dir: os::getcwd(),
+        lint_store: RefCell::new(lint::LintStore::new()),
         lints: RefCell::new(NodeMap::new()),
         node_id: Cell::new(1),
         crate_types: RefCell::new(Vec::new()),
         features: front::feature_gate::Features::new(),
         recursion_limit: Cell::new(64),
-    }
+    };
+
+    sess.lint_store.borrow_mut().register_builtin(Some(&sess));
+    sess
 }
 
 // Seems out of place, but it uses session, so I'm putting it here
index 7fbefbf94160d1f0fd1d6d0aa37d1305a980055e..83a0c22dec19a719c5a5f25fc2297d55cdbb1fa3 100644 (file)
@@ -1517,7 +1517,7 @@ fn check_item(&mut self, cx: &Context, it: &ast::Item) {
         match it.node {
             ast::ItemEnum(..) => {
                 let lint_id = lint::LintId::of(variant_size_difference);
-                match cx.get_level_source(lint_id) {
+                match cx.lints.get_level_source(lint_id) {
                     lvlsrc @ (lvl, _) if lvl != lint::Allow => {
                         cx.insert_node_level(it.id, lint_id, lvlsrc);
                     },
index 1664dd6f309f8b7c532f9f50a40641892f66ebee..c481c5081d173f9cb56c7adc67de404d79466cce 100644 (file)
@@ -48,6 +48,7 @@
 use middle::typeck::astconv::AstConv;
 use middle::typeck::infer;
 use driver::session::Session;
+use driver::early_error;
 
 use std::collections::HashMap;
 use std::rc::Rc;
@@ -58,6 +59,7 @@
 use std::hash::Hash;
 use std::tuple::Tuple2;
 use std::hash;
+use std::mem;
 use syntax::ast_util::IdVisitingOperation;
 use syntax::attr::AttrMetaMethods;
 use syntax::attr;
@@ -115,7 +117,7 @@ pub struct Lint {
     pub desc: &'static str,
 }
 
-type LintArray = &'static [&'static Lint];
+pub type LintArray = &'static [&'static Lint];
 
 /// Trait for types providing lint checks. Each `check` method checks a single
 /// syntax node, and should not invoke methods recursively (unlike `Visitor`).
@@ -123,7 +125,7 @@ pub struct Lint {
 //
 // FIXME: eliminate the duplication with `Visitor`. But this also
 // contains a few lint-specific methods with no equivalent in `Visitor`.
-trait LintPass {
+pub trait LintPass {
     /// Get descriptions of the lints this `LintPass` object can emit.
     ///
     /// NB: there is no enforcement that the object only emits lints it registered.
@@ -246,15 +248,117 @@ pub enum LintSource {
 
 pub type LevelSource = (Level, LintSource);
 
-struct Context<'a> {
+/// Information about the registered lints.
+/// This is basically the subset of `Context` that we can
+/// build early in the compile pipeline.
+pub struct LintStore {
+    /// Registered lints. The bool is true if the lint was
+    /// added by a plugin.
+    lints: Vec<(&'static Lint, bool)>,
+
     /// Trait objects for each lint pass.
-    lint_objects: Vec<RefCell<LintPassObject>>,
+    passes: Vec<RefCell<LintPassObject>>,
 
     /// Lints indexed by name.
-    lints_by_name: HashMap<&'static str, LintId>,
+    by_name: HashMap<&'static str, LintId>,
 
     /// Current levels of each lint, and where they were set.
     levels: HashMap<LintId, LevelSource>,
+}
+
+impl LintStore {
+    fn get_level_source(&self, lint: LintId) -> LevelSource {
+        match self.levels.find(&lint) {
+            Some(&s) => s,
+            None => (Allow, Default),
+        }
+    }
+
+    fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) {
+        if lvlsrc.val0() == Allow {
+            self.levels.remove(&lint);
+        } else {
+            self.levels.insert(lint, lvlsrc);
+        }
+    }
+
+    pub fn new() -> LintStore {
+        LintStore {
+            lints: vec!(),
+            passes: vec!(),
+            by_name: HashMap::new(),
+            levels: HashMap::new(),
+        }
+    }
+
+    pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] {
+        self.lints.as_slice()
+    }
+
+    pub fn register_pass(&mut self, sess: Option<&Session>,
+                         from_plugin: bool, pass: LintPassObject) {
+        for &lint in pass.get_lints().iter() {
+            self.lints.push((lint, from_plugin));
+
+            let id = LintId::of(lint);
+            if !self.by_name.insert(lint.name, id) {
+                let msg = format!("duplicate specification of lint {}", lint.name);
+                match (sess, from_plugin) {
+                    // We load builtin lints first, so a duplicate is a compiler bug.
+                    // Use early_error when handling -W help with no crate.
+                    (None, _) => early_error(msg.as_slice()),
+                    (Some(sess), false) => sess.bug(msg.as_slice()),
+
+                    // A duplicate name from a plugin is a user error.
+                    (Some(sess), true)  => sess.err(msg.as_slice()),
+                }
+            }
+
+            if lint.default_level != Allow {
+                self.levels.insert(id, (lint.default_level, Default));
+            }
+        }
+        self.passes.push(RefCell::new(pass));
+    }
+
+    pub fn register_builtin(&mut self, sess: Option<&Session>) {
+        macro_rules! add_builtin_lints ( ( $sess:ident, $($name:ident),*, ) => (
+            {$(
+                {
+                    let obj: builtin::$name = Default::default();
+                    self.register_pass($sess, false, box obj as LintPassObject);
+                };
+            )*}
+        ))
+
+        add_builtin_lints!(sess,
+            WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory,
+            RawPointerDeriving, UnusedAttribute, PathStatement,
+            UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes,
+            NonSnakeCaseFunctions, NonUppercaseStatics,
+            NonUppercasePatternStatics, UppercaseVariables,
+            UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut,
+            UnnecessaryAllocation, MissingDoc, Stability,
+
+            GatherNodeLevels, HardwiredLints,
+        )
+    }
+
+    pub fn process_command_line(&mut self, sess: &Session) {
+        for &(ref lint_name, level) in sess.opts.lint_opts.iter() {
+            match self.by_name.find_equiv(&lint_name.as_slice()) {
+                Some(&lint_id) => self.set_level(lint_id, (level, CommandLine)),
+                None => sess.err(format!("unknown {} flag: {}",
+                    level.as_str(), lint_name).as_slice()),
+            }
+        }
+    }
+}
+
+/// Context for lint checking.
+pub struct Context<'a> {
+    /// The store of registered lints.
+    lints: LintStore,
 
     /// Context we're checking in (used to access fields like sess).
     tcx: &'a ty::ctxt,
@@ -271,7 +375,7 @@ struct Context<'a> {
 
 /// Convenience macro for calling a `LintPass` method on every pass in the context.
 macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => (
-    for obj in $cx.lint_objects.iter() {
+    for obj in $cx.lints.passes.iter() {
         obj.borrow_mut().$f($cx, $($args),*);
     }
 ))
@@ -316,26 +420,11 @@ pub fn emit_lint(sess: &Session, lint: &'static Lint,
 }
 
 impl<'a> Context<'a> {
-    fn get_level_source(&self, lint: LintId) -> LevelSource {
-        match self.levels.find(&lint) {
-            Some(&s) => s,
-            None => (Allow, Default),
-        }
-    }
-
-    fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) {
-        if lvlsrc.val0() == Allow {
-            self.levels.remove(&lint);
-        } else {
-            self.levels.insert(lint, lvlsrc);
-        }
-    }
-
-    fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
-        let (level, src) = match self.levels.find(&LintId::of(lint)) {
+    pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
+        let (level, src) = match self.lints.levels.find(&LintId::of(lint)) {
             None => return,
             Some(&(Warn, src))
-                => (self.get_level_source(LintId::of(builtin::warnings)).val0(), src),
+                => (self.lints.get_level_source(LintId::of(builtin::warnings)).val0(), src),
             Some(&pair) => pair,
         };
 
@@ -357,17 +446,17 @@ fn with_lint_attrs(&mut self,
         let lint_attrs = self.gather_lint_attrs(attrs);
         let mut pushed = 0u;
         for (lint_id, level, span) in lint_attrs.move_iter() {
-            let now = self.get_level_source(lint_id).val0();
+            let now = self.lints.get_level_source(lint_id).val0();
             if now == Forbid && level != Forbid {
                 let lint_name = lint_id.as_str();
                 self.tcx.sess.span_err(span,
                 format!("{}({}) overruled by outer forbid({})",
                         level.as_str(), lint_name, lint_name).as_slice());
             } else if now != level {
-                let src = self.get_level_source(lint_id).val1();
+                let src = self.lints.get_level_source(lint_id).val1();
                 self.level_stack.push((lint_id, (now, src)));
                 pushed += 1;
-                self.set_level(lint_id, (level, Node(span)));
+                self.lints.set_level(lint_id, (level, Node(span)));
             }
         }
 
@@ -378,7 +467,7 @@ fn with_lint_attrs(&mut self,
         // rollback
         for _ in range(0, pushed) {
             let (lint, lvlsrc) = self.level_stack.pop().unwrap();
-            self.set_level(lint, lvlsrc);
+            self.lints.set_level(lint, lvlsrc);
         }
     }
 
@@ -419,7 +508,7 @@ fn gather_lint_attrs(&mut self, attrs: &[ast::Attribute]) -> Vec<(LintId, Level,
             for meta in metas.iter() {
                 match meta.node {
                     ast::MetaWord(ref lint_name) => {
-                        match self.lints_by_name.find_equiv(lint_name) {
+                        match self.lints.by_name.find_equiv(lint_name) {
                             Some(lint_id) => out.push((*lint_id, level, meta.span)),
 
                             None => self.span_lint(builtin::unrecognized_lint,
@@ -636,75 +725,21 @@ fn visit_id(&self, id: ast::NodeId) {
     }
 }
 
-fn builtin_lints() -> Vec<Box<LintPass>> {
-    macro_rules! builtin_lints (( $($name:ident),*, ) => (
-        vec!($(
-            {
-                let obj: builtin::$name = Default::default();
-                box obj as LintPassObject
-            }
-        ),*)
-    ))
-
-    builtin_lints!(
-        WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory,
-        RawPointerDeriving, UnusedAttribute, PathStatement,
-        UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes,
-        NonSnakeCaseFunctions, NonUppercaseStatics,
-        NonUppercasePatternStatics, UppercaseVariables,
-        UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut,
-        UnnecessaryAllocation, MissingDoc, Stability,
-
-        GatherNodeLevels, HardwiredLints,
-    )
-}
-
-/// Get specs for all builtin lints.  Used for `-W help`.
-pub fn builtin_lint_specs() -> Vec<&'static Lint> {
-    builtin_lints().move_iter()
-        .flat_map(|x| x.get_lints().iter().map(|&y| y))
-        .collect()
-}
-
 pub fn check_crate(tcx: &ty::ctxt,
                    exported_items: &ExportedItems,
                    krate: &ast::Crate) {
-    let lints = builtin_lints().move_iter().map(|x| RefCell::new(x)).collect();
+
+    // We want to own the lint store, so move it out of the session.
+    let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(),
+        LintStore::new());
 
     let mut cx = Context {
-        lint_objects: lints,
-        lints_by_name: HashMap::new(),
-        levels: HashMap::new(),
+        lints: lint_store,
         tcx: tcx,
         level_stack: Vec::new(),
         node_levels: RefCell::new(HashMap::new()),
     };
 
-    // Index the lints by name, and set the default levels.
-    for obj in cx.lint_objects.iter() {
-        for &lint in obj.borrow_mut().get_lints().iter() {
-            let id = LintId::of(lint);
-            if !cx.lints_by_name.insert(lint.name, id) {
-                cx.tcx.sess.err(format!("duplicate specification of lint {}",
-                    lint.name).as_slice());
-            }
-            if lint.default_level != Allow {
-                cx.levels.insert(id, (lint.default_level, Default));
-            }
-        }
-    }
-
-    // Set command line lint levels.
-    for &(ref lint_name, level) in tcx.sess.opts.lint_opts.iter() {
-        match cx.lints_by_name.find_equiv(&lint_name.as_slice()) {
-            Some(&lint_id) => cx.set_level(lint_id, (level, CommandLine)),
-            None => cx.tcx.sess.err(format!("unknown {} flag: {}",
-                level.as_str(), lint_name).as_slice()),
-        }
-    }
-
-    tcx.sess.abort_if_errors();
-
     // Visit the whole crate.
     cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
         cx.visit_id(ast::CRATE_NODE_ID);
index f08cbb06c9e3166c21b83d5d4c1bbc52140f408e..5ae469c41f2dfa39ed0f37b06b50454db956cf3c 100644 (file)
@@ -120,7 +120,8 @@ fn test_env(_test_name: &str,
                              name: "test".to_owned(),
                              version: None };
     let (krate, ast_map) =
-        driver::phase_2_configure_and_expand(&sess, krate, &krate_id);
+        driver::phase_2_configure_and_expand(&sess, krate, &krate_id)
+            .expect("phase 2 aborted");
 
     // run just enough stuff to build a tcx:
     let lang_items = lang_items::collect_language_items(&krate, &sess);
index ef8367dfc76132ebdba13a8ff4f654e0cc4fafc0..36bf02cac3050ddaa0eb5ab3b587e4b75cb8c7fa 100644 (file)
@@ -102,8 +102,10 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
     }
 
     let krate = phase_1_parse_input(&sess, cfg, &input);
-    let (krate, ast_map) = phase_2_configure_and_expand(&sess, krate,
-                                                        &from_str("rustdoc").unwrap());
+    let (krate, ast_map)
+        = phase_2_configure_and_expand(&sess, krate, &from_str("rustdoc").unwrap())
+            .expect("phase_2_configure_and_expand aborted in rustdoc!");
+
     let driver::driver::CrateAnalysis {
         exported_items, public_items, ty_cx, ..
     } = phase_3_run_analysis_passes(sess, &krate, ast_map);
index c1d87fbb03bd1fe422eaf5df83080ac330090c27..e7fc3cedf5ec9cd40cc834997b1ccb9d387b3397 100644 (file)
@@ -69,7 +69,8 @@ pub fn run(input: &str,
     }));
     let krate = driver::phase_1_parse_input(&sess, cfg, &input);
     let (krate, _) = driver::phase_2_configure_and_expand(&sess, krate,
-                                                          &from_str("rustdoc-test").unwrap());
+            &from_str("rustdoc-test").unwrap())
+        .expect("phase_2_configure_and_expand aborted in rustdoc!");
 
     let ctx = box(GC) core::DocContext {
         krate: krate,