about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/lint/context.rs251
-rw-r--r--src/test/run-pass/auxiliary/issue_42007_s.rs14
-rw-r--r--src/test/run-pass/issue-42007.rs19
3 files changed, 198 insertions, 86 deletions
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 9d5ba2c8f95..e7c8d3285c8 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -40,6 +40,7 @@ use std::cmp;
 use std::default::Default as StdDefault;
 use std::mem;
 use std::fmt;
+use std::cell::{Ref, RefCell};
 use syntax::attr;
 use syntax::ast;
 use syntax::symbol::Symbol;
@@ -61,8 +62,8 @@ pub struct LintStore {
     lints: Vec<(&'static Lint, bool)>,
 
     /// Trait objects for each lint pass.
-    /// This is only `None` while iterating over the objects. See the definition
-    /// of run_lints.
+    /// This is only `None` while performing a lint pass. See the definition
+    /// of `LintSession::new`.
     early_passes: Option<Vec<EarlyLintPassObject>>,
     late_passes: Option<Vec<LateLintPassObject>>,
 
@@ -70,7 +71,7 @@ pub struct LintStore {
     by_name: FxHashMap<String, TargetLint>,
 
     /// Current levels of each lint, and where they were set.
-    levels: FxHashMap<LintId, LevelSource>,
+    levels: LintLevels,
 
     /// Map of registered lint groups to what lints they expand to. The bool
     /// is true if the lint group was added by a plugin.
@@ -79,11 +80,36 @@ pub struct LintStore {
     /// Extra info for future incompatibility lints, descibing the
     /// issue or RFC that caused the incompatibility.
     future_incompatible: FxHashMap<LintId, FutureIncompatibleInfo>,
+}
+
+
+#[derive(Default)]
+struct LintLevels {
+    /// Current levels of each lint, and where they were set.
+    levels: FxHashMap<LintId, LevelSource>,
 
     /// Maximum level a lint can be
     lint_cap: Option<Level>,
 }
 
+
+pub struct LintSession<'a, PassObject> {
+    /// Reference to the store of registered lints.
+    lints: Ref<'a, LintStore>,
+
+    /// The current lint levels.
+    levels: LintLevels,
+
+    /// When recursing into an attributed node of the ast which modifies lint
+    /// levels, this stack keeps track of the previous lint levels of whatever
+    /// was modified.
+    stack: Vec<(LintId, LevelSource)>,
+
+    /// Trait objects for each lint pass.
+    passes: Option<Vec<PassObject>>,
+}
+
+
 /// When you call `add_lint` on the session, you wind up storing one
 /// of these, which records a "potential lint" at a particular point.
 #[derive(PartialEq, RustcEncodable, RustcDecodable)]
@@ -157,34 +183,15 @@ enum FindLintError {
 }
 
 impl LintStore {
-    fn get_level_source(&self, lint: LintId) -> LevelSource {
-        match self.levels.get(&lint) {
-            Some(&s) => s,
-            None => (Allow, Default),
-        }
-    }
-
-    fn set_level(&mut self, lint: LintId, mut lvlsrc: LevelSource) {
-        if let Some(cap) = self.lint_cap {
-            lvlsrc.0 = cmp::min(lvlsrc.0, cap);
-        }
-        if lvlsrc.0 == Allow {
-            self.levels.remove(&lint);
-        } else {
-            self.levels.insert(lint, lvlsrc);
-        }
-    }
-
     pub fn new() -> LintStore {
         LintStore {
             lints: vec![],
             early_passes: Some(vec![]),
             late_passes: Some(vec![]),
             by_name: FxHashMap(),
-            levels: FxHashMap(),
+            levels: LintLevels::default(),
             future_incompatible: FxHashMap(),
             lint_groups: FxHashMap(),
-            lint_cap: None,
         }
     }
 
@@ -236,9 +243,7 @@ impl LintStore {
                 }
             }
 
-            if lint.default_level != Allow {
-                self.levels.insert(id, (lint.default_level, Default));
-            }
+            self.levels.set(id, (lint.default_level, Default));
         }
     }
 
