]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc/lint/context.rs
Imported Upstream version 1.6.0+dfsg1
[rustc.git] / src / librustc / lint / context.rs
index e3eac2e6068076ef20177cf48dd282b593ef321b..343963e7c809d08ab870bd590d7aa92c40fd0fd3 100644 (file)
@@ -25,7 +25,7 @@
 //! for all lint attributes.
 use self::TargetLint::*;
 
-use middle::privacy::ExportedItems;
+use middle::privacy::AccessLevels;
 use middle::ty::{self, Ty};
 use session::{early_error, Session};
 use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass};
@@ -42,9 +42,10 @@ use syntax::attr::{self, AttrMetaMethods};
 use syntax::codemap::Span;
 use syntax::parse::token::InternedString;
 use syntax::ast;
+use syntax::attr::ThinAttributesExt;
 use rustc_front::hir;
 use rustc_front::util;
-use rustc_front::visit as hir_visit;
+use rustc_front::intravisit as hir_visit;
 use syntax::visit as ast_visit;
 use syntax::diagnostic;
 
@@ -217,21 +218,10 @@ impl LintStore {
     {
         match self.by_name.get(lint_name) {
             Some(&Id(lint_id)) => Ok(lint_id),
-            Some(&Renamed(ref new_name, lint_id)) => {
-                let warning = format!("lint {} has been renamed to {}",
-                                      lint_name, new_name);
-                match span {
-                    Some(span) => sess.span_warn(span, &warning[..]),
-                    None => sess.warn(&warning[..]),
-                };
+            Some(&Renamed(_, lint_id)) => {
                 Ok(lint_id)
             },
             Some(&Removed(ref reason)) => {
-                let warning = format!("lint {} has been removed: {}", lint_name, reason);
-                match span {
-                    Some(span) => sess.span_warn(span, &warning[..]),
-                    None => sess.warn(&warning[..])
-                }
                 Err(FindLintError::Removed)
             },
             None => Err(FindLintError::NotFound)
@@ -240,8 +230,12 @@ impl LintStore {
 
     pub fn process_command_line(&mut self, sess: &Session) {
         for &(ref lint_name, level) in &sess.opts.lint_opts {
+            check_lint_name_cmdline(sess, self,
+                                    &lint_name[..], level);
+
             match self.find_lint(&lint_name[..], sess, None) {
                 Ok(lint_id) => self.set_level(lint_id, (level, CommandLine)),
+                Err(FindLintError::Removed) => { }
                 Err(_) => {
                     match self.lint_groups.iter().map(|(&x, pair)| (x, pair.0.clone()))
                                                  .collect::<FnvHashMap<&'static str,
@@ -253,8 +247,11 @@ impl LintStore {
                                      self.set_level(*lint_id, (level, CommandLine)))
                              .collect::<Vec<()>>();
                         }
-                        None => sess.err(&format!("unknown {} flag: {}",
-                                                 level.as_str(), lint_name)),
+                        None => {
+                            // The lint or lint group doesn't exist.
+                            // This is an error, but it was handled
+                            // by check_lint_name_cmdline.
+                        }
                     }
                 }
             }
@@ -277,8 +274,8 @@ pub struct LateContext<'a, 'tcx: 'a> {
     /// The crate being checked.
     pub krate: &'a hir::Crate,
 
-    /// Items exported from the crate being checked.
-    pub exported_items: &'a ExportedItems,
+    /// Items accessible from the crate being checked.
+    pub access_levels: &'a AccessLevels,
 
     /// The store of registered lints.
     lints: LintStore,
@@ -329,29 +326,39 @@ pub fn gather_attrs(attrs: &[ast::Attribute])
                     -> Vec<Result<(InternedString, Level, Span), Span>> {
     let mut out = vec!();
     for attr in attrs {
-        let level = match Level::from_str(&attr.name()) {
-            None => continue,
-            Some(lvl) => lvl,
-        };
+        let r = gather_attr(attr);
+        out.extend(r.into_iter());
+    }
+    out
+}
 
-        attr::mark_used(attr);
+pub fn gather_attr(attr: &ast::Attribute)
+                   -> Vec<Result<(InternedString, Level, Span), Span>> {
+    let mut out = vec!();
 
-        let meta = &attr.node.value;
-        let metas = match meta.node {
-            ast::MetaList(_, ref metas) => metas,
-            _ => {
-                out.push(Err(meta.span));
-                continue;
-            }
-        };
+    let level = match Level::from_str(&attr.name()) {
+        None => return out,
+        Some(lvl) => lvl,
+    };
 
-        for meta in metas {
-            out.push(match meta.node {
-                ast::MetaWord(ref lint_name) => Ok((lint_name.clone(), level, meta.span)),
-                _ => Err(meta.span),
-            });
+    attr::mark_used(attr);
+
+    let meta = &attr.node.value;
+    let metas = match meta.node {
+        ast::MetaList(_, ref metas) => metas,
+        _ => {
+            out.push(Err(meta.span));
+            return out;
         }
+    };
+
+    for meta in metas {
+        out.push(match meta.node {
+            ast::MetaWord(ref lint_name) => Ok((lint_name.clone(), level, meta.span)),
+            _ => Err(meta.span),
+        });
     }
+
     out
 }
 
@@ -362,8 +369,12 @@ pub fn gather_attrs(attrs: &[ast::Attribute])
 /// in trans that run after the main lint pass is finished. Most
 /// lints elsewhere in the compiler should call
 /// `Session::add_lint()` instead.
-pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
-                     lvlsrc: LevelSource, span: Option<Span>, msg: &str) {
+pub fn raw_emit_lint(sess: &Session,
+                     lints: &LintStore,
+                     lint: &'static Lint,
+                     lvlsrc: LevelSource,
+                     span: Option<Span>,
+                     msg: &str) {
     let (mut level, source) = lvlsrc;
     if level == Allow { return }
 
@@ -398,6 +409,18 @@ pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
         _ => sess.bug("impossible level in raw_emit_lint"),
     }
 
+    // Check for future incompatibility lints and issue a stronger warning.
+    let future_incompat_lints = &lints.lint_groups[builtin::FUTURE_INCOMPATIBLE];
+    let this_id = LintId::of(lint);
+    if future_incompat_lints.0.iter().any(|&id| id == this_id) {
+        let msg = "this lint will become a HARD ERROR in a future release!";
+        if let Some(sp) = span {
+            sess.span_note(sp, msg);
+        } else {
+            sess.note(msg);
+        }
+    }
+
     if let Some(span) = def {
         sess.span_note(span, "lint level defined here");
     }
@@ -427,7 +450,7 @@ pub trait LintContext: Sized {
             Some(&pair) => pair,
         };
 
-        raw_emit_lint(&self.sess(), lint, (level, src), span, msg);
+        raw_emit_lint(&self.sess(), self.lints(), lint, (level, src), span, msg);
     }
 
     /// Emit a lint at the appropriate level, for a particular span.
@@ -493,9 +516,9 @@ pub trait LintContext: Sized {
                                                            (*lint_id, level, span))
                                                       .collect(),
                                 None => {
-                                    self.span_lint(builtin::UNKNOWN_LINTS, span,
-                                                   &format!("unknown `{}` attribute: `{}`",
-                                                            level.as_str(), lint_name));
+                                    // The lint or lint group doesn't exist.
+                                    // This is an error, but it was handled
+                                    // by check_lint_name_attribute.
                                     continue;
                                 }
                             }
@@ -555,7 +578,6 @@ impl<'a> EarlyContext<'a> {
     {
         let mut v = ast_util::IdVisitor {
             operation: self,
-            pass_through_items: false,
             visited_outermost: false,
         };
         f(&mut v);
@@ -565,7 +587,7 @@ impl<'a> EarlyContext<'a> {
 impl<'a, 'tcx> LateContext<'a, 'tcx> {
     fn new(tcx: &'a ty::ctxt<'tcx>,
            krate: &'a hir::Crate,
-           exported_items: &'a ExportedItems) -> LateContext<'a, 'tcx> {
+           access_levels: &'a AccessLevels) -> LateContext<'a, 'tcx> {
         // 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());
@@ -573,7 +595,7 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> {
         LateContext {
             tcx: tcx,
             krate: krate,
-            exported_items: exported_items,
+            access_levels: access_levels,
             lints: lint_store,
             level_stack: vec![],
             node_levels: RefCell::new(FnvHashMap()),
@@ -583,11 +605,7 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> {
     fn visit_ids<F>(&mut self, f: F)
         where F: FnOnce(&mut util::IdVisitor<LateContext>)
     {
-        let mut v = util::IdVisitor {
-            operation: self,
-            pass_through_items: false,
-            visited_outermost: false,
-        };
+        let mut v = util::IdVisitor::new(self);
         f(&mut v);
     }
 }
@@ -611,10 +629,12 @@ impl<'a, 'tcx> LintContext for LateContext<'a, 'tcx> {
     }
 
     fn enter_attrs(&mut self, attrs: &[ast::Attribute]) {
+        debug!("late context: enter_attrs({:?})", attrs);
         run_lints!(self, enter_lint_attrs, late_passes, attrs);
     }
 
     fn exit_attrs(&mut self, attrs: &[ast::Attribute]) {
+        debug!("late context: exit_attrs({:?})", attrs);
         run_lints!(self, exit_lint_attrs, late_passes, attrs);
     }
 }
@@ -638,15 +658,24 @@ impl<'a> LintContext for EarlyContext<'a> {
     }
 
     fn enter_attrs(&mut self, attrs: &[ast::Attribute]) {
+        debug!("early context: exit_attrs({:?})", attrs);
         run_lints!(self, enter_lint_attrs, early_passes, attrs);
     }
 
     fn exit_attrs(&mut self, attrs: &[ast::Attribute]) {
+        debug!("early context: exit_attrs({:?})", attrs);
         run_lints!(self, exit_lint_attrs, early_passes, attrs);
     }
 }
 
 impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
+    /// Because lints are scoped lexically, we want to walk nested
+    /// items in the context of the outer item, so enable
+    /// deep-walking.
+    fn visit_nested_item(&mut self, item: hir::ItemId) {
+        self.visit_item(self.tcx.map.expect_item(item.id))
+    }
+
     fn visit_item(&mut self, it: &hir::Item) {
         self.with_lint_attrs(&it.attrs, |cx| {
             run_lints!(cx, check_item, late_passes, it);
@@ -668,11 +697,18 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
     }
 
     fn visit_expr(&mut self, e: &hir::Expr) {
-        run_lints!(self, check_expr, late_passes, e);
-        hir_visit::walk_expr(self, e);
+        self.with_lint_attrs(e.attrs.as_attr_slice(), |cx| {
+            run_lints!(cx, check_expr, late_passes, e);
+            hir_visit::walk_expr(cx, e);
+        })
     }
 
     fn visit_stmt(&mut self, s: &hir::Stmt) {
+        // statement attributes are actually just attributes on one of
+        // - item
+        // - local
+        // - expression
+        // so we keep track of lint levels there
         run_lints!(self, check_stmt, late_passes, s);
         hir_visit::walk_stmt(self, s);
     }
@@ -724,8 +760,10 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
     }
 
     fn visit_local(&mut self, l: &hir::Local) {
-        run_lints!(self, check_local, late_passes, l);
-        hir_visit::walk_local(self, l);
+        self.with_lint_attrs(l.attrs.as_attr_slice(), |cx| {
+            run_lints!(cx, check_local, late_passes, l);
+            hir_visit::walk_local(cx, l);
+        })
     }
 
     fn visit_block(&mut self, b: &hir::Block) {
@@ -792,6 +830,7 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
     }
 
     fn visit_attribute(&mut self, attr: &ast::Attribute) {
+        check_lint_name_attribute(self, attr);
         run_lints!(self, check_attribute, late_passes, attr);
     }
 }
@@ -952,6 +991,7 @@ impl<'a, 'tcx> IdVisitingOperation for LateContext<'a, 'tcx> {
         match self.sess().lints.borrow_mut().remove(&id) {
             None => {}
             Some(lints) => {
+                debug!("LateContext::visit_id: id={:?} lints={:?}", id, lints);
                 for (lint_id, span, msg) in lints {
                     self.span_lint(lint_id.lint, span, &msg[..])
                 }
@@ -1004,20 +1044,121 @@ impl LateLintPass for GatherNodeLevels {
     }
 }
 
+enum CheckLintNameResult {
+    Ok,
+    // Lint doesn't exist
+    NoLint,
+    // The lint is either renamed or removed and a warning was generated
+    Mentioned
+}
+
+/// Checks the name of a lint for its existence, and whether it was
+/// renamed or removed. Generates a DiagnosticBuilder containing a
+/// warning for renamed and removed lints. This is over both lint
+/// names from attributes and those passed on the command line. Since
+/// it emits non-fatal warnings and there are *two* lint passes that
+/// inspect attributes, this is only run from the late pass to avoid
+/// printing duplicate warnings.
+fn check_lint_name(sess: &Session,
+                   lint_cx: &LintStore,
+                   lint_name: &str,
+                   span: Option<Span>) -> CheckLintNameResult {
+    match lint_cx.by_name.get(lint_name) {
+        Some(&Renamed(ref new_name, _)) => {
+            let warning = format!("lint {} has been renamed to {}",
+                                  lint_name, new_name);
+            match span {
+                Some(span) => sess.span_warn(span, &warning[..]),
+                None => sess.warn(&warning[..]),
+            };
+            CheckLintNameResult::Mentioned
+        },
+        Some(&Removed(ref reason)) => {
+            let warning = format!("lint {} has been removed: {}", lint_name, reason);
+            match span {
+                Some(span) => sess.span_warn(span, &warning[..]),
+                None => sess.warn(&warning[..])
+            };
+            CheckLintNameResult::Mentioned
+        },
+        None => {
+            match lint_cx.lint_groups.get(lint_name) {
+                None => {
+                    CheckLintNameResult::NoLint
+                }
+                Some(_) => {
+                    /* lint group exists */
+                    CheckLintNameResult::Ok
+                }
+            }
+        }
+        Some(_) => {
+            /* lint exists */
+            CheckLintNameResult::Ok
+        }
+    }
+}
+
+// Checks the validity of lint names derived from attributes
+fn check_lint_name_attribute(cx: &LateContext, attr: &ast::Attribute) {
+    for result in gather_attr(attr) {
+        match result {
+            Err(_) => {
+                // Malformed lint attr. Reported by with_lint_attrs
+                continue;
+            }
+            Ok((lint_name, _, span)) => {
+                match check_lint_name(&cx.tcx.sess, &cx.lints, &lint_name[..], Some(span)) {
+                    CheckLintNameResult::Ok => (),
+                    CheckLintNameResult::Mentioned => (),
+                    CheckLintNameResult::NoLint => {
+                        cx.span_lint(builtin::UNKNOWN_LINTS, span,
+                                     &format!("unknown lint: `{}`",
+                                              lint_name));
+                    }
+                }
+            }
+        }
+    }
+}
+
+// Checks the validity of lint names derived from the command line
+fn check_lint_name_cmdline(sess: &Session, lint_cx: &LintStore,
+                           lint_name: &str, level: Level) {
+    let explain = match check_lint_name(sess, lint_cx, lint_name, None) {
+        CheckLintNameResult::Ok => false,
+        CheckLintNameResult::Mentioned => true,
+        CheckLintNameResult::NoLint => {
+            sess.err(&format!("unknown lint: `{}`", lint_name));
+            true
+        }
+    };
+
+    if explain {
+        let msg = format!("requested on the command line with `{} {}`",
+                          match level {
+                              Level::Allow => "-A",
+                              Level::Warn => "-W",
+                              Level::Deny => "-D",
+                              Level::Forbid => "-F",
+                          },
+                          lint_name);
+        sess.note(&msg);
+    }
+}
+
+
 /// Perform lint checking on a crate.
 ///
 /// Consumes the `lint_store` field of the `Session`.
-pub fn check_crate(tcx: &ty::ctxt,
-                   krate: &hir::Crate,
-                   exported_items: &ExportedItems) {
-
-    let mut cx = LateContext::new(tcx, krate, exported_items);
+pub fn check_crate(tcx: &ty::ctxt, access_levels: &AccessLevels) {
+    let krate = tcx.map.krate();
+    let mut cx = LateContext::new(tcx, krate, access_levels);
 
     // Visit the whole crate.
     cx.with_lint_attrs(&krate.attrs, |cx| {
         cx.visit_id(ast::CRATE_NODE_ID);
         cx.visit_ids(|v| {
-            v.visited_outermost = true;
             hir_visit::walk_crate(v, krate);
         });