|
| 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