Change:
Consider this (erroneous) program: As we expect, this fails to compile, because the outer "forbid" attribute contradicts the "allow" attribute. ("Forbid" is like "deny" except that it prevents itself from being subsequently overridden.) However, the error messages leave something to be desired: We get a separate error for each lint in the "unused" lint group! This is kind of terrible! We would prefer to get a single error for the contradicting attributes, whose message names the offending attributes (it's weird to say "overruled by outer forbid(pathstatements)", when the outer forbid is for unused, not pathstatements). A fix by the present author is forthcoming.

Discussion:
self.by_name.insert(name.into(), Removed(reason.into())); } <del> #[allow(unused_variables)] fn find_lint(&self, lint_name: &str, sess: &Session, span: Option<Span>) -> Result<LintId, FindLintError> { </del> <ins> fn find_lint(&self, lint_name: &str) -> Result<LintId, FindLintError> { </ins> match self.by_name.get(lint_name) { Some(&Id(lint_id)) => Ok(lint_id), Some(&Renamed(_, lint_id)) => { Ok(lint_id) }, <del> Some(&Removed(ref reason)) => { </del> <ins> Some(&Removed(_)) => { </ins> Err(FindLintError::Removed) }, None => Err(FindLintError::NotFound)

&lint_name[..], level); let lint_flag_val = Symbol::intern(&lint_name); <del> match self.find_lint(&lint_name[..], sess, None) { </del> <ins> match self.find_lint(&lint_name[..]) { </ins> Ok(lint_id) => self.levels.set(lint_id, (level, CommandLine(lint_flag_val))), Err(FindLintError::Removed) => { } Err(_) => {

let mut pushed = 0; for result in gather_attrs(attrs) { <del> let v = match result { </del> <ins> let (is_group, lint_level_spans) = match result { </ins> Err(span) => { span_err!(self.sess(), span, E0452, "malformed lint attribute"); continue; } Ok((lint_name, level, span)) => { <del> match self.lints().find_lint(&lint_name.as_str(), &self.sess(), Some(span)) { Ok(lint_id) => vec![(lint_id, level, span)], </del> <ins> match self.lints().find_lint(&lint_name.as_str()) { Ok(lint_id) => (false, vec![(lint_id, level, span)]), </ins> Err(FindLintError::NotFound) => { match self.lints().lint_groups.get(&*lint_name.as_str()) { <del> Some(&(ref v, _)) => v.iter() </del> <ins> Some(&(ref v, _)) => (true, v.iter() </ins> .map(|lint_id: &LintId| (*lint_id, level, span)) <del> .collect(), </del> <ins> .collect()), </ins> None => { // The lint or lint group doesn't exist. // This is an error, but it was handled

let lint_attr_name = result.expect("lint attribute should be well-formed").0; <del> for (lint_id, level, span) in v { </del> <ins> for (lint_id, level, span) in lint_level_spans { </ins> let (now, now_source) = self.lint_sess().get_source(lint_id); if now == Forbid && level != Forbid { <del> let lint_name = lint_id.to_string(); </del> <ins> let forbidden_lint_name = match now_source { LintSource::Default => lint_id.to_string(), LintSource::Node(name, _) => name.to_string(), LintSource::CommandLine(name) => name.to_string(), }; </ins> let mut diag_builder = struct_span_err!(self.sess(), span, E0453, "{}({}) overruled by outer forbid({})", <del> level.as_str(), lint_name, lint_name); </del> <ins> level.as_str(), lint_attr_name, forbidden_lint_name); </ins> diag_builder.span_label(span, "overruled by previous forbid"); match now_source { LintSource::Default => &mut diag_builder,

LintSource::CommandLine(_) => { diag_builder.note("`forbid` lint level was set on command line") } <del> }.emit() </del> <ins> }.emit(); if is_group { // don't set a separate error for every lint in the group break; } </ins> } else if now != level { let cx = self.lint_sess_mut(); cx.stack.push((lint_id, (now, now_source)));

fn decode<D: Decoder>(d: &mut D) -> Result<LintId, D::Error> { let s = d.read_str()?; ty::tls::with(|tcx| { <del> match tcx.sess.lint_store.borrow().find_lint(&s, tcx.sess, None) { </del> <ins> match tcx.sess.lint_store.borrow().find_lint(&s) { </ins> Ok(id) => Ok(id), Err(_) => panic!("invalid lint-id `{}`", s), }

<ins> // 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. // Forbidding a group (here, `unused`) overrules subsequent allowance of both // the group, and an individual lint in the group (here, `unused_variables`); // and, forbidding an individual lint (here, `non_snake_case`) overrules // subsequent allowance of a lint group containing it (here, `bad_style`). See // Issue #42873. #![forbid(unused, non_snake_case)] #[allow(unused, unused_variables, bad_style)] fn main() { println!("hello forbidden world") } </ins>

<ins> error[E0453]: allow(unused) overruled by outer forbid(unused) --> $DIR/outer-forbid.rs:19:9 | 17 | #![forbid(unused, non_snake_case)] |           ------ `forbid` level set here 18 | 19 | #[allow(unused, unused_variables, bad_style)] |         ^^^^^^ overruled by previous forbid error[E0453]: allow(unused_variables) overruled by outer forbid(unused) --> $DIR/outer-forbid.rs:19:17 | 17 | #![forbid(unused, non_snake_case)] |           ------ `forbid` level set here 18 | 19 | #[allow(unused, unused_variables, bad_style)] |                 ^^^^^^^^^^^^^^^^ overruled by previous forbid error[E0453]: allow(bad_style) overruled by outer forbid(non_snake_case) --> $DIR/outer-forbid.rs:19:35 | 17 | #![forbid(unused, non_snake_case)] |                   -------------- `forbid` level set here 18 | 19 | #[allow(unused, unused_variables, bad_style)] |                                   ^^^^^^^^^ overruled by previous forbid error: aborting due to previous error(s) </ins>