Skip to content

Commit 7d883cd

Browse files
committed
Extract diagnostics module and document some functions
This moves the lint building functions from `utils/mod.rs` to their own `utils/diagnostics.rs` file. Also adds documentation for three of them.
1 parent ff1b1a7 commit 7d883cd

File tree

2 files changed

+208
-199
lines changed

2 files changed

+208
-199
lines changed

clippy_lints/src/utils/diagnostics.rs

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
//! Clippy wrappers around rustc's diagnostic functions.
2+
3+
use crate::reexport::*;
4+
use rustc::lint::{LateContext, Lint, LintContext};
5+
use rustc_errors::{Applicability, CodeSuggestion, Substitution, SubstitutionPart, SuggestionStyle};
6+
use std::env;
7+
use syntax::errors::DiagnosticBuilder;
8+
use syntax::source_map::Span;
9+
10+
/// Wrapper around `DiagnosticBuilder` that adds a link to Clippy documentation for the emitted lint
11+
pub struct DiagnosticWrapper<'a>(pub DiagnosticBuilder<'a>);
12+
13+
impl<'a> Drop for DiagnosticWrapper<'a> {
14+
fn drop(&mut self) {
15+
self.0.emit();
16+
}
17+
}
18+
19+
impl<'a> DiagnosticWrapper<'a> {
20+
fn docs_link(&mut self, lint: &'static Lint) {
21+
if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
22+
self.0.help(&format!(
23+
"for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
24+
&option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
25+
// extract just major + minor version and ignore patch versions
26+
format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
27+
}),
28+
lint.name_lower().replacen("clippy::", "", 1)
29+
));
30+
}
31+
}
32+
}
33+
34+
/// Emit a basic lint message with a `msg` and a `span`.
35+
///
36+
/// This is the most primitive of our lint emission methods and can
37+
/// be a good way to get a new lint started.
38+
///
39+
/// Usually it's nicer to provide more context for lint messages.
40+
/// Be sure the output is understandable when you use this method.
41+
///
42+
/// # Example
43+
///
44+
/// ```ignore
45+
/// error: usage of mem::forget on Drop type
46+
/// --> $DIR/mem_forget.rs:17:5
47+
/// |
48+
/// 17 | std::mem::forget(seven);
49+
/// | ^^^^^^^^^^^^^^^^^^^^^^^
50+
/// ```
51+
pub fn span_lint<'a, T: LintContext<'a>>(cx: &T, lint: &'static Lint, sp: Span, msg: &str) {
52+
DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg)).docs_link(lint);
53+
}
54+
55+
/// Same as `span_lint` but with an extra `help` message.
56+
///
57+
/// Use this if you want to provide some general help but
58+
/// can't provide a specific machine applicable suggestion.
59+
///
60+
/// The `help` message is not attached to any `Span`.
61+
///
62+
/// # Example
63+
///
64+
/// ```ignore
65+
/// error: constant division of 0.0 with 0.0 will always result in NaN
66+
/// --> $DIR/zero_div_zero.rs:6:25
67+
/// |
68+
/// 6 | let other_f64_nan = 0.0f64 / 0.0;
69+
/// | ^^^^^^^^^^^^
70+
/// |
71+
/// = help: Consider using `std::f64::NAN` if you would like a constant representing NaN
72+
/// ```
73+
pub fn span_help_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
74+
cx: &'a T,
75+
lint: &'static Lint,
76+
span: Span,
77+
msg: &str,
78+
help: &str,
79+
) {
80+
let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg));
81+
db.0.help(help);
82+
db.docs_link(lint);
83+
}
84+
85+
/// Like `span_lint` but with a `note` section instead of a `help` message.
86+
///
87+
/// The `note` message is presented separately from the main lint message
88+
/// and is attached to a specific span:
89+
///
90+
/// # Example
91+
///
92+
/// ```ignore
93+
/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
94+
/// --> $DIR/drop_forget_ref.rs:10:5
95+
/// |
96+
/// 10 | forget(&SomeStruct);
97+
/// | ^^^^^^^^^^^^^^^^^^^
98+
/// |
99+
/// = note: `-D clippy::forget-ref` implied by `-D warnings`
100+
/// note: argument has type &SomeStruct
101+
/// --> $DIR/drop_forget_ref.rs:10:12
102+
/// |
103+
/// 10 | forget(&SomeStruct);
104+
/// | ^^^^^^^^^^^
105+
/// ```
106+
pub fn span_note_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
107+
cx: &'a T,
108+
lint: &'static Lint,
109+
span: Span,
110+
msg: &str,
111+
note_span: Span,
112+
note: &str,
113+
) {
114+
let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg));
115+
if note_span == span {
116+
db.0.note(note);
117+
} else {
118+
db.0.span_note(note_span, note);
119+
}
120+
db.docs_link(lint);
121+
}
122+
123+
pub fn span_lint_and_then<'a, 'tcx: 'a, T: LintContext<'tcx>, F>(
124+
cx: &'a T,
125+
lint: &'static Lint,
126+
sp: Span,
127+
msg: &str,
128+
f: F,
129+
) where
130+
F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
131+
{
132+
let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg));
133+
f(&mut db.0);
134+
db.docs_link(lint);
135+
}
136+
137+
pub fn span_lint_node(cx: &LateContext<'_, '_>, lint: &'static Lint, node: NodeId, sp: Span, msg: &str) {
138+
DiagnosticWrapper(cx.tcx.struct_span_lint_node(lint, node, sp, msg)).docs_link(lint);
139+
}
140+
141+
pub fn span_lint_node_and_then(
142+
cx: &LateContext<'_, '_>,
143+
lint: &'static Lint,
144+
node: NodeId,
145+
sp: Span,
146+
msg: &str,
147+
f: impl FnOnce(&mut DiagnosticBuilder<'_>),
148+
) {
149+
let mut db = DiagnosticWrapper(cx.tcx.struct_span_lint_node(lint, node, sp, msg));
150+
f(&mut db.0);
151+
db.docs_link(lint);
152+
}
153+
154+
/// Add a span lint with a suggestion on how to fix it.
155+
///
156+
/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.
157+
/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
158+
/// 2)"`.
159+
///
160+
/// ```ignore
161+
/// error: This `.fold` can be more succinctly expressed as `.any`
162+
/// --> $DIR/methods.rs:390:13
163+
/// |
164+
/// 390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2);
165+
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
166+
/// |
167+
/// = note: `-D fold-any` implied by `-D warnings`
168+
/// ```
169+
pub fn span_lint_and_sugg<'a, 'tcx: 'a, T: LintContext<'tcx>>(
170+
cx: &'a T,
171+
lint: &'static Lint,
172+
sp: Span,
173+
msg: &str,
174+
help: &str,
175+
sugg: String,
176+
applicability: Applicability,
177+
) {
178+
span_lint_and_then(cx, lint, sp, msg, |db| {
179+
db.span_suggestion(sp, help, sugg, applicability);
180+
});
181+
}
182+
183+
/// Create a suggestion made from several `span → replacement`.
184+
///
185+
/// Note: in the JSON format (used by `compiletest_rs`), the help message will
186+
/// appear once per
187+
/// replacement. In human-readable format though, it only appears once before
188+
/// the whole suggestion.
189+
pub fn multispan_sugg<I>(db: &mut DiagnosticBuilder<'_>, help_msg: String, sugg: I)
190+
where
191+
I: IntoIterator<Item = (Span, String)>,
192+
{
193+
let sugg = CodeSuggestion {
194+
substitutions: vec![Substitution {
195+
parts: sugg
196+
.into_iter()
197+
.map(|(span, snippet)| SubstitutionPart { snippet, span })
198+
.collect(),
199+
}],
200+
msg: help_msg,
201+
style: SuggestionStyle::ShowCode,
202+
applicability: Applicability::Unspecified,
203+
};
204+
db.suggestions.push(sugg);
205+
}

0 commit comments

Comments
 (0)