Skip to content

Commit f26eedb

Browse files
authored
Auto merge of #37191 - zackmdavis:we_heard_you_the_first_time_really, r=nikomatsakis
introing one-time diagnostics: only emit "lint level defined here" once This is a revised resubmission of PR #34084 (which was closed due to inactivity on account of time constraints on the author's part). --- 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. --- r? @nikomatsakis
2 parents bf5b824 + ef6a072 commit f26eedb

File tree

4 files changed

+90
-3
lines changed

4 files changed

+90
-3
lines changed

src/librustc/lint/context.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -452,8 +452,7 @@ pub fn raw_struct_lint<'a>(sess: &'a Session,
452452
}
453453

454454
if let Some(span) = def {
455-
let explanation = "lint level defined here";
456-
err.span_note(span, &explanation);
455+
sess.diag_span_note_once(&mut err, lint, span, "lint level defined here");
457456
}
458457

459458
err

src/librustc/session/mod.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use middle::dependency_format;
1717
use session::search_paths::PathKind;
1818
use session::config::DebugInfoLevel;
1919
use ty::tls;
20-
use util::nodemap::{NodeMap, FnvHashMap};
20+
use util::nodemap::{NodeMap, FnvHashMap, FnvHashSet};
2121
use util::common::duration_to_secs_str;
2222
use mir::transform as mir_pass;
2323

@@ -75,6 +75,10 @@ pub struct Session {
7575
pub working_dir: PathBuf,
7676
pub lint_store: RefCell<lint::LintStore>,
7777
pub lints: RefCell<NodeMap<Vec<(lint::LintId, Span, String)>>>,
78+
/// Set of (LintId, span, message) tuples tracking lint (sub)diagnostics
79+
/// that have been set once, but should not be set again, in order to avoid
80+
/// redundantly verbose output (Issue #24690).
81+
pub one_time_diagnostics: RefCell<FnvHashSet<(lint::LintId, Span, String)>>,
7882
pub plugin_llvm_passes: RefCell<Vec<String>>,
7983
pub mir_passes: RefCell<mir_pass::Passes>,
8084
pub plugin_attributes: RefCell<Vec<(String, AttributeType)>>,
@@ -288,6 +292,35 @@ impl Session {
288292
pub fn diagnostic<'a>(&'a self) -> &'a errors::Handler {
289293
&self.parse_sess.span_diagnostic
290294
}
295+
296+
/// Analogous to calling `.span_note` on the given DiagnosticBuilder, but
297+
/// deduplicates on lint ID, span, and message for this `Session` if we're
298+
/// not outputting in JSON mode.
299+
//
300+
// FIXME: if the need arises for one-time diagnostics other than
301+
// `span_note`, we almost certainly want to generalize this
302+
// "check/insert-into the one-time diagnostics map, then set message if
303+
// it's not already there" code to accomodate all of them
304+
pub fn diag_span_note_once<'a, 'b>(&'a self,
305+
diag_builder: &'b mut DiagnosticBuilder<'a>,
306+
lint: &'static lint::Lint, span: Span, message: &str) {
307+
match self.opts.error_format {
308+
// when outputting JSON for tool consumption, the tool might want
309+
// the duplicates
310+
config::ErrorOutputType::Json => {
311+
diag_builder.span_note(span, &message);
312+
},
313+
_ => {
314+
let lint_id = lint::LintId::of(lint);
315+
let id_span_message = (lint_id, span, message.to_owned());
316+
let fresh = self.one_time_diagnostics.borrow_mut().insert(id_span_message);
317+
if fresh {
318+
diag_builder.span_note(span, &message);
319+
}
320+
}
321+
}
322+
}
323+
291324
pub fn codemap<'a>(&'a self) -> &'a codemap::CodeMap {
292325
self.parse_sess.codemap()
293326
}
@@ -561,6 +594,7 @@ pub fn build_session_(sopts: config::Options,
561594
working_dir: env::current_dir().unwrap(),
562595
lint_store: RefCell::new(lint::LintStore::new()),
563596
lints: RefCell::new(NodeMap()),
597+
one_time_diagnostics: RefCell::new(FnvHashSet()),
564598
plugin_llvm_passes: RefCell::new(Vec::new()),
565599
mir_passes: RefCell::new(mir_pass::Passes::new()),
566600
plugin_attributes: RefCell::new(Vec::new()),

src/test/ui/span/issue-24690.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! A test to ensure that helpful `note` messages aren't emitted more often
12+
//! than necessary.
13+
14+
// Although there are three errors, we should only get two "lint level defined
15+
// here" notes pointing at the `warnings` span, one for each error type.
16+
#![deny(warnings)]
17+
18+
fn main() {
19+
let theTwo = 2;
20+
let theOtherTwo = 2;
21+
println!("{}", theTwo);
22+
}

src/test/ui/span/issue-24690.stderr

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
error: unused variable: `theOtherTwo`
2+
--> $DIR/issue-24690.rs:20:9
3+
|
4+
20 | let theOtherTwo = 2;
5+
| ^^^^^^^^^^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/issue-24690.rs:16:9
9+
|
10+
16 | #![deny(warnings)]
11+
| ^^^^^^^^
12+
13+
error: variable `theTwo` should have a snake case name such as `the_two`
14+
--> $DIR/issue-24690.rs:19:9
15+
|
16+
19 | let theTwo = 2;
17+
| ^^^^^^
18+
|
19+
note: lint level defined here
20+
--> $DIR/issue-24690.rs:16:9
21+
|
22+
16 | #![deny(warnings)]
23+
| ^^^^^^^^
24+
25+
error: variable `theOtherTwo` should have a snake case name such as `the_other_two`
26+
--> $DIR/issue-24690.rs:20:9
27+
|
28+
20 | let theOtherTwo = 2;
29+
| ^^^^^^^^^^^
30+
31+
error: aborting due to 3 previous errors
32+

0 commit comments

Comments
 (0)