From 1bfa1d51fd220ee5281176f565581cd9dfeaa380 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Sat, 15 Oct 2016 10:28:12 -0700 Subject: [PATCH 1/4] introing one-time diagnostics: only emit "lint level defined here" once We introduce a new `one_time_diagnostics` field on `rustc::session::Session` to hold a hashset of diagnostic messages we've set once but don't want to see again (as uniquified by span and message text), "lint level defined here" being the motivating example dealt with here. This is in the matter of #24690. --- src/librustc/lint/context.rs | 3 +-- src/librustc/session/mod.rs | 27 ++++++++++++++++++- src/test/compile-fail/lint-group-style.rs | 2 -- .../lint-unconditional-recursion.rs | 14 +--------- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 81d3d440b566f..eebf4d32ab26f 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -452,8 +452,7 @@ pub fn raw_struct_lint<'a>(sess: &'a Session, } if let Some(span) = def { - let explanation = "lint level defined here"; - err.span_note(span, &explanation); + sess.diag_span_note_once(&mut err, span, "lint level defined here"); } err diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index d002aba595bca..7917964c8f1b6 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -17,7 +17,7 @@ use middle::dependency_format; use session::search_paths::PathKind; use session::config::DebugInfoLevel; use ty::tls; -use util::nodemap::{NodeMap, FnvHashMap}; +use util::nodemap::{NodeMap, FnvHashMap, FnvHashSet}; use util::common::duration_to_secs_str; use mir::transform as mir_pass; @@ -75,6 +75,10 @@ pub struct Session { pub working_dir: PathBuf, pub lint_store: RefCell, pub lints: RefCell>>, + /// Set of (span, message) tuples tracking lint (sub)diagnostics that have + /// been set once, but should not be set again, in order to avoid + /// redundantly verbose output (Issue #24690). + pub one_time_diagnostics: RefCell>, pub plugin_llvm_passes: RefCell>, pub mir_passes: RefCell, pub plugin_attributes: RefCell>, @@ -288,6 +292,26 @@ impl Session { pub fn diagnostic<'a>(&'a self) -> &'a errors::Handler { &self.parse_sess.span_diagnostic } + + /// Analogous to calling `.span_note` on the given DiagnosticBuilder, but + /// deduplicates on span and message for this `Session`. + // + // FIXME: if the need arises for one-time diagnostics other than + // `span_note`, we almost certainly want to generalize this "check the + // one-time diagnostics map, then set message if it's not already there" + // code to accomodate all of them + pub fn diag_span_note_once<'a, 'b>(&'a self, + diag_builder: &'b mut DiagnosticBuilder<'a>, + span: Span, message: &str) { + let span_message = (span, message.to_owned()); + let already_noted: bool = self.one_time_diagnostics.borrow() + .contains(&span_message); + if !already_noted { + diag_builder.span_note(span, &message); + self.one_time_diagnostics.borrow_mut().insert(span_message); + } + } + pub fn codemap<'a>(&'a self) -> &'a codemap::CodeMap { self.parse_sess.codemap() } @@ -561,6 +585,7 @@ pub fn build_session_(sopts: config::Options, working_dir: env::current_dir().unwrap(), lint_store: RefCell::new(lint::LintStore::new()), lints: RefCell::new(NodeMap()), + one_time_diagnostics: RefCell::new(FnvHashSet()), plugin_llvm_passes: RefCell::new(Vec::new()), mir_passes: RefCell::new(mir_pass::Passes::new()), plugin_attributes: RefCell::new(Vec::new()), diff --git a/src/test/compile-fail/lint-group-style.rs b/src/test/compile-fail/lint-group-style.rs index 393e46ab5394c..a88e0c63ac374 100644 --- a/src/test/compile-fail/lint-group-style.rs +++ b/src/test/compile-fail/lint-group-style.rs @@ -20,7 +20,6 @@ mod test { #[forbid(bad_style)] //~^ NOTE lint level defined here - //~^^ NOTE lint level defined here mod bad { fn CamelCase() {} //~ ERROR function `CamelCase` should have a snake case name @@ -30,7 +29,6 @@ mod test { mod warn { #![warn(bad_style)] //~^ NOTE lint level defined here - //~| NOTE lint level defined here fn CamelCase() {} //~ WARN function `CamelCase` should have a snake case name diff --git a/src/test/compile-fail/lint-unconditional-recursion.rs b/src/test/compile-fail/lint-unconditional-recursion.rs index 94e189aa47f6f..bee5a2c45be6d 100644 --- a/src/test/compile-fail/lint-unconditional-recursion.rs +++ b/src/test/compile-fail/lint-unconditional-recursion.rs @@ -10,19 +10,7 @@ #![deny(unconditional_recursion)] //~^ NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here -//~| NOTE lint level defined here + #![allow(dead_code)] fn foo() { //~ ERROR function cannot return without recurring foo(); //~ NOTE recursive call site From 95805ee6275b4fe12cdb8845e699e7a6aa6b26eb Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Wed, 26 Oct 2016 20:16:58 -0700 Subject: [PATCH 2/4] save a borrow by using return value of HashSet::insert Thanks to Niko Matsakis's review for the suggestion. --- src/librustc/session/mod.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 7917964c8f1b6..f4029fde471b9 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -297,18 +297,16 @@ impl Session { /// deduplicates on span and message for this `Session`. // // FIXME: if the need arises for one-time diagnostics other than - // `span_note`, we almost certainly want to generalize this "check the - // one-time diagnostics map, then set message if it's not already there" - // code to accomodate all of them + // `span_note`, we almost certainly want to generalize this + // "check/insert-into the one-time diagnostics map, then set message if + // it's not already there" code to accomodate all of them pub fn diag_span_note_once<'a, 'b>(&'a self, diag_builder: &'b mut DiagnosticBuilder<'a>, span: Span, message: &str) { let span_message = (span, message.to_owned()); - let already_noted: bool = self.one_time_diagnostics.borrow() - .contains(&span_message); - if !already_noted { + let fresh = self.one_time_diagnostics.borrow_mut().insert(span_message); + if fresh { diag_builder.span_note(span, &message); - self.one_time_diagnostics.borrow_mut().insert(span_message); } } From 8d1da8452cc725443c43c44e0f4ec64a73944cdf Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Wed, 26 Oct 2016 21:03:18 -0700 Subject: [PATCH 3/4] one-time diagnostics are only one-time for humans, not JSON-eating tools Jonathan D. Turner pointed out that we don't want to dedup in JSON mode. Since the compile-test runner uses JSON output, we regrettably need to revert the edits to existing tests; one imagines that testing for one-time diagnosticity for humans will have to be added as a UI test. This remains in the matter of #24690. --- src/librustc/session/mod.rs | 20 ++++++++++++++----- src/test/compile-fail/lint-group-style.rs | 2 ++ .../lint-unconditional-recursion.rs | 14 ++++++++++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index f4029fde471b9..154fe9af79a9a 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -294,7 +294,8 @@ impl Session { } /// Analogous to calling `.span_note` on the given DiagnosticBuilder, but - /// deduplicates on span and message for this `Session`. + /// deduplicates on span and message for this `Session` if we're not + /// outputting in JSON mode. // // FIXME: if the need arises for one-time diagnostics other than // `span_note`, we almost certainly want to generalize this @@ -303,10 +304,19 @@ impl Session { pub fn diag_span_note_once<'a, 'b>(&'a self, diag_builder: &'b mut DiagnosticBuilder<'a>, span: Span, message: &str) { - let span_message = (span, message.to_owned()); - let fresh = self.one_time_diagnostics.borrow_mut().insert(span_message); - if fresh { - diag_builder.span_note(span, &message); + match self.opts.error_format { + // when outputting JSON for tool consumption, the tool might want + // the duplicates + config::ErrorOutputType::Json => { + diag_builder.span_note(span, &message); + }, + _ => { + let span_message = (span, message.to_owned()); + let fresh = self.one_time_diagnostics.borrow_mut().insert(span_message); + if fresh { + diag_builder.span_note(span, &message); + } + } } } diff --git a/src/test/compile-fail/lint-group-style.rs b/src/test/compile-fail/lint-group-style.rs index a88e0c63ac374..393e46ab5394c 100644 --- a/src/test/compile-fail/lint-group-style.rs +++ b/src/test/compile-fail/lint-group-style.rs @@ -20,6 +20,7 @@ mod test { #[forbid(bad_style)] //~^ NOTE lint level defined here + //~^^ NOTE lint level defined here mod bad { fn CamelCase() {} //~ ERROR function `CamelCase` should have a snake case name @@ -29,6 +30,7 @@ mod test { mod warn { #![warn(bad_style)] //~^ NOTE lint level defined here + //~| NOTE lint level defined here fn CamelCase() {} //~ WARN function `CamelCase` should have a snake case name diff --git a/src/test/compile-fail/lint-unconditional-recursion.rs b/src/test/compile-fail/lint-unconditional-recursion.rs index bee5a2c45be6d..94e189aa47f6f 100644 --- a/src/test/compile-fail/lint-unconditional-recursion.rs +++ b/src/test/compile-fail/lint-unconditional-recursion.rs @@ -10,7 +10,19 @@ #![deny(unconditional_recursion)] //~^ NOTE lint level defined here - +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here +//~| NOTE lint level defined here #![allow(dead_code)] fn foo() { //~ ERROR function cannot return without recurring foo(); //~ NOTE recursive call site From ef6a07221d66bff1ef8edac4f9ffc39013abf256 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Wed, 26 Oct 2016 23:07:38 -0700 Subject: [PATCH 4/4] deduplicate one-time diagnostics on lint ID as well as span and message Some lint-level attributes (like `bad-style`, or, more dramatically, `warnings`) can affect more than one lint; it seems fairer to point out the attribute once for each distinct lint affected. Also, a UI test is added. This remains in the matter of #24690. --- src/librustc/lint/context.rs | 2 +- src/librustc/session/mod.rs | 17 +++++++-------- src/test/ui/span/issue-24690.rs | 22 ++++++++++++++++++++ src/test/ui/span/issue-24690.stderr | 32 +++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/span/issue-24690.rs create mode 100644 src/test/ui/span/issue-24690.stderr diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index eebf4d32ab26f..cf4115e37cd98 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -452,7 +452,7 @@ pub fn raw_struct_lint<'a>(sess: &'a Session, } if let Some(span) = def { - sess.diag_span_note_once(&mut err, span, "lint level defined here"); + sess.diag_span_note_once(&mut err, lint, span, "lint level defined here"); } err diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 154fe9af79a9a..ce3f2be54b2d5 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -75,10 +75,10 @@ pub struct Session { pub working_dir: PathBuf, pub lint_store: RefCell, pub lints: RefCell>>, - /// Set of (span, message) tuples tracking lint (sub)diagnostics that have - /// been set once, but should not be set again, in order to avoid + /// Set of (LintId, span, message) tuples tracking lint (sub)diagnostics + /// that have been set once, but should not be set again, in order to avoid /// redundantly verbose output (Issue #24690). - pub one_time_diagnostics: RefCell>, + pub one_time_diagnostics: RefCell>, pub plugin_llvm_passes: RefCell>, pub mir_passes: RefCell, pub plugin_attributes: RefCell>, @@ -294,8 +294,8 @@ impl Session { } /// Analogous to calling `.span_note` on the given DiagnosticBuilder, but - /// deduplicates on span and message for this `Session` if we're not - /// outputting in JSON mode. + /// deduplicates on lint ID, span, and message for this `Session` if we're + /// not outputting in JSON mode. // // FIXME: if the need arises for one-time diagnostics other than // `span_note`, we almost certainly want to generalize this @@ -303,7 +303,7 @@ impl Session { // it's not already there" code to accomodate all of them pub fn diag_span_note_once<'a, 'b>(&'a self, diag_builder: &'b mut DiagnosticBuilder<'a>, - span: Span, message: &str) { + lint: &'static lint::Lint, span: Span, message: &str) { match self.opts.error_format { // when outputting JSON for tool consumption, the tool might want // the duplicates @@ -311,8 +311,9 @@ impl Session { diag_builder.span_note(span, &message); }, _ => { - let span_message = (span, message.to_owned()); - let fresh = self.one_time_diagnostics.borrow_mut().insert(span_message); + let lint_id = lint::LintId::of(lint); + let id_span_message = (lint_id, span, message.to_owned()); + let fresh = self.one_time_diagnostics.borrow_mut().insert(id_span_message); if fresh { diag_builder.span_note(span, &message); } diff --git a/src/test/ui/span/issue-24690.rs b/src/test/ui/span/issue-24690.rs new file mode 100644 index 0000000000000..def0d9aced3c3 --- /dev/null +++ b/src/test/ui/span/issue-24690.rs @@ -0,0 +1,22 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A test to ensure that helpful `note` messages aren't emitted more often +//! than necessary. + +// Although there are three errors, we should only get two "lint level defined +// here" notes pointing at the `warnings` span, one for each error type. +#![deny(warnings)] + +fn main() { + let theTwo = 2; + let theOtherTwo = 2; + println!("{}", theTwo); +} diff --git a/src/test/ui/span/issue-24690.stderr b/src/test/ui/span/issue-24690.stderr new file mode 100644 index 0000000000000..0d2a2ef751666 --- /dev/null +++ b/src/test/ui/span/issue-24690.stderr @@ -0,0 +1,32 @@ +error: unused variable: `theOtherTwo` + --> $DIR/issue-24690.rs:20:9 + | +20 | let theOtherTwo = 2; + | ^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/issue-24690.rs:16:9 + | +16 | #![deny(warnings)] + | ^^^^^^^^ + +error: variable `theTwo` should have a snake case name such as `the_two` + --> $DIR/issue-24690.rs:19:9 + | +19 | let theTwo = 2; + | ^^^^^^ + | +note: lint level defined here + --> $DIR/issue-24690.rs:16:9 + | +16 | #![deny(warnings)] + | ^^^^^^^^ + +error: variable `theOtherTwo` should have a snake case name such as `the_other_two` + --> $DIR/issue-24690.rs:20:9 + | +20 | let theOtherTwo = 2; + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors +