//! 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};
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;
{
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)
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,
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.
+ }
}
}
}
/// 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,
-> 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
}
/// 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 }
_ => 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");
}
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.
(*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;
}
}
{
let mut v = ast_util::IdVisitor {
operation: self,
- pass_through_items: false,
visited_outermost: false,
};
f(&mut v);
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());
LateContext {
tcx: tcx,
krate: krate,
- exported_items: exported_items,
+ access_levels: access_levels,
lints: lint_store,
level_stack: vec![],
node_levels: RefCell::new(FnvHashMap()),
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);
}
}
}
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);
}
}
}
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);
}
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);
}
}
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) {
}
fn visit_attribute(&mut self, attr: &ast::Attribute) {
+ check_lint_name_attribute(self, attr);
run_lints!(self, check_attribute, late_passes, attr);
}
}
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[..])
}
}
}
+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);
});