@@ -310,7 +315,7 @@ impl LintStore {
 
             let lint_flag_val = Symbol::intern(&lint_name);
             match self.find_lint(&lint_name[..], sess, None) {
-                Ok(lint_id) => self.set_level(lint_id, (level, CommandLine(lint_flag_val))),
+                Ok(lint_id) => self.levels.set(lint_id, (level, CommandLine(lint_flag_val))),
                 Err(FindLintError::Removed) => { }
                 Err(_) => {
                     match self.lint_groups.iter().map(|(&x, pair)| (x, pair.0.clone()))
@@ -318,10 +323,9 @@ impl LintStore {
                                                                       Vec<LintId>>>()
                                                  .get(&lint_name[..]) {
                         Some(v) => {
-                            v.iter()
-                             .map(|lint_id: &LintId|
-                                     self.set_level(*lint_id, (level, CommandLine(lint_flag_val))))
-                             .collect::<Vec<()>>();
+                            for lint_id in v {
+                                self.levels.set(*lint_id, (level, CommandLine(lint_flag_val)));
+                            }
                         }
                         None => {
                             // The lint or lint group doesn't exist.
@@ -333,15 +337,73 @@ impl LintStore {
             }
         }
 
-        self.lint_cap = sess.opts.lint_cap;
+        self.levels.set_lint_cap(sess.opts.lint_cap);
+    }
+}
+
+
+impl LintLevels {
+    fn get_source(&self, lint: LintId) -> LevelSource {
+        match self.levels.get(&lint) {
+            Some(&s) => s,
+            None => (Allow, Default),
+        }
+    }
+
+    fn set(&mut self, lint: LintId, mut lvlsrc: LevelSource) {
         if let Some(cap) = self.lint_cap {
-            for level in self.levels.iter_mut().map(|p| &mut (p.1).0) {
-                *level = cmp::min(*level, cap);
+            lvlsrc.0 = cmp::min(lvlsrc.0, cap);
+        }
+        if lvlsrc.0 == Allow {
+            self.levels.remove(&lint);
+        } else {
+            self.levels.insert(lint, lvlsrc);
+        }
+    }
+
+    fn set_lint_cap(&mut self, lint_cap: Option<Level>) {
+        self.lint_cap = lint_cap;
+        if let Some(cap) = lint_cap {
+            for (_, level) in &mut self.levels {
+                level.0 = cmp::min(level.0, cap);
             }
         }
     }
 }
 
+
+impl<'a, PassObject: LintPassObject> LintSession<'a, PassObject> {
+    /// Creates a new `LintSession`, by moving out the `LintStore`'s initial
+    /// lint levels and pass objects. These can be restored using the `restore`
+    /// method.
+    fn new(store: &'a RefCell<LintStore>) -> LintSession<'a, PassObject> {
+        let mut s = store.borrow_mut();
+        let levels = mem::replace(&mut s.levels, LintLevels::default());
+        let passes = PassObject::take_passes(&mut *s);
+        drop(s);
+        LintSession {
+            lints: store.borrow(),
+            stack: Vec::new(),
+            levels,
+            passes,
+        }
+    }
+
+    /// Restores the levels back to the original lint store.
+    fn restore(self, store: &RefCell<LintStore>) {
+        drop(self.lints);
+        let mut s = store.borrow_mut();
+        s.levels = self.levels;
+        PassObject::restore_passes(&mut *s, self.passes);
+    }
+
+    fn get_source(&self, lint_id: LintId) -> LevelSource {
+        self.levels.get_source(lint_id)
+    }
+}
+
+
+
 /// Context for lint checking after type checking.
 pub struct LateContext<'a, 'tcx: 'a> {
     /// Type context we're checking in.
@@ -356,13 +418,8 @@ pub struct LateContext<'a, 'tcx: 'a> {
     /// Items accessible from the crate being checked.
     pub access_levels: &'a AccessLevels,
 
-    /// The store of registered lints.
-    lints: LintStore,
-
-    /// When recursing into an attributed node of the ast which modifies lint
-    /// levels, this stack keeps track of the previous lint levels of whatever
-    /// was modified.
-    level_stack: Vec<(LintId, LevelSource)>,
+    /// The store of registered lints and the lint levels.
+    lint_sess: LintSession<'tcx, LateLintPassObject>,
 }
 
 /// Context for lint checking of the AST, after expansion, before lowering to
@@ -374,24 +431,19 @@ pub struct EarlyContext<'a> {
     /// The crate being checked.
     pub krate: &'a ast::Crate,
 
-    /// The store of registered lints.
-    lints: LintStore,
-
-    /// When recursing into an attributed node of the ast which modifies lint
-    /// levels, this stack keeps track of the previous lint levels of whatever
-    /// was modified.
-    level_stack: Vec<(LintId, LevelSource)>,
+    /// The store of registered lints and the lint levels.
+    lint_sess: LintSession<'a, EarlyLintPassObject>,
 }
 
 /// Convenience macro for calling a `LintPass` method on every pass in the context.
 macro_rules! run_lints { ($cx:expr, $f:ident, $ps:ident, $($args:expr),*) => ({
     // Move the vector of passes out of `$cx` so that we can
     // iterate over it mutably while passing `$cx` to the methods.
-    let mut passes = $cx.mut_lints().$ps.take().unwrap();
+    let mut passes = $cx.lint_sess_mut().passes.take().unwrap();
     for obj in &mut passes {
         obj.$f($cx, $($args),*);
     }
-    $cx.mut_lints().$ps = Some(passes);
+    $cx.lint_sess_mut().passes = Some(passes);
 }) }
 
 /// Parse the lint attributes into a vector, with `Err`s for malformed lint
@@ -522,25 +574,55 @@ pub fn raw_struct_lint<'a, S>(sess: &'a Session,
     err
 }
 
+
+pub trait LintPassObject: Sized {
+    fn take_passes(store: &mut LintStore) -> Option<Vec<Self>>;
+    fn restore_passes(store: &mut LintStore, passes: Option<Vec<Self>>);
+}
+
+impl LintPassObject for EarlyLintPassObject {
+    fn take_passes(store: &mut LintStore) -> Option<Vec<Self>> {
+        store.early_passes.take()
+    }
+
+    fn restore_passes(store: &mut LintStore, passes: Option<Vec<Self>>) {
+        store.early_passes = passes;
+    }
+}
+
+impl LintPassObject for LateLintPassObject {
+    fn take_passes(store: &mut LintStore) -> Option<Vec<Self>> {
+        store.late_passes.take()
+    }
+
+    fn restore_passes(store: &mut LintStore, passes: Option<Vec<Self>>) {
+        store.late_passes = passes;
+    }
+}
+
+
 pub trait LintContext<'tcx>: Sized {
+    type PassObject: LintPassObject;
+
     fn sess(&self) -> &Session;
     fn lints(&self) -> &LintStore;
-    fn mut_lints(&mut self) -> &mut LintStore;
-    fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)>;
+    fn lint_sess(&self) -> &LintSession<'tcx, Self::PassObject>;
+    fn lint_sess_mut(&mut self) -> &mut LintSession<'tcx, Self::PassObject>;
     fn enter_attrs(&mut self, attrs: &'tcx [ast::Attribute]);
     fn exit_attrs(&mut self, attrs: &'tcx [ast::Attribute]);
 
     /// Get the level of `lint` at the current position of the lint
     /// traversal.
     fn current_level(&self, lint: &'static Lint) -> Level {
-        self.lints().levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl)
+        self.lint_sess().get_source(LintId::of(lint)).0
     }
 
     fn level_src(&self, lint: &'static Lint) -> Option<LevelSource> {
-        self.lints().levels.get(&LintId::of(lint)).map(|ls| match ls {
+        let ref levels = self.lint_sess().levels;
+        levels.levels.get(&LintId::of(lint)).map(|ls| match ls {
             &(Warn, _) => {
                 let lint_id = LintId::of(builtin::WARNINGS);
-                let warn_src = self.lints().get_level_source(lint_id);
+                let warn_src = levels.get_source(lint_id);
                 if warn_src.0 != Warn {
                     warn_src
                 } else {
@@ -674,7 +756,7 @@ pub trait LintContext<'tcx>: Sized {
             let lint_attr_name = result.expect("lint attribute should be well-formed").0;
 
             for (lint_id, level, span) in v {
-                let (now, now_source) = self.lints().get_level_source(lint_id);
+                let (now, now_source) = self.lint_sess().get_source(lint_id);
                 if now == Forbid && level != Forbid {
                     let lint_name = lint_id.to_string();
                     let mut diag_builder = struct_span_err!(self.sess(), span, E0453,
@@ -693,10 +775,10 @@ pub trait LintContext<'tcx>: Sized {
                         }
                     }.emit()
                 } else if now != level {
-                    let src = self.lints().get_level_source(lint_id).1;
-                    self.level_stack().push((lint_id, (now, src)));
+                    let cx = self.lint_sess_mut();
+                    cx.stack.push((lint_id, (now, now_source)));
                     pushed += 1;
-                    self.mut_lints().set_level(lint_id, (level, Node(lint_attr_name, span)));
+                    cx.levels.set(lint_id, (level, Node(lint_attr_name, span)));
                 }
             }
         }
@@ -706,9 +788,10 @@ pub trait LintContext<'tcx>: Sized {
         self.exit_attrs(attrs);
 
         // rollback
+        let cx = self.lint_sess_mut();
         for _ in 0..pushed {
-            let (lint, lvlsrc) = self.level_stack().pop().unwrap();
-            self.mut_lints().set_level(lint, lvlsrc);
+            let (lint, lvlsrc) = cx.stack.pop().unwrap();
+            cx.levels.set(lint, lvlsrc);
         }
     }
 }
@@ -717,35 +800,32 @@ pub trait LintContext<'tcx>: Sized {
 impl<'a> EarlyContext<'a> {
     fn new(sess: &'a Session,
            krate: &'a ast::Crate) -> EarlyContext<'a> {
-        // We want to own the lint store, so move it out of the session. Remember
-        // to put it back later...
-        let lint_store = mem::replace(&mut *sess.lint_store.borrow_mut(),
-                                      LintStore::new());
         EarlyContext {
             sess: sess,
             krate: krate,
-            lints: lint_store,
-            level_stack: vec![],
+            lint_sess: LintSession::new(&sess.lint_store),
         }
     }
 }
 
 impl<'a, 'tcx> LintContext<'tcx> for LateContext<'a, 'tcx> {
+    type PassObject = LateLintPassObject;
+
     /// Get the overall compiler `Session` object.
     fn sess(&self) -> &Session {
         &self.tcx.sess
     }
 
     fn lints(&self) -> &LintStore {
-        &self.lints
+        &*self.lint_sess.lints
     }
 
-    fn mut_lints(&mut self) -> &mut LintStore {
-        &mut self.lints
+    fn lint_sess(&self) -> &LintSession<'tcx, Self::PassObject> {
+        &self.lint_sess
     }
 
-    fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)> {
-        &mut self.level_stack
+    fn lint_sess_mut(&mut self) -> &mut LintSession<'tcx, Self::PassObject> {
+        &mut self.lint_sess
     }
 
     fn enter_attrs(&mut self, attrs: &'tcx [ast::Attribute]) {
@@ -760,21 +840,23 @@ impl<'a, 'tcx> LintContext<'tcx> for LateContext<'a, 'tcx> {
 }
 
 impl<'a> LintContext<'a> for EarlyContext<'a> {
+    type PassObject = EarlyLintPassObject;
+
     /// Get the overall compiler `Session` object.
     fn sess(&self) -> &Session {
         &self.sess
     }
 
     fn lints(&self) -> &LintStore {
-        &self.lints
+        &*self.lint_sess.lints
     }
 
-    fn mut_lints(&mut self) -> &mut LintStore {
-        &mut self.lints
+    fn lint_sess(&self) -> &LintSession<'a, Self::PassObject> {
+        &self.lint_sess
     }
 
-    fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)> {
-        &mut self.level_stack
+    fn lint_sess_mut(&mut self) -> &mut LintSession<'a, Self::PassObject> {
+        &mut self.lint_sess
     }
 
     fn enter_attrs(&mut self, attrs: &'a [ast::Attribute]) {
@@ -1191,7 +1273,7 @@ fn check_lint_name_attribute(cx: &LateContext, attr: &ast::Attribute) {
                 continue;
             }
             Ok((lint_name, _, span)) => {
-                match check_lint_name(&cx.lints, &lint_name.as_str()) {
+                match check_lint_name(&cx.lint_sess.lints, &lint_name.as_str()) {
                     CheckLintNameResult::Ok => (),
                     CheckLintNameResult::Warning(ref msg) => {
                         cx.span_lint(builtin::RENAMED_AND_REMOVED_LINTS,
@@ -1246,15 +1328,12 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
 
     let krate = tcx.hir.krate();
 
-    // 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 = LateContext {
         tcx: tcx,
         tables: &ty::TypeckTables::empty(),
         krate: krate,
         access_levels: access_levels,
-        lints: lint_store,
-        level_stack: vec![],
+        lint_sess: LintSession::new(&tcx.sess.lint_store),
     };
 
     // Visit the whole crate.
@@ -1278,8 +1357,8 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
         }
     }
 
-    // Put the lint store back in the session.
-    mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), cx.lints);
+    // Put the lint store levels and passes back in the session.
+    cx.lint_sess.restore(&tcx.sess.lint_store);
 }
 
 pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
@@ -1302,8 +1381,8 @@ pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
         run_lints!(cx, check_crate_post, early_passes, krate);
     });
 
-    // Put the lint store back in the session.
-    mem::replace(&mut *sess.lint_store.borrow_mut(), cx.lints);
+    // Put the lint store levels and passes back in the session.
+    cx.lint_sess.restore(&sess.lint_store);
 
     // If we missed any lints added to the session, then there's a bug somewhere
     // in the iteration code.
diff --git a/src/test/run-pass/auxiliary/issue_42007_s.rs b/src/test/run-pass/auxiliary/issue_42007_s.rs
new file mode 100644
index 00000000000..b965e916f98
--- /dev/null
+++ b/src/test/run-pass/auxiliary/issue_42007_s.rs
@@ -0,0 +1,14 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[repr(u8)]
+pub enum E {
+    B = 1 as u8,
+}
\ No newline at end of file
diff --git a/src/test/run-pass/issue-42007.rs b/src/test/run-pass/issue-42007.rs
new file mode 100644
index 00000000000..cc7e3bc372c
--- /dev/null
+++ b/src/test/run-pass/issue-42007.rs
@@ -0,0 +1,19 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:issue_42007_s.rs
+
+extern crate issue_42007_s;
+
+enum I {
+    E(issue_42007_s::E),
+}
+
+fn main() {}