diff options
| author | jumbatm <jumbatm@gmail.com> | 2020-01-31 22:24:57 +1000 |
|---|---|---|
| committer | jumbatm <jumbatm@gmail.com> | 2020-02-11 19:47:40 +1000 |
| commit | 7c58ffe874142e1943e2674d7e75989938a7d21b (patch) | |
| tree | 1a8486c1492d3038d9cc6e4032c69f2aa10a4d99 | |
| parent | dc4242d9052a42cdf329c3a2430d02a3b3d415cb (diff) | |
| download | rust-7c58ffe874142e1943e2674d7e75989938a7d21b.tar.gz rust-7c58ffe874142e1943e2674d7e75989938a7d21b.zip | |
Invert control in struct_lint_level.
Caller now passes in a `decorate` function, which is only run if the lint is allowed.
51 files changed, 1299 insertions, 1130 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 57a52a991ed..01390f2c719 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -1701,7 +1701,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { sub: Region<'tcx>, ) { self.construct_generic_bound_failure(region_scope_tree, span, origin, bound_kind, sub) - .emit() + .emit(); } pub fn construct_generic_bound_failure( diff --git a/src/librustc/lint.rs b/src/librustc/lint.rs index 8c18b1368a9..e7291d7a4ab 100644 --- a/src/librustc/lint.rs +++ b/src/librustc/lint.rs @@ -11,6 +11,7 @@ use rustc_span::hygiene::MacroKind; use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan}; use rustc_span::{Span, Symbol}; + /// How a lint level was set. #[derive(Clone, Copy, PartialEq, Eq, HashStable)] pub enum LintSource { @@ -174,20 +175,37 @@ impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap { } } -pub fn struct_lint_level<'a>( - sess: &'a Session, + +pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a>); + +impl<'a> LintDiagnosticBuilder<'a> { + /// Return the inner DiagnosticBuilder, first setting the primary message to `msg`. + pub fn build(mut self, msg: &str) -> DiagnosticBuilder<'a> { + self.0.set_primary_message(msg); + self.0 + } + + /// Create a LintDiagnosticBuilder from some existing DiagnosticBuilder. + pub fn new(err: DiagnosticBuilder<'a>) -> LintDiagnosticBuilder<'a>{ + LintDiagnosticBuilder(err) + } +} + +pub fn struct_lint_level<'s>( + sess: &'s Session, lint: &'static Lint, level: Level, src: LintSource, span: Option<MultiSpan>, - msg: &str, -) -> DiagnosticBuilder<'a> { + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)) { + + // FIXME: Move the guts of this function into a fn which takes dyn Fn to reduce code bloat. let mut err = match (level, span) { - (Level::Allow, _) => return sess.diagnostic().struct_dummy(), - (Level::Warn, Some(span)) => sess.struct_span_warn(span, msg), - (Level::Warn, None) => sess.struct_warn(msg), - (Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => sess.struct_span_err(span, msg), - (Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(msg), + (Level::Allow, _) => { return; }, + (Level::Warn, Some(span)) => sess.struct_span_warn(span, ""), + (Level::Warn, None) => sess.struct_warn(""), + (Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => sess.struct_span_err(span, ""), + (Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(""), }; // Check for future incompatibility lints and issue a stronger warning. @@ -209,7 +227,7 @@ pub fn struct_lint_level<'a>( err.cancel(); // Don't continue further, since we don't want to have // `diag_span_note_once` called for a diagnostic that isn't emitted. - return err; + return; } } @@ -299,7 +317,8 @@ pub fn struct_lint_level<'a>( err.note(&citation); } - return err; + // Finally, run `decorate`. This function is also responsible for emitting the diagnostic. + decorate(LintDiagnosticBuilder::new(err)); } /// Returns whether `span` originates in a foreign crate's external macro. diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 7cbe77b9e82..dd30fb23c2e 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -222,11 +222,13 @@ fn late_report_deprecation( return; } - let mut diag = tcx.struct_span_lint_hir(lint, hir_id, span, message); - if let hir::Node::Expr(_) = tcx.hir().get(hir_id) { - deprecation_suggestion(&mut diag, suggestion, span); - } - diag.emit(); + tcx.struct_span_lint_hir(lint, hir_id, span, |lint| { + let mut diag = lint.build(message); + if let hir::Node::Expr(_) = tcx.hir().get(hir_id) { + deprecation_suggestion(&mut diag, suggestion, span); + } + diag.emit() + }); if hir_id == hir::DUMMY_HIR_ID { span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id); } @@ -387,8 +389,11 @@ impl<'tcx> TyCtxt<'tcx> { /// Additionally, this function will also check if the item is deprecated. If so, and `id` is /// not `None`, a deprecated lint attached to `id` will be emitted. pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) { - let soft_handler = - |lint, span, msg: &_| self.lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg); + let soft_handler = |lint, span, msg: &_| { + self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| { + lint.build(msg).emit() + }) + }; match self.eval_stability(def_id, id, span) { EvalResult::Allow => {} EvalResult::Deny { feature, reason, issue, is_soft } => { diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 349dbd74ad1..bb84458cf7b 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -83,18 +83,15 @@ impl<'tcx> ConstEvalErr<'tcx> { &self, tcx: TyCtxtAt<'tcx>, message: &str, - ) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> { - self.struct_generic(tcx, message, None) + emit: impl FnOnce(DiagnosticBuilder<'_>), + ) -> Result<(), ErrorHandled> { + self.struct_generic(tcx, message, emit, None) } pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled { - let err = self.struct_error(tcx, message); - match err { - Ok(mut err) => { - err.emit(); - ErrorHandled::Reported - } - Err(err) => err, + match self.struct_error(tcx, message, |mut e| e.emit()) { + Ok(_) => ErrorHandled::Reported, + Err(x) => x, } } @@ -105,9 +102,11 @@ impl<'tcx> ConstEvalErr<'tcx> { lint_root: hir::HirId, span: Option<Span>, ) -> ErrorHandled { - let lint = self.struct_generic(tcx, message, Some(lint_root)); - match lint { - Ok(mut lint) => { + match self.struct_generic( + tcx, + message, + |mut lint: DiagnosticBuilder<'_>| { + // Apply the span. if let Some(span) = span { let primary_spans = lint.span.primary_spans().to_vec(); // point at the actual error as the primary span @@ -121,18 +120,26 @@ impl<'tcx> ConstEvalErr<'tcx> { } } lint.emit(); + } + , Some(lint_root)) { + Ok(_) => { ErrorHandled::Reported } Err(err) => err, } } + /// Sets the message passed in via `message`, then adds the span labels for you, before applying + /// further modifications in `emit`. It's up to you to call emit(), stash(..), etc. within the + /// `emit` method. If you don't need to do any additional processing, just use + /// struct_generic. fn struct_generic( &self, tcx: TyCtxtAt<'tcx>, message: &str, + emit: impl FnOnce(DiagnosticBuilder<'_>), lint_root: Option<hir::HirId>, - ) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> { + ) -> Result<(), ErrorHandled> { let must_error = match self.error { InterpError::MachineStop(_) => bug!("CTFE does not stop"), err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { @@ -143,7 +150,22 @@ impl<'tcx> ConstEvalErr<'tcx> { _ => false, }; trace!("reporting const eval failure at {:?}", self.span); - let mut err = if let (Some(lint_root), false) = (lint_root, must_error) { + + let add_span_labels = |err: &mut DiagnosticBuilder<'_>| { + if !must_error { + err.span_label(self.span, self.error.to_string()); + } + // Skip the last, which is just the environment of the constant. The stacktrace + // is sometimes empty because we create "fake" eval contexts in CTFE to do work + // on constant values. + if self.stacktrace.len() > 0 { + for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] { + err.span_label(frame_info.call_site, frame_info.to_string()); + } + } + }; + + if let (Some(lint_root), false) = (lint_root, must_error) { let hir_id = self .stacktrace .iter() @@ -155,25 +177,22 @@ impl<'tcx> ConstEvalErr<'tcx> { rustc_session::lint::builtin::CONST_ERR, hir_id, tcx.span, - message, - ) - } else if must_error { - struct_error(tcx, &self.error.to_string()) + |lint| { + let mut err = lint.build(message); + add_span_labels(&mut err); + emit(err); + }, + ); } else { - struct_error(tcx, message) + let mut err = if must_error { + struct_error(tcx, &self.error.to_string()) + } else { + struct_error(tcx, message) + }; + add_span_labels(&mut err); + emit(err); }; - if !must_error { - err.span_label(self.span, self.error.to_string()); - } - // Skip the last, which is just the environment of the constant. The stacktrace - // is sometimes empty because we create "fake" eval contexts in CTFE to do work - // on constant values. - if self.stacktrace.len() > 0 { - for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] { - err.span_label(frame_info.call_site, frame_info.to_string()); - } - } - Ok(err) + Ok(()) } } diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs index 3c886ce7f3e..0e6347897d2 100644 --- a/src/librustc/traits/object_safety.rs +++ b/src/librustc/traits/object_safety.rs @@ -227,37 +227,39 @@ fn object_safety_violations_for_trait( { // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. // It's also hard to get a use site span, so we use the method definition span. - let mut err = tcx.struct_span_lint_hir( + tcx.struct_span_lint_hir( WHERE_CLAUSES_OBJECT_SAFETY, hir::CRATE_HIR_ID, *span, - &format!( - "the trait `{}` cannot be made into an object", - tcx.def_path_str(trait_def_id) - ), + |lint| { + let mut err = lint.build(&format!( + "the trait `{}` cannot be made into an object", + tcx.def_path_str(trait_def_id) + )); + let node = tcx.hir().get_if_local(trait_def_id); + let msg = if let Some(hir::Node::Item(item)) = node { + err.span_label(item.ident.span, "this trait cannot be made into an object..."); + format!("...because {}", violation.error_msg()) + } else { + format!( + "the trait cannot be made into an object because {}", + violation.error_msg() + ) + }; + err.span_label(*span, &msg); + match (node, violation.solution()) { + (Some(_), Some((note, None))) => { + err.help(¬e); + } + (Some(_), Some((note, Some((sugg, span))))) => { + err.span_suggestion(span, ¬e, sugg, Applicability::MachineApplicable); + } + // Only provide the help if its a local trait, otherwise it's not actionable. + _ => {} + } + err.emit(); + }, ); - let node = tcx.hir().get_if_local(trait_def_id); - let msg = if let Some(hir::Node::Item(item)) = node { - err.span_label(item.ident.span, "this trait cannot be made into an object..."); - format!("...because {}", violation.error_msg()) - } else { - format!( - "the trait cannot be made into an object because {}", - violation.error_msg() - ) - }; - err.span_label(*span, &msg); - match (node, violation.solution()) { - (Some(_), Some((note, None))) => { - err.help(¬e); - } - (Some(_), Some((note, Some((sugg, span))))) => { - err.span_suggestion(span, ¬e, sugg, Applicability::MachineApplicable); - } - // Only provide the help if its a local trait, otherwise it's not actionable. - _ => {} - } - err.emit(); false } else { true diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index 071b5277dd9..33a78ae896b 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -22,6 +22,7 @@ use rustc_hir::def_id::DefId; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; use rustc_span::DUMMY_SP; +use rustc::lint::LintDiagnosticBuilder; use super::util::impl_trait_ref_and_oblig; use super::{FulfillmentContext, SelectionContext}; @@ -317,22 +318,69 @@ pub(super) fn specialization_graph_provider( }; if let Some(overlap) = overlap { - let msg = format!( - "conflicting implementations of trait `{}`{}:{}", - overlap.trait_desc, - overlap - .self_desc - .clone() - .map_or(String::new(), |ty| { format!(" for type `{}`", ty) }), - match used_to_be_allowed { - Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)", - _ => "", - } - ); let impl_span = tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap()); - let mut err = match used_to_be_allowed { - None => struct_span_err!(tcx.sess, impl_span, E0119, "{}", msg), + + // Work to be done after we've built the DiagnosticBuilder. We have to define it + // now because the struct_lint methods don't return back the DiagnosticBuilder + // that's passed in. + let decorate = |err: LintDiagnosticBuilder<'_>| { + let msg = format!( + "conflicting implementations of trait `{}`{}:{}", + overlap.trait_desc, + overlap + .self_desc + .clone() + .map_or(String::new(), |ty| { format!(" for type `{}`", ty) }), + match used_to_be_allowed { + Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)", + _ => "", + } + ); + let mut err = err.build(&msg); + match tcx.span_of_impl(overlap.with_impl) { + Ok(span) => { + err.span_label( + tcx.sess.source_map().def_span(span), + "first implementation here".to_string(), + ); + + err.span_label( + impl_span, + format!( + "conflicting implementation{}", + overlap + .self_desc + .map_or(String::new(), |ty| format!(" for `{}`", ty)) + ), + ); + } + Err(cname) => { + let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { + Some(s) => { + format!("conflicting implementation in crate `{}`:\n- {}", cname, s) + } + None => format!("conflicting implementation in crate `{}`", cname), + }; + err.note(&msg); + } + } + + for cause in &overlap.intercrate_ambiguity_causes { + cause.add_intercrate_ambiguity_hint(&mut err); + } + + if overlap.involves_placeholder { + coherence::add_placeholder_note(&mut err); + } + err.emit() + }; + + match used_to_be_allowed { + None => { + let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); + decorate(LintDiagnosticBuilder::new(err)); + } Some(kind) => { let lint = match kind { FutureCompatOverlapErrorKind::Issue33140 => { @@ -344,47 +392,11 @@ pub(super) fn specialization_graph_provider( lint, tcx.hir().as_local_hir_id(impl_def_id).unwrap(), impl_span, - &msg, + decorate, ) } }; - match tcx.span_of_impl(overlap.with_impl) { - Ok(span) => { - err.span_label( - tcx.sess.source_map().def_span(span), - "first implementation here".to_string(), - ); - err.span_label( - impl_span, - format!( - "conflicting implementation{}", - overlap - .self_desc - .map_or(String::new(), |ty| format!(" for `{}`", ty)) - ), - ); - } - Err(cname) => { - let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { - Some(s) => { - format!("conflicting implementation in crate `{}`:\n- {}", cname, s) - } - None => format!("conflicting implementation in crate `{}`", cname), - }; - err.note(&msg); - } - } - - for cause in &overlap.intercrate_ambiguity_causes { - cause.add_intercrate_ambiguity_hint(&mut err); - } - - if overlap.involves_placeholder { - coherence::add_placeholder_note(&mut err); - } - - err.emit(); } } else { let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 92c5600362e..a885f9e9600 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -42,6 +42,7 @@ use crate::ty::{InferConst, ParamConst}; use crate::ty::{List, TyKind, TyS}; use crate::util::common::ErrorReported; use rustc_attr as attr; +use rustc::lint::LintDiagnosticBuilder; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; @@ -49,7 +50,6 @@ use rustc_data_structures::stable_hasher::{ hash_stable_hashmap, HashStable, StableHasher, StableVec, }; use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; -use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, DefIndex, LOCAL_CRATE}; @@ -2551,16 +2551,6 @@ impl<'tcx> TyCtxt<'tcx> { iter.intern_with(|xs| self.intern_goals(xs)) } - pub fn lint_hir( - self, - lint: &'static Lint, - hir_id: HirId, - span: impl Into<MultiSpan>, - msg: &str, - ) { - self.struct_span_lint_hir(lint, hir_id, span.into(), msg).emit() - } - /// Walks upwards from `id` to find a node which might change lint levels with attributes. /// It stops at `bound` and just returns it if reached. pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId { @@ -2604,20 +2594,20 @@ impl<'tcx> TyCtxt<'tcx> { lint: &'static Lint, hir_id: HirId, span: impl Into<MultiSpan>, - msg: &str, - ) -> DiagnosticBuilder<'tcx> { + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>) + ) { let (level, src) = self.lint_level_at_node(lint, hir_id); - struct_lint_level(self.sess, lint, level, src, Some(span.into()), msg) + struct_lint_level(self.sess, lint, level, src, Some(span.into()), decorate); } pub fn struct_lint_node( self, lint: &'static Lint, id: HirId, - msg: &str, - ) -> DiagnosticBuilder<'tcx> { + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { let (level, src) = self.lint_level_at_node(lint, id); - struct_lint_level(self.sess, lint, level, src, None, msg) + struct_lint_level(self.sess, lint, level, src, None, decorate); } pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx StableVec<TraitCandidate>> { diff --git a/src/librustc_attr/builtin.rs b/src/librustc_attr/builtin.rs index ab03297fffe..e5381a58dab 100644 --- a/src/librustc_attr/builtin.rs +++ b/src/librustc_attr/builtin.rs @@ -37,7 +37,7 @@ fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) { .span_label(span, format!("expected one of {}", expected.join(", "))) .emit(); } - AttrError::MissingSince => struct_span_err!(diag, span, E0542, "missing 'since'").emit(), + AttrError::MissingSince => { struct_span_err!(diag, span, E0542, "missing 'since'").emit(); }, AttrError::MissingFeature => { struct_span_err!(diag, span, E0546, "missing 'feature'").emit(); } @@ -639,7 +639,7 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F let (cfg, feature, has_feature) = gated_cfg; if !has_feature(features) && !cfg_span.allows_unstable(*feature) { let explain = format!("`cfg({})` is experimental and subject to change", cfg); - feature_err(sess, *feature, cfg_span, &explain).emit() + feature_err(sess, *feature, cfg_span, &explain).emit(); } } diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 5286e8c19b4..189b5bd0f9e 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -194,6 +194,7 @@ impl Diagnostic { found_extra: &dyn fmt::Display, ) -> &mut Self { let expected_label = format!("expected {}", expected_label); + let found_label = format!("found {}", found_label); let (found_padding, expected_padding) = if expected_label.len() > found_label.len() { (expected_label.len() - found_label.len(), 0) diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index 82bbae18a9c..84bfa07d267 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -106,7 +106,7 @@ impl<'a> DiagnosticBuilder<'a> { /// /// See `emit` and `delay_as_bug` for details. pub fn emit_unless(&mut self, delay: bool) { - if delay { self.delay_as_bug() } else { self.emit() } + if delay { self.delay_as_bug(); } else { self.emit(); } } /// Stashes diagnostic for possible later improvement in a different, @@ -369,6 +369,7 @@ impl<'a> DiagnosticBuilder<'a> { /// Creates a new `DiagnosticBuilder` with an already constructed /// diagnostic. crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> { + debug!("Created new diagnostic"); DiagnosticBuilder(Box::new(DiagnosticBuilderInner { handler, diagnostic, diff --git a/src/librustc_expand/base.rs b/src/librustc_expand/base.rs index e167089b93a..9e583d7e19d 100644 --- a/src/librustc_expand/base.rs +++ b/src/librustc_expand/base.rs @@ -1113,7 +1113,7 @@ pub fn expr_to_string( err_msg: &str, ) -> Option<(Symbol, ast::StrStyle)> { expr_to_spanned_string(cx, expr, err_msg) - .map_err(|err| err.map(|mut err| err.emit())) + .map_err(|err| err.map(|mut err| { err.emit(); })) .ok() .map(|(symbol, style, _)| (symbol, style)) } diff --git a/src/librustc_lint/array_into_iter.rs b/src/librustc_lint/array_into_iter.rs index fb11b6771e9..a91d735622f 100644 --- a/src/librustc_lint/array_into_iter.rs +++ b/src/librustc_lint/array_into_iter.rs @@ -77,13 +77,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter { // to an array or to a slice. _ => bug!("array type coerced to something other than array or slice"), }; - let msg = format!( + cx.struct_span_lint(ARRAY_INTO_ITER, *span, |lint| { + lint.build(&format!( "this method call currently resolves to `<&{} as IntoIterator>::into_iter` (due \ to autoref coercions), but that might change in the future when \ `IntoIterator` impls for arrays are added.", target, - ); - cx.struct_span_lint(ARRAY_INTO_ITER, *span, &msg) + )) .span_suggestion( call.ident.span, "use `.iter()` instead of `.into_iter()` to avoid ambiguity", @@ -91,6 +91,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter { Applicability::MachineApplicable, ) .emit(); + }) } } } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index ca66717ac5e..e84ef713b42 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -77,14 +77,16 @@ impl EarlyLintPass for WhileTrue { if !lit.span.from_expansion() { let msg = "denote infinite loops with `loop { ... }`"; let condition_span = cx.sess.source_map().def_span(e.span); - cx.struct_span_lint(WHILE_TRUE, condition_span, msg) - .span_suggestion_short( + cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| { + lint.build(msg) + .span_suggestion_short( condition_span, "use `loop`", "loop".to_owned(), Applicability::MachineApplicable, ) .emit(); + }) } } } @@ -174,29 +176,31 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns { if cx.tcx.find_field_index(ident, &variant) == Some(cx.tcx.field_index(fieldpat.hir_id, cx.tables)) { - let mut err = cx.struct_span_lint( + cx.struct_span_lint( NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, - &format!("the `{}:` in this pattern is redundant", ident), - ); - let binding = match binding_annot { - hir::BindingAnnotation::Unannotated => None, - hir::BindingAnnotation::Mutable => Some("mut"), - hir::BindingAnnotation::Ref => Some("ref"), - hir::BindingAnnotation::RefMut => Some("ref mut"), - }; - let ident = if let Some(binding) = binding { - format!("{} {}", binding, ident) - } else { - ident.to_string() - }; - err.span_suggestion( - fieldpat.span, - "use shorthand field pattern", - ident, - Applicability::MachineApplicable, + |lint| { + let mut err = lint.build(&format!("the `{}:` in this pattern is redundant", ident)); + let binding = match binding_annot { + hir::BindingAnnotation::Unannotated => None, + hir::BindingAnnotation::Mutable => Some("mut"), + hir::BindingAnnotation::Ref => Some("ref"), + hir::BindingAnnotation::RefMut => Some("ref mut"), + }; + let ident = if let Some(binding) = binding { + format!("{} {}", binding, ident) + } else { + ident.to_string() + }; + err.span_suggestion( + fieldpat.span, + "use shorthand field pattern", + ident, + Applicability::MachineApplicable, + ); + err.emit(); + } ); - err.emit(); } } } @@ -643,17 +647,19 @@ impl EarlyLintPass for AnonymousParameters { cx.struct_span_lint( ANONYMOUS_PARAMETERS, arg.pat.span, - "anonymous parameters are deprecated and will be \ - removed in the next edition.", - ) - .span_suggestion( - arg.pat.span, - "try naming the parameter or explicitly \ - ignoring it", - format!("_: {}", ty_snip), - appl, + |lint| { + lint.build("anonymous parameters are deprecated and will be \ + removed in the next edition.") + .span_suggestion( + arg.pat.span, + "try naming the parameter or explicitly \ + ignoring it", + format!("_: {}", ty_snip), + appl, + ) + .emit(); + }, ) - .emit(); } } _ => (), @@ -687,14 +693,16 @@ fn lint_deprecated_attr( msg: &str, suggestion: Option<&str>, ) { - cx.struct_span_lint(DEPRECATED, attr.span, &msg) - .span_suggestion_short( - attr.span, - suggestion.unwrap_or("remove this attribute"), - String::new(), - Applicability::MachineApplicable, - ) - .emit(); + cx.struct_span_lint(DEPRECATED, attr.span, |lint| { + lint.build(msg) + .span_suggestion_short( + attr.span, + suggestion.unwrap_or("remove this attribute"), + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + }) } impl EarlyLintPass for DeprecatedAttr { @@ -759,21 +767,20 @@ impl UnusedDocComment { let span = sugared_span.take().unwrap_or_else(|| attr.span); if attr.is_doc_comment() || attr.check_name(sym::doc) { - let mut err = cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, "unused doc comment"); - - err.span_label( - node_span, - format!("rustdoc does not generate documentation for {}", node_kind), - ); - - if is_macro_expansion { - err.help( - "to document an item produced by a macro, \ - the macro must produce the documentation as part of its expansion", + cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| { + let mut err = lint.build("unused doc comment"); + err.span_label( + node_span, + format!("rustdoc does not generate documentation for {}", node_kind), ); - } - - err.emit(); + if is_macro_expansion { + err.help( + "to document an item produced by a macro, \ + the macro must produce the documentation as part of its expansion", + ); + } + err.emit(); + }); } } } @@ -831,20 +838,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems { match param.kind { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { - let mut err = cx.struct_span_lint( + cx.struct_span_lint( NO_MANGLE_GENERIC_ITEMS, it.span, - "functions generic over types or consts must be mangled", - ); - err.span_suggestion_short( - no_mangle_attr.span, - "remove this attribute", - String::new(), - // Use of `#[no_mangle]` suggests FFI intent; correct - // fix may be to monomorphize source by hand - Applicability::MaybeIncorrect, + |lint| { + lint.build("functions generic over types or consts must be mangled") + .span_suggestion_short( + no_mangle_attr.span, + "remove this attribute", + String::new(), + // Use of `#[no_mangle]` suggests FFI intent; correct + // fix may be to monomorphize source by hand + Applicability::MaybeIncorrect, + ) + .emit(); + }, ); - err.emit(); break; } } @@ -856,25 +865,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems { // Const items do not refer to a particular location in memory, and therefore // don't have anything to attach a symbol to let msg = "const items should never be `#[no_mangle]`"; - let mut err = cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg); - - // account for "pub const" (#45562) - let start = cx - .tcx - .sess - .source_map() - .span_to_snippet(it.span) - .map(|snippet| snippet.find("const").unwrap_or(0)) - .unwrap_or(0) as u32; - // `const` is 5 chars - let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5)); - err.span_suggestion( - const_span, - "try a static value", - "pub static".to_owned(), - Applicability::MachineApplicable, - ); - err.emit(); + cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| { + let mut err = lint.build(msg); + + // account for "pub const" (#45562) + let start = cx + .tcx + .sess + .source_map() + .span_to_snippet(it.span) + .map(|snippet| snippet.find("const").unwrap_or(0)) + .unwrap_or(0) as u32; + // `const` is 5 chars + let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5)); + err.span_suggestion( + const_span, + "try a static value", + "pub static".to_owned(), + Applicability::MachineApplicable, + ); + err.emit(); + }); } } _ => {} @@ -984,28 +995,30 @@ impl UnreachablePub { applicability = Applicability::MaybeIncorrect; } let def_span = cx.tcx.sess.source_map().def_span(span); - let mut err = cx.struct_span_lint( + cx.struct_span_lint( UNREACHABLE_PUB, def_span, - &format!("unreachable `pub` {}", what), - ); - let replacement = if cx.tcx.features().crate_visibility_modifier { - "crate" - } else { - "pub(crate)" - } - .to_owned(); + |lint| { + let mut err = lint.build(&format!("unreachable `pub` {}", what)); + let replacement = if cx.tcx.features().crate_visibility_modifier { + "crate" + } else { + "pub(crate)" + } + .to_owned(); - err.span_suggestion( - vis.span, - "consider restricting its visibility", - replacement, - applicability, + err.span_suggestion( + vis.span, + "consider restricting its visibility", + replacement, + applicability, + ); + if exportable { + err.help("or consider exporting it for use by other crates"); + } + err.emit(); + } ); - if exportable { - err.help("or consider exporting it for use by other crates"); - } - err.emit(); } _ => {} } @@ -1120,22 +1133,24 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds { .iter() .map(|pred| pred.span()) .collect(); - let mut err = cx.struct_span_lint( + cx.struct_span_lint( TYPE_ALIAS_BOUNDS, spans, - "where clauses are not enforced in type aliases", - ); - err.span_suggestion( - type_alias_generics.where_clause.span_for_predicates_or_empty_place(), - "the clause will not be checked when the type alias is used, and should be removed", - String::new(), - Applicability::MachineApplicable, + |lint| { + let mut err = lint.build("where clauses are not enforced in type aliases"); + err.span_suggestion( + type_alias_generics.where_clause.span_for_predicates_or_empty_place(), + "the clause will not be checked when the type alias is used, and should be removed", + String::new(), + Applicability::MachineApplicable, + ); + if !suggested_changing_assoc_types { + TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); + suggested_changing_assoc_types = true; + } + err.emit(); + }, ); - if !suggested_changing_assoc_types { - TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); - suggested_changing_assoc_types = true; - } - err.emit(); } // The parameters must not have bounds for param in type_alias_generics.params.iter() { @@ -1148,19 +1163,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds { }) .collect(); if !spans.is_empty() { - let mut err = cx.struct_span_lint( + cx.struct_span_lint( TYPE_ALIAS_BOUNDS, spans, - "bounds on generic parameters are not enforced in type aliases", + |lint| { + let mut err = lint.build("bounds on generic parameters are not enforced in type aliases"); + let msg = "the bound will not be checked when the type alias is used, \ + and should be removed"; + err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable); + if !suggested_changing_assoc_types { + TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); + suggested_changing_assoc_types = true; + } + err.emit(); + }, ); - let msg = "the bound will not be checked when the type alias is used, \ - and should be removed"; - err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable); - if !suggested_changing_assoc_types { - TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); - suggested_changing_assoc_types = true; - } - err.emit(); } } } @@ -1322,23 +1339,27 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { Some(start) => format!("&({}..={})", expr_to_string(&start), end), None => format!("&(..={})", end), }; - let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg); - err.span_suggestion( - pat.span, - suggestion, - replace, - Applicability::MachineApplicable, - ); - err.emit(); + cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| { + lint.build(msg) + .span_suggestion( + pat.span, + suggestion, + replace, + Applicability::MachineApplicable, + ) + .emit(); + }); } else { - let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, msg); - err.span_suggestion_short( - join, - suggestion, - "..=".to_owned(), - Applicability::MachineApplicable, - ); - err.emit(); + cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| { + lint.build(msg) + .span_suggestion_short( + join, + suggestion, + "..=".to_owned(), + Applicability::MachineApplicable + ) + .emit(); + }); }; } } @@ -1384,7 +1405,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestItems { } if let Some(attr) = attr::find_by_name(&it.attrs, sym::rustc_test_marker) { - cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, "cannot test inner items").emit(); + cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| lint.build("cannot test inner items").emit()); } } @@ -1465,18 +1486,20 @@ impl KeywordIdents { return; } - let mut lint = cx.struct_span_lint( + cx.struct_span_lint( KEYWORD_IDENTS, ident.span, - &format!("`{}` is a keyword in the {} edition", ident, next_edition), - ); - lint.span_suggestion( - ident.span, - "you can use a raw identifier to stay compatible", - format!("r#{}", ident), - Applicability::MachineApplicable, + |lint| { + lint.build(&format!("`{}` is a keyword in the {} edition", ident, next_edition)) + .span_suggestion( + ident.span, + "you can use a raw identifier to stay compatible", + format!("r#{}", ident), + Applicability::MachineApplicable, + ) + .emit() + }, ); - lint.emit() } } @@ -1780,17 +1803,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements { } if !lint_spans.is_empty() { - let mut err = cx.struct_span_lint( + cx.struct_span_lint( EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), - "outlives requirements can be inferred", - ); - err.multipart_suggestion( - if bound_count == 1 { "remove this bound" } else { "remove these bounds" }, - lint_spans.into_iter().map(|span| (span, "".to_owned())).collect::<Vec<_>>(), - Applicability::MachineApplicable, + |lint| { + lint.build("outlives requirements can be inferred") + .multipart_suggestion( + if bound_count == 1 { "remove this bound" } else { "remove these bounds" }, + lint_spans.into_iter().map(|span| (span, "".to_owned())).collect::<Vec<_>>(), + Applicability::MachineApplicable, + ) + .emit(); + }, ); - err.emit(); } } } @@ -1820,12 +1845,11 @@ impl EarlyLintPass for IncompleteFeatures { cx.struct_span_lint( INCOMPLETE_FEATURES, span, - &format!( + |lint| lint.build(&format!( "the feature `{}` is incomplete and may cause the compiler to crash", name, - ), + )).emit(), ) - .emit(); }); } } @@ -2015,30 +2039,32 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { // We are extremely conservative with what we warn about. let conjured_ty = cx.tables.expr_ty(expr); if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) { - let mut err = cx.struct_span_lint( + cx.struct_span_lint( INVALID_VALUE, expr.span, - &format!( - "the type `{}` does not permit {}", - conjured_ty, - match init { - InitKind::Zeroed => "zero-initialization", - InitKind::Uninit => "being left uninitialized", - }, - ), - ); - err.span_label(expr.span, "this code causes undefined behavior when executed"); - err.span_label( - expr.span, - "help: use `MaybeUninit<T>` instead, \ - and only call `assume_init` after initialization is done", + |lint| { + let mut err = lint.build(&format!( + "the type `{}` does not permit {}", + conjured_ty, + match init { + InitKind::Zeroed => "zero-initialization", + InitKind::Uninit => "being left uninitialized", + }, + )); + err.span_label(expr.span, "this code causes undefined behavior when executed"); + err.span_label( + expr.span, + "help: use `MaybeUninit<T>` instead, \ + and only call `assume_init` after initialization is done", + ); + if let Some(span) = span { + err.span_note(span, &msg); + } else { + err.note(&msg); + } + err.emit(); + }, ); - if let Some(span) = span { - err.span_note(span, &msg); - } else { - err.note(&msg); - } - err.emit(); } } } diff --git a/src/librustc_lint/context.rs b/src/librustc_lint/context.rs index 3b8cce5635d..7b085f6c2b3 100644 --- a/src/librustc_lint/context.rs +++ b/src/librustc_lint/context.rs @@ -26,7 +26,8 @@ use rustc::ty::layout::{LayoutError, LayoutOf, TyLayout}; use rustc::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_errors::{struct_span_err, Applicability}; +use rustc::lint::LintDiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_session::lint::BuiltinLintDiagnostics; @@ -474,7 +475,7 @@ pub trait LintContext: Sized { fn lints(&self) -> &LintStore; fn lookup_and_emit<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: Option<S>, msg: &str) { - self.lookup(lint, span, msg).emit(); + self.lookup(lint, span, |lint| lint.build(msg).emit()); } fn lookup_and_emit_with_diagnostics<S: Into<MultiSpan>>( @@ -484,95 +485,96 @@ pub trait LintContext: Sized { msg: &str, diagnostic: BuiltinLintDiagnostics, ) { - let mut db = self.lookup(lint, span, msg); - - let sess = self.sess(); - match diagnostic { - BuiltinLintDiagnostics::Normal => (), - BuiltinLintDiagnostics::BareTraitObject(span, is_global) => { - let (sugg, app) = match sess.source_map().span_to_snippet(span) { - Ok(s) if is_global => { - (format!("dyn ({})", s), Applicability::MachineApplicable) - } - Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable), - Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders), - }; - db.span_suggestion(span, "use `dyn`", sugg, app); - } - BuiltinLintDiagnostics::AbsPathWithModule(span) => { - let (sugg, app) = match sess.source_map().span_to_snippet(span) { - Ok(ref s) => { - // FIXME(Manishearth) ideally the emitting code - // can tell us whether or not this is global - let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; - - (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable) - } - Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders), - }; - db.span_suggestion(span, "use `crate`", sugg, app); - } - BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => { - db.span_label( - span, - "names from parent modules are not accessible without an explicit import", - ); - } - BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { - db.span_note(span_def, "the macro is defined here"); - } - BuiltinLintDiagnostics::ElidedLifetimesInPaths( - n, - path_span, - incl_angl_brckt, - insertion_span, - anon_lts, - ) => { - add_elided_lifetime_in_path_suggestion( - sess, - &mut db, + self.lookup(lint, span, |lint| { + let mut db = lint.build(msg); + let sess = self.sess(); + match diagnostic { + BuiltinLintDiagnostics::Normal => (), + BuiltinLintDiagnostics::BareTraitObject(span, is_global) => { + let (sugg, app) = match sess.source_map().span_to_snippet(span) { + Ok(s) if is_global => { + (format!("dyn ({})", s), Applicability::MachineApplicable) + } + Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable), + Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders), + }; + db.span_suggestion(span, "use `dyn`", sugg, app); + } + BuiltinLintDiagnostics::AbsPathWithModule(span) => { + let (sugg, app) = match sess.source_map().span_to_snippet(span) { + Ok(ref s) => { + // FIXME(Manishearth) ideally the emitting code + // can tell us whether or not this is global + let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; + + (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable) + } + Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders), + }; + db.span_suggestion(span, "use `crate`", sugg, app); + } + BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => { + db.span_label( + span, + "names from parent modules are not accessible without an explicit import", + ); + } + BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { + db.span_note(span_def, "the macro is defined here"); + } + BuiltinLintDiagnostics::ElidedLifetimesInPaths( n, path_span, incl_angl_brckt, insertion_span, anon_lts, - ); - } - BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { - db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect); - } - BuiltinLintDiagnostics::UnusedImports(message, replaces) => { - if !replaces.is_empty() { - db.tool_only_multipart_suggestion( - &message, - replaces, - Applicability::MachineApplicable, + ) => { + add_elided_lifetime_in_path_suggestion( + sess, + &mut db, + n, + path_span, + incl_angl_brckt, + insertion_span, + anon_lts, ); } - } - BuiltinLintDiagnostics::RedundantImport(spans, ident) => { - for (span, is_imported) in spans { - let introduced = if is_imported { "imported" } else { "defined" }; - db.span_label( - span, - format!("the item `{}` is already {} here", ident, introduced), - ); + BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { + db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect); + } + BuiltinLintDiagnostics::UnusedImports(message, replaces) => { + if !replaces.is_empty() { + db.tool_only_multipart_suggestion( + &message, + replaces, + Applicability::MachineApplicable, + ); + } + } + BuiltinLintDiagnostics::RedundantImport(spans, ident) => { + for (span, is_imported) in spans { + let introduced = if is_imported { "imported" } else { "defined" }; + db.span_label( + span, + format!("the item `{}` is already {} here", ident, introduced), + ); + } + } + BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => { + stability::deprecation_suggestion(&mut db, suggestion, span) } } - BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => { - stability::deprecation_suggestion(&mut db, suggestion, span) - } - } - db.emit(); + db.emit(); + }); } fn lookup<S: Into<MultiSpan>>( &self, lint: &'static Lint, span: Option<S>, - msg: &str, - ) -> DiagnosticBuilder<'_>; + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ); /// Emit a lint at the appropriate level, for a particular span. fn span_lint<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: S, msg: &str) { @@ -583,9 +585,9 @@ pub trait LintContext: Sized { &self, lint: &'static Lint, span: S, - msg: &str, - ) -> DiagnosticBuilder<'_> { - self.lookup(lint, Some(span), msg) + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>) + ) { + self.lookup(lint, Some(span), decorate); } /// Emit a lint and note at the appropriate level, for a particular span. @@ -597,21 +599,25 @@ pub trait LintContext: Sized { note_span: Span, note: &str, ) { - let mut err = self.lookup(lint, Some(span), msg); - if note_span == span { - err.note(note); - } else { - err.span_note(note_span, note); - } - err.emit(); + self.lookup(lint, Some(span), |lint| { + let mut err = lint.build(msg); + if note_span == span { + err.note(note); + } else { + err.span_note(note_span, note); + } + err.emit(); + }); } /// Emit a lint and help at the appropriate level, for a particular span. fn span_lint_help(&self, lint: &'static Lint, span: Span, msg: &str, help: &str) { - let mut err = self.lookup(lint, Some(span), msg); - self.span_lint(lint, span, msg); - err.span_help(span, help); - err.emit(); + self.lookup(lint, Some(span), |err| { + let mut err = err.build(msg); + self.span_lint(lint, span, msg); + err.span_help(span, help); + err.emit(); + }); } /// Emit a lint at the appropriate level, with no associated span. @@ -654,13 +660,13 @@ impl LintContext for LateContext<'_, '_> { &self, lint: &'static Lint, span: Option<S>, - msg: &str, - ) -> DiagnosticBuilder<'_> { + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { let hir_id = self.last_node_with_lint_attrs; match span { - Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, msg), - None => self.tcx.struct_lint_node(lint, hir_id, msg), + Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, decorate), + None => self.tcx.struct_lint_node(lint, hir_id, decorate), } } } @@ -681,9 +687,9 @@ impl LintContext for EarlyContext<'_> { &self, lint: &'static Lint, span: Option<S>, - msg: &str, - ) -> DiagnosticBuilder<'_> { - self.builder.struct_lint(lint, span.map(|s| s.into()), msg) + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { + self.builder.struct_lint(lint, span.map(|s| s.into()), decorate) } } diff --git a/src/librustc_lint/internal.rs b/src/librustc_lint/internal.rs index 8480c85075d..19778a044a4 100644 --- a/src/librustc_lint/internal.rs +++ b/src/librustc_lint/internal.rs @@ -37,16 +37,19 @@ impl_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]); impl EarlyLintPass for DefaultHashTypes { fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { if let Some(replace) = self.map.get(&ident.name) { + // FIXME: We can avoid a copy here. Would require us to take String instead of &str. let msg = format!("Prefer {} over {}, it has better performance", replace, ident); - let mut db = cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, &msg); - db.span_suggestion( - ident.span, - "use", - replace.to_string(), - Applicability::MaybeIncorrect, // FxHashMap, ... needs another import - ); - db.note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace)) - .emit(); + cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, |lint| { + lint.build(&msg) + .span_suggestion( + ident.span, + "use", + replace.to_string(), + Applicability::MaybeIncorrect, // FxHashMap, ... needs another import + ) + .note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace)) + .emit(); + }); } } } @@ -85,7 +88,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind { if let Some(last) = segments.last() { let span = path.span.with_hi(last.ident.span.hi()); if lint_ty_kind_usage(cx, last) { - cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, "usage of `ty::TyKind::<kind>`") + cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, |lint| { + lint.build("usage of `ty::TyKind::<kind>`") .span_suggestion( span, "try using ty::<kind> directly", @@ -93,6 +97,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind { Applicability::MaybeIncorrect, // ty maybe needs an import ) .emit(); + }) } } } @@ -106,10 +111,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind { cx.struct_span_lint( USAGE_OF_TY_TYKIND, path.span, - "usage of `ty::TyKind`", + |lint| { + lint.build("usage of `ty::TyKind`") + .help("try using `Ty` instead") + .emit(); + }, ) - .help("try using `Ty` instead") - .emit(); } else { if ty.span.from_expansion() { return; @@ -119,16 +126,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind { cx.struct_span_lint( USAGE_OF_QUALIFIED_TY, path.span, - &format!("usage of qualified `ty::{}`", t), + |lint| { + lint.build(&format!("usage of qualified `ty::{}`", t)) + .span_suggestion( + path.span, + "try using it unqualified", + t, + // The import probably needs to be changed + Applicability::MaybeIncorrect, + ) + .emit(); + }, ) - .span_suggestion( - path.span, - "try using it unqualified", - t, - // The import probably needs to be changed - Applicability::MaybeIncorrect, - ) - .emit(); } } } @@ -145,16 +154,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind { cx.struct_span_lint( TY_PASS_BY_REFERENCE, ty.span, - &format!("passing `{}` by reference", t), - ) - .span_suggestion( - ty.span, - "try passing by value", - t, - // Changing type of function argument - Applicability::MaybeIncorrect, + |lint| { + lint.build(&format!("passing `{}` by reference", t)) + .span_suggestion( + ty.span, + "try passing by value", + t, + // Changing type of function argument + Applicability::MaybeIncorrect, + ) + .emit(); + }, ) - .emit(); } } _ => {} @@ -234,10 +245,12 @@ impl EarlyLintPass for LintPassImpl { cx.struct_span_lint( LINT_PASS_IMPL_WITHOUT_MACRO, lint_pass.path.span, - "implementing `LintPass` by hand", + |lint| { + lint.build("implementing `LintPass` by hand") + .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead") + .emit(); + }, ) - .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead") - .emit(); } } } diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs index 4f30d2b2226..6dd899d1461 100644 --- a/src/librustc_lint/levels.rs +++ b/src/librustc_lint/levels.rs @@ -6,7 +6,8 @@ use rustc::ty::query::Providers; use rustc::ty::TyCtxt; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_errors::{struct_span_err, Applicability}; +use rustc::lint::LintDiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::{intravisit, HirId}; @@ -39,8 +40,8 @@ fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap { tcx.arena.alloc(builder.levels.build_map()) } -pub struct LintLevelsBuilder<'a> { - sess: &'a Session, +pub struct LintLevelsBuilder<'s> { + sess: &'s Session, sets: LintLevelSets, id_to_set: FxHashMap<HirId, u32>, cur: u32, @@ -52,8 +53,8 @@ pub struct BuilderPush { pub changed: bool, } -impl<'a> LintLevelsBuilder<'a> { - pub fn new(sess: &'a Session, warn_about_weird_lints: bool, store: &LintStore) -> Self { +impl<'s> LintLevelsBuilder<'s> { + pub fn new(sess: &'s Session, warn_about_weird_lints: bool, store: &LintStore) -> Self { let mut builder = LintLevelsBuilder { sess, sets: LintLevelSets::new(), @@ -245,15 +246,17 @@ impl<'a> LintLevelsBuilder<'a> { lvl, src, Some(li.span().into()), - &msg, - ) - .span_suggestion( - li.span(), - "change it to", - new_lint_name.to_string(), - Applicability::MachineApplicable, - ) - .emit(); + |lint| { + lint.build(&msg) + .span_suggestion( + li.span(), + "change it to", + new_lint_name.to_string(), + Applicability::MachineApplicable, + ) + .emit(); + }, + ); let src = LintSource::Node( Symbol::intern(&new_lint_name), @@ -279,48 +282,51 @@ impl<'a> LintLevelsBuilder<'a> { let lint = builtin::RENAMED_AND_REMOVED_LINTS; let (level, src) = self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess); - let mut err = struct_lint_level( + struct_lint_level( self.sess, lint, level, src, Some(li.span().into()), - &msg, + |lint| { + let mut err = lint.build(&msg); + if let Some(new_name) = renamed { + err.span_suggestion( + li.span(), + "use the new name", + new_name, + Applicability::MachineApplicable, + ); + } + err.emit(); + }, ); - if let Some(new_name) = renamed { - err.span_suggestion( - li.span(), - "use the new name", - new_name, - Applicability::MachineApplicable, - ); - } - err.emit(); } CheckLintNameResult::NoLint(suggestion) => { let lint = builtin::UNKNOWN_LINTS; let (level, src) = self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess); let msg = format!("unknown lint: `{}`", name); - let mut db = struct_lint_level( + struct_lint_level( self.sess, lint, level, src, Some(li.span().into()), - &msg, + |lint| { + let mut db = lint.build(&msg); + if let Some(suggestion) = suggestion { + db.span_suggestion( + li.span(), + "did you mean", + suggestion.to_string(), + Applicability::MachineApplicable, + ); + } + db.emit(); + }, ); - if let Some(suggestion) = suggestion { - db.span_suggestion( - li.span(), - "did you mean", - suggestion.to_string(), - Applicability::MachineApplicable, - ); - } - - db.emit(); } } } @@ -390,10 +396,10 @@ impl<'a> LintLevelsBuilder<'a> { &self, lint: &'static Lint, span: Option<MultiSpan>, - msg: &str, - ) -> DiagnosticBuilder<'a> { + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { let (level, src) = self.sets.get_lint_level(lint, self.cur, None, self.sess); - struct_lint_level(self.sess, lint, level, src, span, msg) + struct_lint_level(self.sess, lint, level, src, span, decorate) } /// Registers the ID provided with the current set of lints stored in diff --git a/src/librustc_lint/non_ascii_idents.rs b/src/librustc_lint/non_ascii_idents.rs index 3c85a1b31b2..df91df8f8b5 100644 --- a/src/librustc_lint/non_ascii_idents.rs +++ b/src/librustc_lint/non_ascii_idents.rs @@ -25,16 +25,14 @@ impl EarlyLintPass for NonAsciiIdents { cx.struct_span_lint( NON_ASCII_IDENTS, ident.span, - "identifier contains non-ASCII characters", - ) - .emit(); + |lint| lint.build("identifier contains non-ASCII characters").emit(), + ); if !name_str.chars().all(GeneralSecurityProfile::identifier_allowed) { cx.struct_span_lint( UNCOMMON_CODEPOINTS, ident.span, - "identifier contains uncommon Unicode codepoints", + |lint| lint.build("identifier contains uncommon Unicode codepoints").emit(), ) - .emit(); } } } diff --git a/src/librustc_lint/nonstandard_style.rs b/src/librustc_lint/nonstandard_style.rs index 6fdbfea7f03..9e291d2cb74 100644 --- a/src/librustc_lint/nonstandard_style.rs +++ b/src/librustc_lint/nonstandard_style.rs @@ -108,14 +108,14 @@ impl NonCamelCaseTypes { if !is_camel_case(name) { let msg = format!("{} `{}` should have an upper camel case name", sort, name); - cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, &msg) - .span_suggestion( + cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, |lint| { + lint.build(&msg).span_suggestion( ident.span, "convert the identifier to upper camel case", to_camel_case(name), Applicability::MaybeIncorrect, - ) - .emit(); + ).emit() + }) } } } @@ -226,22 +226,24 @@ impl NonSnakeCase { let sc = NonSnakeCase::to_snake_case(name); let msg = format!("{} `{}` should have a snake case name", sort, name); - let mut err = cx.struct_span_lint(NON_SNAKE_CASE, ident.span, &msg); + cx.struct_span_lint(NON_SNAKE_CASE, ident.span, |lint| { + let mut err = lint.build(&msg); + // We have a valid span in almost all cases, but we don't have one when linting a crate + // name provided via the command line. + if !ident.span.is_dummy() { + err.span_suggestion( + ident.span, + "convert the identifier to snake case", + sc, + Applicability::MaybeIncorrect, + ); + } else { + err.help(&format!("convert the identifier to snake case: `{}`", sc)); + } - // We have a valid span in almost all cases, but we don't have one when linting a crate - // name provided via the command line. - if !ident.span.is_dummy() { - err.span_suggestion( - ident.span, - "convert the identifier to snake case", - sc, - Applicability::MaybeIncorrect, - ); - } else { - err.help(&format!("convert the identifier to snake case: `{}`", sc)); - } + err.emit(); + }); - err.emit(); } } } @@ -390,8 +392,8 @@ impl NonUpperCaseGlobals { if name.chars().any(|c| c.is_lowercase()) { let uc = NonSnakeCase::to_snake_case(&name).to_uppercase(); - let msg = format!("{} `{}` should have an upper case name", sort, name); - cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, &msg) + cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, |lint| { + lint.build(&format!("{} `{}` should have an upper case name", sort, name)) .span_suggestion( ident.span, "convert the identifier to upper case", @@ -399,6 +401,7 @@ impl NonUpperCaseGlobals { Applicability::MaybeIncorrect, ) .emit(); + }) } } } diff --git a/src/librustc_lint/redundant_semicolon.rs b/src/librustc_lint/redundant_semicolon.rs index 21b244ad75d..68cb0b54f56 100644 --- a/src/librustc_lint/redundant_semicolon.rs +++ b/src/librustc_lint/redundant_semicolon.rs @@ -26,19 +26,21 @@ impl EarlyLintPass for RedundantSemicolon { } else { "unnecessary trailing semicolon" }; - let mut err = cx.struct_span_lint(REDUNDANT_SEMICOLON, stmt.span, &msg); - let suggest_msg = if multiple { - "remove these semicolons" - } else { - "remove this semicolon" - }; - err.span_suggestion( - stmt.span, - &suggest_msg, - String::new(), - Applicability::MaybeIncorrect, - ); - err.emit(); + cx.struct_span_lint(REDUNDANT_SEMICOLON, stmt.span, |lint| { + let mut err = lint.build(&msg); + let suggest_msg = if multiple { + "remove these semicolons" + } else { + "remove this semicolon" + }; + err.span_suggestion( + stmt.span, + &suggest_msg, + String::new(), + Applicability::MaybeIncorrect, + ); + err.emit(); + }); } } } diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 6bc6f58f3e7..30a903a2a30 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -67,6 +67,7 @@ fn lint_overflowing_range_endpoint<'a, 'tcx>( ) -> bool { // We only want to handle exclusive (`..`) ranges, // which are represented as `ExprKind::Struct`. + let mut overwritten = false; if let ExprKind::Struct(_, eps, _) = &parent_expr.kind { if eps.len() != 2 { return false; @@ -75,35 +76,36 @@ fn lint_overflowing_range_endpoint<'a, 'tcx>( // (`..=`) instead only if it is the `end` that is // overflowing and only by 1. if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max { - let mut err = cx.struct_span_lint( + cx.struct_span_lint( OVERFLOWING_LITERALS, parent_expr.span, - &format!("range endpoint is out of range for `{}`", ty), + |lint| { + let mut err = lint.build(&format!("range endpoint is out of range for `{}`", ty)); + if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) { + use ast::{LitIntType, LitKind}; + // We need to preserve the literal's suffix, + // as it may determine typing information. + let suffix = match lit.node { + LitKind::Int(_, LitIntType::Signed(s)) => format!("{}", s.name_str()), + LitKind::Int(_, LitIntType::Unsigned(s)) => format!("{}", s.name_str()), + LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(), + _ => bug!(), + }; + let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); + err.span_suggestion( + parent_expr.span, + &"use an inclusive range instead", + suggestion, + Applicability::MachineApplicable, + ); + err.emit(); + overwritten = true; + } + }, ); - if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) { - use ast::{LitIntType, LitKind}; - // We need to preserve the literal's suffix, - // as it may determine typing information. - let suffix = match lit.node { - LitKind::Int(_, LitIntType::Signed(s)) => format!("{}", s.name_str()), - LitKind::Int(_, LitIntType::Unsigned(s)) => format!("{}", s.name_str()), - LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(), - _ => bug!(), - }; - let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); - err.span_suggestion( - parent_expr.span, - &"use an inclusive range instead", - suggestion, - Applicability::MachineApplicable, - ); - err.emit(); - return true; - } } } - - false + overwritten } // For `isize` & `usize`, be conservative with the warnings, so that the @@ -163,31 +165,32 @@ fn report_bin_hex_error( (t.name_str(), actually.to_string()) } }; - let mut err = cx.struct_span_lint( + cx.struct_span_lint( OVERFLOWING_LITERALS, expr.span, - &format!("literal out of range for {}", t), + |lint| { + let mut err = lint.build(&format!("literal out of range for {}", t)); + err.note(&format!( + "the literal `{}` (decimal `{}`) does not fit into \ + an `{}` and will become `{}{}`", + repr_str, val, t, actually, t + )); + if let Some(sugg_ty) = get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative) { + if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { + let (sans_suffix, _) = repr_str.split_at(pos); + err.span_suggestion( + expr.span, + &format!("consider using `{}` instead", sugg_ty), + format!("{}{}", sans_suffix, sugg_ty), + Applicability::MachineApplicable, + ); + } else { + err.help(&format!("consider using `{}` instead", sugg_ty)); + } + } + err.emit(); + }, ); - err.note(&format!( - "the literal `{}` (decimal `{}`) does not fit into \ - an `{}` and will become `{}{}`", - repr_str, val, t, actually, t - )); - if let Some(sugg_ty) = get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative) { - if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { - let (sans_suffix, _) = repr_str.split_at(pos); - err.span_suggestion( - expr.span, - &format!("consider using `{}` instead", sugg_ty), - format!("{}{}", sans_suffix, sugg_ty), - Applicability::MachineApplicable, - ); - } else { - err.help(&format!("consider using `{}` instead", sugg_ty)); - } - } - - err.emit(); } // This function finds the next fitting type and generates a suggestion string. @@ -298,18 +301,16 @@ fn lint_uint_literal<'a, 'tcx>( match par_e.kind { hir::ExprKind::Cast(..) => { if let ty::Char = cx.tables.expr_ty(par_e).kind { - let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - par_e.span, - "only `u8` can be cast into `char`", - ); - err.span_suggestion( - par_e.span, - &"use a `char` literal instead", - format!("'\\u{{{:X}}}'", lit_val), - Applicability::MachineApplicable, - ); - err.emit(); + cx.struct_span_lint(OVERFLOWING_LITERALS, par_e.span, |lint| { + lint.build("only `u8` can be cast into `char`") + .span_suggestion( + par_e.span, + &"use a `char` literal instead", + format!("'\\u{{{:X}}}'", lit_val), + Applicability::MachineApplicable, + ) + .emit(); + }); return; } } @@ -883,22 +884,21 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { note: &str, help: Option<&str>, ) { - let mut diag = self.cx.struct_span_lint( - IMPROPER_CTYPES, - sp, - &format!("`extern` block uses type `{}`, which is not FFI-safe", ty), - ); - diag.span_label(sp, "not FFI-safe"); - if let Some(help) = help { - diag.help(help); - } - diag.note(note); - if let ty::Adt(def, _) = ty.kind { - if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) { - diag.span_note(sp, "the type is defined here"); + self.cx.struct_span_lint(IMPROPER_CTYPES, sp, |lint| { + let mut diag = + lint.build(&format!("`extern` block uses type `{}`, which is not FFI-safe", ty)); + diag.span_label(sp, "not FFI-safe"); + if let Some(help) = help { + diag.help(help); } - } - diag.emit(); + diag.note(note); + if let ty::Adt(def, _) = ty.kind { + if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) { + diag.span_note(sp, "the type is defined here"); + } + } + diag.emit(); + }); } fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 5490e5e2b4d..2f4a9572a83 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -213,18 +213,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { ) -> bool { for attr in cx.tcx.get_attrs(def_id).iter() { if attr.check_name(sym::must_use) { - let msg = format!( - "unused {}`{}`{} that must be used", - descr_pre_path, - cx.tcx.def_path_str(def_id), - descr_post_path - ); - let mut err = cx.struct_span_lint(UNUSED_MUST_USE, span, &msg); - // check for #[must_use = "..."] - if let Some(note) = attr.value_str() { - err.note(¬e.as_str()); - } - err.emit(); + cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { + let msg = format!( + "unused {}`{}`{} that must be used", + descr_pre_path, + cx.tcx.def_path_str(def_id), + descr_post_path + ); + let mut err = lint.build(&msg); + // check for #[must_use = "..."] + if let Some(note) = attr.value_str() { + err.note(¬e.as_str()); + } + err.emit(); + }); return true; } } @@ -406,52 +408,54 @@ impl UnusedParens { msg: &str, keep_space: (bool, bool), ) { - let span_msg = format!("unnecessary parentheses around {}", msg); - let mut err = cx.struct_span_lint(UNUSED_PARENS, span, &span_msg); - let mut ate_left_paren = false; - let mut ate_right_paren = false; - let parens_removed = pattern.trim_matches(|c| match c { - '(' => { - if ate_left_paren { - false - } else { - ate_left_paren = true; - true + cx.struct_span_lint(UNUSED_PARENS, span, |lint| { + let span_msg = format!("unnecessary parentheses around {}", msg); + let mut err = lint.build(&span_msg); + let mut ate_left_paren = false; + let mut ate_right_paren = false; + let parens_removed = pattern.trim_matches(|c| match c { + '(' => { + if ate_left_paren { + false + } else { + ate_left_paren = true; + true + } } - } - ')' => { - if ate_right_paren { - false - } else { - ate_right_paren = true; - true + ')' => { + if ate_right_paren { + false + } else { + ate_right_paren = true; + true + } } - } - _ => false, - }); + _ => false, + }); - let replace = { - let mut replace = if keep_space.0 { - let mut s = String::from(" "); - s.push_str(parens_removed); - s - } else { - String::from(parens_removed) - }; + let replace = { + let mut replace = if keep_space.0 { + let mut s = String::from(" "); + s.push_str(parens_removed); + s + } else { + String::from(parens_removed) + }; - if keep_space.1 { - replace.push(' '); - } - replace - }; + if keep_space.1 { + replace.push(' '); + } + replace + }; - err.span_suggestion_short( - span, - "remove these parentheses", - replace, - Applicability::MachineApplicable, - ); - err.emit(); + err.span_suggestion_short( + span, + "remove these parentheses", + replace, + Applicability::MachineApplicable, + ); + err.emit(); + }); } } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index e528159fcef..25f1a495ad3 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -319,18 +319,20 @@ fn do_mir_borrowck<'a, 'tcx>( }; // Span and message don't matter; we overwrite them below anyway - let mut diag = mbcx.infcx.tcx.struct_span_lint_hir( + mbcx.infcx.tcx.struct_span_lint_hir( MUTABLE_BORROW_RESERVATION_CONFLICT, lint_root, DUMMY_SP, - "", - ); + |lint| { + let mut diag = lint.build(""); - diag.message = initial_diag.styled_message().clone(); - diag.span = initial_diag.span.clone(); + diag.message = initial_diag.styled_message().clone(); + diag.span = initial_diag.span.clone(); + diag.buffer(&mut mbcx.errors_buffer); + }, + ); initial_diag.cancel(); - diag.buffer(&mut mbcx.errors_buffer); } // For each non-user used mutable variable, check if it's been assigned from @@ -381,15 +383,17 @@ fn do_mir_borrowck<'a, 'tcx>( UNUSED_MUT, lint_root, span, - "variable does not need to be mutable", - ) - .span_suggestion_short( - mut_span, - "remove this `mut`", - String::new(), - Applicability::MachineApplicable, + |lint| { + lint.build("variable does not need to be mutable") + .span_suggestion_short( + mut_span, + "remove this `mut`", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + }, ) - .emit(); } // Buffer any move errors that we collected and de-duplicated. diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs index 428db8356b1..99d3b281945 100644 --- a/src/librustc_mir/const_eval/eval_queries.rs +++ b/src/librustc_mir/const_eval/eval_queries.rs @@ -209,10 +209,11 @@ fn validate_and_turn_into_const<'tcx>( val.map_err(|error| { let err = error_to_const_error(&ecx, error); - match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") { - Ok(mut diag) => { - diag.note(note_on_undefined_behavior_error()); - diag.emit(); + match err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| { + diag.note(note_on_undefined_behavior_error()); + diag.emit(); + }) { + Ok(_) => { ErrorHandled::Reported } Err(err) => err, diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs index 0c65b77a382..9fea82d78c9 100644 --- a/src/librustc_mir/interpret/intern.rs +++ b/src/librustc_mir/interpret/intern.rs @@ -326,12 +326,15 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>( // to read enum discriminants in order to find references in enum variant fields. if let err_unsup!(ValidationFailure(_)) = error.kind { let err = crate::const_eval::error_to_const_error(&ecx, error); - match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") { - Ok(mut diag) => { + match err.struct_error( + ecx.tcx, + "it is undefined behavior to use this value", + |mut diag| { diag.note(crate::const_eval::note_on_undefined_behavior_error()); diag.emit(); - } - Err(ErrorHandled::TooGeneric) | Err(ErrorHandled::Reported) => {} + }, + ) { + Ok(()) | Err(ErrorHandled::TooGeneric) | Err(ErrorHandled::Reported) => {} } } } diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 8dc185cd82b..c019fa57fb7 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -527,7 +527,9 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: DefId) { does not derive Copy (error E0133)" .to_string() }; - tcx.lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), &message); + tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| { + lint.build(&message).emit() + }); } /// Returns the `HirId` for an enclosing scope that is also `unsafe`. @@ -559,15 +561,17 @@ fn is_enclosed( fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) { let span = tcx.sess.source_map().def_span(tcx.hir().span(id)); let msg = "unnecessary `unsafe` block"; - let mut db = tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, msg); - db.span_label(span, msg); - if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) { - db.span_label( - tcx.sess.source_map().def_span(tcx.hir().span(id)), - format!("because it's nested under this `unsafe` {}", kind), - ); - } - db.emit(); + tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| { + let mut db = lint.build(msg); + db.span_label(span, msg); + if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) { + db.span_label( + tcx.sess.source_map().def_span(tcx.hir().span(id)), + format!("because it's nested under this `unsafe` {}", kind), + ); + } + db.emit(); + }); } fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> { @@ -619,13 +623,17 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { SAFE_PACKED_BORROWS, lint_hir_id, source_info.span, - &format!( - "{} is unsafe and requires unsafe function or block (error E0133)", - description - ), + |lint| { + lint.build( + &format!( + "{} is unsafe and requires unsafe function or block (error E0133)", + description + ) + ) + .note(&details.as_str()) + .emit() + } ) - .note(&details.as_str()) - .emit(); } } } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index d645f6cf183..aeeca52ba3b 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -557,11 +557,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { if r_bits.map_or(false, |b| b >= left_bits as u128) { let lint_root = self.lint_root(source_info)?; let dir = if op == BinOp::Shr { "right" } else { "left" }; - self.tcx.lint_hir( + self.tcx.struct_span_lint_hir( ::rustc::lint::builtin::EXCEEDING_BITSHIFTS, lint_root, source_info.span, - &format!("attempt to shift {} with overflow", dir), + |lint| lint.build(&format!("attempt to shift {} with overflow", dir)).emit(), ); return None; } @@ -912,35 +912,42 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { .hir() .as_local_hir_id(self.source.def_id()) .expect("some part of a failing const eval must be local"); - let msg = match msg { - PanicInfo::Overflow(_) - | PanicInfo::OverflowNeg - | PanicInfo::DivisionByZero - | PanicInfo::RemainderByZero => msg.description().to_owned(), - PanicInfo::BoundsCheck { ref len, ref index } => { - let len = - self.eval_operand(len, source_info).expect("len must be const"); - let len = match self.ecx.read_scalar(len) { - Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data, - other => bug!("const len not primitive: {:?}", other), - }; - let index = self - .eval_operand(index, source_info) - .expect("index must be const"); - let index = match self.ecx.read_scalar(index) { - Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data, - other => bug!("const index not primitive: {:?}", other), + self.tcx.struct_span_lint_hir( + ::rustc::lint::builtin::CONST_ERR, + hir_id, + span, + |lint| { + let msg = match msg { + PanicInfo::Overflow(_) + | PanicInfo::OverflowNeg + | PanicInfo::DivisionByZero + | PanicInfo::RemainderByZero => msg.description().to_owned(), + PanicInfo::BoundsCheck { ref len, ref index } => { + let len = + self.eval_operand(len, source_info).expect("len must be const"); + let len = match self.ecx.read_scalar(len) { + Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data, + other => bug!("const len not primitive: {:?}", other), + }; + let index = self + .eval_operand(index, source_info) + .expect("index must be const"); + let index = match self.ecx.read_scalar(index) { + Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data, + other => bug!("const index not primitive: {:?}", other), + }; + format!( + "index out of bounds: \ + the len is {} but the index is {}", + len, index, + ) + } + // Need proper const propagator for these + _ => return, }; - format!( - "index out of bounds: \ - the len is {} but the index is {}", - len, index, - ) - } - // Need proper const propagator for these - _ => return, - }; - self.tcx.lint_hir(::rustc::lint::builtin::CONST_ERR, hir_id, span, &msg); + lint.build(&msg).emit() + }, + ); } else { if self.should_const_prop(value) { if let ScalarMaybeUndef::Scalar(scalar) = value_const { diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs index 4f0e5bb4582..7ed6c81eb63 100644 --- a/src/librustc_mir_build/hair/pattern/_match.rs +++ b/src/librustc_mir_build/hair/pattern/_match.rs @@ -2235,24 +2235,26 @@ fn lint_overlapping_patterns<'tcx>( overlaps: Vec<IntRange<'tcx>>, ) { if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) { - let mut err = tcx.struct_span_lint_hir( + tcx.struct_span_lint_hir( lint::builtin::OVERLAPPING_PATTERNS, hir_id, ctor_range.span, - "multiple patterns covering the same range", + |lint| { + let mut err = lint.build("multiple patterns covering the same range"); + err.span_label(ctor_range.span, "overlapping patterns"); + for int_range in overlaps { + // Use the real type for user display of the ranges: + err.span_label( + int_range.span, + &format!( + "this range overlaps on `{}`", + IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx), + ), + ); + } + err.emit(); + }, ); - err.span_label(ctor_range.span, "overlapping patterns"); - for int_range in overlaps { - // Use the real type for user display of the ranges: - err.span_label( - int_range.span, - &format!( - "this range overlaps on `{}`", - IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx), - ), - ); - } - err.emit(); } } diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/src/librustc_mir_build/hair/pattern/check_match.rs index b77bd4ecb8e..421f7958e5f 100644 --- a/src/librustc_mir_build/hair/pattern/check_match.rs +++ b/src/librustc_mir_build/hair/pattern/check_match.rs @@ -292,20 +292,25 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa BINDINGS_WITH_VARIANT_NAME, p.hir_id, p.span, - &format!( - "pattern binding `{}` is named the same as one \ - of the variants of the type `{}`", - ident, ty_path - ), + |lint| { + lint + .build( + &format!( + "pattern binding `{}` is named the same as one \ + of the variants of the type `{}`", + ident, ty_path + ) + ) + .code(error_code!(E0170)) + .span_suggestion( + p.span, + "to match on the variant, qualify the path", + format!("{}::{}", ty_path, ident), + Applicability::MachineApplicable, + ) + .emit(); + }, ) - .code(error_code!(E0170)) - .span_suggestion( - p.span, - "to match on the variant, qualify the path", - format!("{}::{}", ty_path, ident), - Applicability::MachineApplicable, - ) - .emit(); } } } @@ -325,13 +330,15 @@ fn pat_is_catchall(pat: &super::Pat<'_>) -> bool { } fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) { - let mut err = tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern"); - if let Some(catchall) = catchall { - // We had a catchall pattern, hint at that. - err.span_label(span, "unreachable pattern"); - err.span_label(catchall, "matches any value"); - } - err.emit(); + tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| { + let mut err = lint.build("unreachable pattern"); + if let Some(catchall) = catchall { + // We had a catchall pattern, hint at that. + err.span_label(span, "unreachable pattern"); + err.span_label(catchall, "matches any value"); + } + err.emit(); + }); } fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) { @@ -340,7 +347,7 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir:: hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern", _ => bug!(), }; - tcx.lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, msg); + tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| lint.build(msg).emit()); } /// Check for unreachable patterns. diff --git a/src/librustc_mir_build/hair/pattern/const_to_pat.rs b/src/librustc_mir_build/hair/pattern/const_to_pat.rs index a21a0ee8a1b..5fbe764430c 100644 --- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs +++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs @@ -109,11 +109,14 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } }; let path = self.tcx().def_path_str(adt_def.did); - let msg = format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - path, path, - ); + + let make_msg = || -> String { + format!( + "to use a constant of type `{}` in a pattern, \ + `{}` must be annotated with `#[derive(PartialEq, Eq)]`", + path, path, + ) + }; // double-check there even *is* a semantic `PartialEq` to dispatch to. // @@ -143,13 +146,13 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { if !ty_is_partial_eq { // span_fatal avoids ICE from resolution of non-existent method (rare case). - self.tcx().sess.span_fatal(self.span, &msg); + self.tcx().sess.span_fatal(self.span, &make_msg()); } else { - self.tcx().lint_hir( + self.tcx().struct_span_lint_hir( lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, self.span, - &msg, + |lint| lint.build(&make_msg()).emit(), ); } } @@ -177,11 +180,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { let kind = match cv.ty.kind { ty::Float(_) => { - tcx.lint_hir( + tcx.struct_span_lint_hir( ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, id, span, - "floating-point types cannot be used in patterns", + |lint| lint.build("floating-point types cannot be used in patterns").emit(), ); PatKind::Constant { value: cv } } diff --git a/src/librustc_mir_build/lints.rs b/src/librustc_mir_build/lints.rs index 4244e1b8d80..4704f8d034d 100644 --- a/src/librustc_mir_build/lints.rs +++ b/src/librustc_mir_build/lints.rs @@ -124,18 +124,20 @@ fn check_fn_for_unconditional_recursion<'tcx>( if !reached_exit_without_self_call && !self_call_locations.is_empty() { let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); let sp = tcx.sess.source_map().def_span(tcx.hir().span(hir_id)); - let mut db = tcx.struct_span_lint_hir( + tcx.struct_span_lint_hir( UNCONDITIONAL_RECURSION, hir_id, sp, - "function cannot return without recursing", + |lint| { + let mut db = lint.build("function cannot return without recursing"); + db.span_label(sp, "cannot return without recursing"); + // offer some help to the programmer. + for location in &self_call_locations { + db.span_label(location.span, "recursive call site"); + } + db.help("a `loop` may express intention better if this is on purpose"); + db.emit(); + }, ); - db.span_label(sp, "cannot return without recursing"); - // offer some help to the programmer. - for location in &self_call_locations { - db.span_label(location.span, "recursive call site"); - } - db.help("a `loop` may express intention better if this is on purpose"); - db.emit(); } } diff --git a/src/librustc_parse/config.rs b/src/librustc_parse/config.rs index 0edd56680f9..cd935a37f7d 100644 --- a/src/librustc_parse/config.rs +++ b/src/librustc_parse/config.rs @@ -315,10 +315,11 @@ impl<'a> StripUnconfigured<'a> { validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg); match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { Ok(r) => return Some(r), - Err(mut e) => e - .help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) + Err(mut e) => { + e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) .note(CFG_ATTR_NOTE_REF) - .emit(), + .emit(); + }, } } _ => self.error_malformed_cfg_attr_missing(attr.span), diff --git a/src/librustc_parse/lexer/unescape_error_reporting.rs b/src/librustc_parse/lexer/unescape_error_reporting.rs index 88762dabd8a..d41775a143a 100644 --- a/src/librustc_parse/lexer/unescape_error_reporting.rs +++ b/src/librustc_parse/lexer/unescape_error_reporting.rs @@ -69,7 +69,7 @@ pub(crate) fn emit_unescape_error( format!("\"{}\"", lit), Applicability::MachineApplicable, ) - .emit() + .emit(); } EscapeError::EscapeOnlyChar => { let (c, _span) = last_char(); diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs index e58eb9ffc51..905f6bef58e 100644 --- a/src/librustc_parse/parser/attr.rs +++ b/src/librustc_parse/parser/attr.rs @@ -149,7 +149,7 @@ impl<'a> Parser<'a> { source files. Outer attributes, like `#[test]`, annotate the \ item following them.", ) - .emit() + .emit(); } } @@ -239,7 +239,7 @@ impl<'a> Parser<'a> { (`1u8`, `1.0f32`, etc.), use an unsuffixed version \ (`1`, `1.0`, etc.)", ) - .emit() + .emit(); } Ok(lit) diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index 237b3cc13d3..018aef3c13c 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -1014,7 +1014,7 @@ impl<'a> Parser<'a> { String::new(), Applicability::MachineApplicable, ) - .emit() + .emit(); } } diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs index 7131eb1144e..ba97f3feb80 100644 --- a/src/librustc_parse/parser/mod.rs +++ b/src/librustc_parse/parser/mod.rs @@ -1407,6 +1407,6 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, sess: &Pa *sess.reached_eof.borrow_mut() |= unclosed_delims.iter().any(|unmatched_delim| unmatched_delim.found_delim.is_none()); for unmatched in unclosed_delims.drain(..) { - make_unclosed_delims_error(unmatched, sess).map(|mut e| e.emit()); + make_unclosed_delims_error(unmatched, sess).map(|mut e| { e.emit(); }); } } diff --git a/src/librustc_parse/parser/pat.rs b/src/librustc_parse/parser/pat.rs index 985185230f2..ec6d4db6102 100644 --- a/src/librustc_parse/parser/pat.rs +++ b/src/librustc_parse/parser/pat.rs @@ -572,7 +572,7 @@ impl<'a> Parser<'a> { self.struct_span_err(span, problem) .span_suggestion(span, suggestion, fix, Applicability::MachineApplicable) .note("`mut` may be followed by `variable` and `variable @ pattern`") - .emit() + .emit(); } /// Eat any extraneous `mut`s and error + recover if we ate any. diff --git a/src/librustc_parse/validate_attr.rs b/src/librustc_parse/validate_attr.rs index 84562fbb46f..f6d5da68be3 100644 --- a/src/librustc_parse/validate_attr.rs +++ b/src/librustc_parse/validate_attr.rs @@ -27,7 +27,7 @@ pub fn check_meta(sess: &ParseSess, attr: &Attribute) { _ => { if let MacArgs::Eq(..) = attr.get_normal_item().args { // All key-value attributes are restricted to meta-item syntax. - parse_meta(sess, attr).map_err(|mut err| err.emit()).ok(); + parse_meta(sess, attr).map_err(|mut err| { err.emit(); }).ok(); } } } @@ -152,6 +152,6 @@ pub fn check_builtin_attribute( } } } - Err(mut err) => err.emit(), + Err(mut err) => { err.emit(); }, } } diff --git a/src/librustc_passes/check_attr.rs b/src/librustc_passes/check_attr.rs index a877c1de175..6bae61d787b 100644 --- a/src/librustc_passes/check_attr.rs +++ b/src/librustc_passes/check_attr.rs @@ -97,9 +97,8 @@ impl CheckAttrVisitor<'tcx> { UNUSED_ATTRIBUTES, hir_id, attr.span, - "`#[inline]` is ignored on function prototypes", - ) - .emit(); + |lint| lint.build("`#[inline]` is ignored on function prototypes").emit(), + ); true } // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with @@ -112,18 +111,19 @@ impl CheckAttrVisitor<'tcx> { UNUSED_ATTRIBUTES, hir_id, attr.span, - "`#[inline]` is ignored on constants", - ) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \ - for more information", - ) - .emit(); + |lint| { + lint.build("`#[inline]` is ignored on constants") + .warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ) + .note( + "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \ + for more information", + ) + .emit(); + }); true } _ => { @@ -336,10 +336,12 @@ impl CheckAttrVisitor<'tcx> { CONFLICTING_REPR_HINTS, hir_id, hint_spans.collect::<Vec<Span>>(), - "conflicting representation hints", - ) - .code(rustc_errors::error_code!(E0566)) - .emit(); + |lint| { + lint.build("conflicting representation hints") + .code(rustc_errors::error_code!(E0566)) + .emit(); + } + ); } } diff --git a/src/librustc_passes/dead.rs b/src/librustc_passes/dead.rs index 9367909d10c..25b8b8fcd44 100644 --- a/src/librustc_passes/dead.rs +++ b/src/librustc_passes/dead.rs @@ -554,12 +554,9 @@ impl DeadVisitor<'tcx> { participle: &str, ) { if !name.as_str().starts_with("_") { - self.tcx.lint_hir( - lint::builtin::DEAD_CODE, - id, - span, - &format!("{} is never {}: `{}`", node_type, participle, name), - ); + self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| { + lint.build(&format!("{} is never {}: `{}`", node_type, participle, name)).emit() + }); } } } diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs index b355a47c394..27ad953e4fa 100644 --- a/src/librustc_passes/liveness.rs +++ b/src/librustc_passes/liveness.rs @@ -1527,39 +1527,41 @@ impl<'tcx> Liveness<'_, 'tcx> { lint::builtin::UNUSED_VARIABLES, hir_id, spans, - &format!("variable `{}` is assigned to, but never used", name), + |lint| { + lint.build(&format!("variable `{}` is assigned to, but never used", name)) + .note(&format!("consider using `_{}` instead", name)) + .emit(); + }, ) - .note(&format!("consider using `_{}` instead", name)) - .emit(); } else { - let mut err = self.ir.tcx.struct_span_lint_hir( + self.ir.tcx.struct_span_lint_hir( lint::builtin::UNUSED_VARIABLES, hir_id, spans.clone(), - &format!("unused variable: `{}`", name), + |lint| { + let mut err = lint.build(&format!("unused variable: `{}`", name)); + if self.ir.variable_is_shorthand(var) { + if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) { + // Handle `ref` and `ref mut`. + let spans = + spans.iter().map(|_span| (pat.span, format!("{}: _", name))).collect(); + + err.multipart_suggestion( + "try ignoring the field", + spans, + Applicability::MachineApplicable, + ); + } + } else { + err.multipart_suggestion( + "consider prefixing with an underscore", + spans.iter().map(|span| (*span, format!("_{}", name))).collect(), + Applicability::MachineApplicable, + ); + } + err.emit() + }, ); - - if self.ir.variable_is_shorthand(var) { - if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) { - // Handle `ref` and `ref mut`. - let spans = - spans.iter().map(|_span| (pat.span, format!("{}: _", name))).collect(); - - err.multipart_suggestion( - "try ignoring the field", - spans, - Applicability::MachineApplicable, - ); - } - } else { - err.multipart_suggestion( - "consider prefixing with an underscore", - spans.iter().map(|span| (*span, format!("_{}", name))).collect(), - Applicability::MachineApplicable, - ); - } - - err.emit() } } } @@ -1579,10 +1581,12 @@ impl<'tcx> Liveness<'_, 'tcx> { lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans, - &format!("value passed to `{}` is never read", name), + |lint| { + lint.build(&format!("value passed to `{}` is never read", name)) + .help("maybe it is overwritten before being read?") + .emit(); + }, ) - .help("maybe it is overwritten before being read?") - .emit(); } else { self.ir .tcx @@ -1590,10 +1594,12 @@ impl<'tcx> Liveness<'_, 'tcx> { lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans, - &format!("value assigned to `{}` is never read", name), + |lint| { + lint.build(&format!("value assigned to `{}` is never read", name)) + .help("maybe it is overwritten before being read?") + .emit(); + }, ) - .help("maybe it is overwritten before being read?") - .emit(); } } } diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs index 12debfb66a4..374b7d1cf7a 100644 --- a/src/librustc_passes/stability.rs +++ b/src/librustc_passes/stability.rs @@ -604,15 +604,13 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { } fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) { - tcx.lint_hir( - lint::builtin::STABLE_FEATURES, - hir::CRATE_HIR_ID, - span, - &format!( + tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| { + lint.build(&format!( "the feature `{}` has been stable since {} and no longer requires \ - an attribute to enable", - feature, since - ), + an attribute to enable", + feature, since + )).emit(); + } ); } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 74bb72d6fad..43f92ae69c4 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -1781,17 +1781,21 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { if self.leaks_private_dep(def_id) { - self.tcx.lint_hir( + self.tcx.struct_span_lint_hir( lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES, self.item_id, self.span, - &format!( - "{} `{}` from private dependency '{}' in public \ - interface", - kind, - descr, - self.tcx.crate_name(def_id.krate) - ), + |lint| { + lint.build( + &format!( + "{} `{}` from private dependency '{}' in public \ + interface", + kind, + descr, + self.tcx.crate_name(def_id.krate) + ) + ).emit() + }, ); } @@ -1814,12 +1818,9 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { err.emit(); } else { let err_code = if kind == "trait" { "E0445" } else { "E0446" }; - self.tcx.lint_hir( - lint::builtin::PRIVATE_IN_PUBLIC, - hir_id, - self.span, - &format!("{} (error {})", msg, err_code), - ); + self.tcx.struct_span_lint_hir(lint::builtin::PRIVATE_IN_PUBLIC, hir_id, self.span, |lint| { + lint.build(&format!("{} (error {})", msg, err_code)).emit() + }); } } diff --git a/src/librustc_resolve/lifetimes.rs b/src/librustc_resolve/lifetimes.rs index 87522d28d1e..cef5e025741 100644 --- a/src/librustc_resolve/lifetimes.rs +++ b/src/librustc_resolve/lifetimes.rs @@ -1575,22 +1575,23 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } - let mut err = self.tcx.struct_span_lint_hir( + self.tcx.struct_span_lint_hir( lint::builtin::SINGLE_USE_LIFETIMES, id, span, - &format!("lifetime parameter `{}` only used once", name), + |lint| { + let mut err = lint.build(&format!("lifetime parameter `{}` only used once", name)); + if span == lifetime.span { + // spans are the same for in-band lifetime declarations + err.span_label(span, "this lifetime is only used here"); + } else { + err.span_label(span, "this lifetime..."); + err.span_label(lifetime.span, "...is used only here"); + } + self.suggest_eliding_single_use_lifetime(&mut err, def_id, lifetime); + err.emit(); + }, ); - - if span == lifetime.span { - // spans are the same for in-band lifetime declarations - err.span_label(span, "this lifetime is only used here"); - } else { - err.span_label(span, "this lifetime..."); - err.span_label(lifetime.span, "...is used only here"); - } - self.suggest_eliding_single_use_lifetime(&mut err, def_id, lifetime); - err.emit(); } } Some(LifetimeUseSet::Many) => { @@ -1610,26 +1611,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { _ => None, } { debug!("id ={:?} span = {:?} name = {:?}", id, span, name); - let mut err = self.tcx.struct_span_lint_hir( + self.tcx.struct_span_lint_hir( lint::builtin::UNUSED_LIFETIMES, id, span, - &format!("lifetime parameter `{}` never used", name), - ); - if let Some(parent_def_id) = self.tcx.parent(def_id) { - if let Some(generics) = self.tcx.hir().get_generics(parent_def_id) { - let unused_lt_span = self.lifetime_deletion_span(name, generics); - if let Some(span) = unused_lt_span { - err.span_suggestion( - span, - "elide the unused lifetime", - String::new(), - Applicability::MachineApplicable, - ); + |lint| { + let mut err = lint.build(&format!("lifetime parameter `{}` never used", name)); + if let Some(parent_def_id) = self.tcx.parent(def_id) { + if let Some(generics) = self.tcx.hir().get_generics(parent_def_id) { + let unused_lt_span = self.lifetime_deletion_span(name, generics); + if let Some(span) = unused_lt_span { + err.span_suggestion( + span, + "elide the unused lifetime", + String::new(), + Applicability::MachineApplicable, + ); + } + } } - } - } - err.emit(); + err.emit(); + }, + ); } } } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 2909d0f8c54..c26b47c3130 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -331,11 +331,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } else { let mut multispan = MultiSpan::from_span(span); multispan.push_span_label(span_late, note.to_string()); - tcx.lint_hir( + tcx.struct_span_lint_hir( lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, args.args[0].id(), multispan, - msg, + |lint| lint.build(msg).emit(), ); reported_late_bound_region_err = Some(false); } @@ -2216,34 +2216,31 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.check_stability(item.def_id, Some(hir_ref_id), span); if let Some(variant_def_id) = variant_resolution { - let mut err = tcx.struct_span_lint_hir( - AMBIGUOUS_ASSOCIATED_ITEMS, - hir_ref_id, - span, - "ambiguous associated item", - ); + tcx.struct_span_lint_hir(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| { + let mut err = lint.build("ambiguous associated item"); + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + assoc_ident, + also, + kind.descr(def_id) + ); + err.span_note(tcx.def_span(def_id), ¬e_msg); + }; - let mut could_refer_to = |kind: DefKind, def_id, also| { - let note_msg = format!( - "`{}` could{} refer to the {} defined here", - assoc_ident, - also, - kind.descr(def_id) + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(kind, item.def_id, " also"); + + err.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), + Applicability::MachineApplicable, ); - err.span_note(tcx.def_span(def_id), ¬e_msg); - }; - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(kind, item.def_id, " also"); - err.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), - Applicability::MachineApplicable, - ) - .emit(); + err.emit(); + }); } - Ok((ty, kind, item.def_id)) } diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index d254a84df72..02da2766ec6 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -468,23 +468,27 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else { ("", lint::builtin::TRIVIAL_CASTS) }; - let mut err = fcx.tcx.struct_span_lint_hir( + fcx.tcx.struct_span_lint_hir( lint, self.expr.hir_id, self.span, - &format!( - "trivial {}cast: `{}` as `{}`", - adjective, - fcx.ty_to_string(t_expr), - fcx.ty_to_string(t_cast) - ), + |err| { + err.build( + &format!( + "trivial {}cast: `{}` as `{}`", + adjective, + fcx.ty_to_string(t_expr), + fcx.ty_to_string(t_cast) + ) + ) + .help(&format!( + "cast can be replaced by coercion; this might \ + require {}a temporary variable", + type_asc_or + )) + .emit(); + }, ); - err.help(&format!( - "cast can be replaced by coercion; this might \ - require {}a temporary variable", - type_asc_or - )); - err.emit(); } pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) { diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 497a401a031..599f7dfe2d4 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -382,11 +382,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .emit(); } else { - self.tcx.lint_hir( + self.tcx.struct_span_lint_hir( lint::builtin::TYVAR_BEHIND_RAW_POINTER, scope_expr_id, span, - "type annotations needed", + |lint| lint.build("type annotations needed").emit(), ); } } else { @@ -1280,33 +1280,34 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { stable_pick: &Pick<'_>, unstable_candidates: &[(&Candidate<'tcx>, Symbol)], ) { - let mut diag = self.tcx.struct_span_lint_hir( + self.tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, self.fcx.body_id, self.span, - "a method with this name may be added to the standard library in the future", - ); - - // FIXME: This should be a `span_suggestion` instead of `help` - // However `self.span` only - // highlights the method name, so we can't use it. Also consider reusing the code from - // `report_method_error()`. - diag.help(&format!( - "call with fully qualified syntax `{}(...)` to keep using the current method", - self.tcx.def_path_str(stable_pick.item.def_id), - )); - - if nightly_options::is_nightly_build() { - for (candidate, feature) in unstable_candidates { + |lint| { + let mut diag = lint.build("a method with this name may be added to the standard library in the future"); + // FIXME: This should be a `span_suggestion` instead of `help` + // However `self.span` only + // highlights the method name, so we can't use it. Also consider reusing the code from + // `report_method_error()`. diag.help(&format!( - "add `#![feature({})]` to the crate attributes to enable `{}`", - feature, - self.tcx.def_path_str(candidate.item.def_id), + "call with fully qualified syntax `{}(...)` to keep using the current method", + self.tcx.def_path_str(stable_pick.item.def_id), )); - } - } - diag.emit(); + if nightly_options::is_nightly_build() { + for (candidate, feature) in unstable_candidates { + diag.help(&format!( + "add `#![feature({})]` to the crate attributes to enable `{}`", + feature, + self.tcx.def_path_str(candidate.item.def_id), + )); + } + } + + diag.emit(); + }, + ); } fn select_trait_candidate( diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index b82a83171d1..2fff5106dd1 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2895,15 +2895,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - let msg = format!("unreachable {}", kind); self.tcx() - .struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, &msg) - .span_label(span, &msg) - .span_label( - orig_span, - custom_note.unwrap_or("any code following this expression is unreachable"), - ) - .emit(); + .struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { + let msg = format!("unreachable {}", kind); + lint.build(&msg) + .span_label(span, &msg) + .span_label( + orig_span, + custom_note.unwrap_or("any code following this expression is unreachable"), + ) + .emit(); + }) } } } diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs index ec098c1d896..71ce5ce0f02 100644 --- a/src/librustc_typeck/check_unused.rs +++ b/src/librustc_typeck/check_unused.rs @@ -55,12 +55,14 @@ impl CheckVisitor<'tcx> { return; } - let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - format!("unused import: `{}`", snippet) - } else { - "unused import".to_owned() - }; - self.tcx.lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, &msg); + self.tcx.struct_span_lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, |lint| { + let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + format!("unused import: `{}`", snippet) + } else { + "unused import".to_owned() + }; + lint.build(&msg).emit(); + }); } } @@ -130,14 +132,16 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { .map(|attr| attr.span) .fold(span, |acc, attr_span| acc.to(attr_span)); - tcx.struct_span_lint_hir(lint, id, span, msg) - .span_suggestion_short( - span_with_attrs, - "remove it", - String::new(), - Applicability::MachineApplicable, - ) - .emit(); + tcx.struct_span_lint_hir(lint, id, span, |lint| { + lint.build(msg) + .span_suggestion_short( + span_with_attrs, + "remove it", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + }); continue; } } @@ -170,21 +174,25 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { } // Otherwise, we can convert it into a `use` of some kind. - let msg = "`extern crate` is not idiomatic in the new edition"; - let help = format!("convert it to a `{}`", visibility_qualified(&item.vis, "use")); let base_replacement = match extern_crate.orig_name { Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), None => format!("use {};", item.ident.name), }; let replacement = visibility_qualified(&item.vis, base_replacement); - tcx.struct_span_lint_hir(lint, id, extern_crate.span, msg) - .span_suggestion_short( - extern_crate.span, - &help, - replacement, - Applicability::MachineApplicable, - ) - .emit(); + tcx.struct_span_lint_hir(lint, id, extern_crate.span, |lint| { + + let msg = "`extern crate` is not idiomatic in the new edition"; + let help = format!("convert it to a `{}`", visibility_qualified(&item.vis, "use")); + + lint.build(msg) + .span_suggestion_short( + extern_crate.span, + &help, + replacement, + Applicability::MachineApplicable, + ) + .emit(); + }) } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1a3016cd9d3..d26a4cdc5ca 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1151,14 +1151,16 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { GenericParamKind::Type { ref default, synthetic, .. } => { if !allow_defaults && default.is_some() { if !tcx.features().default_type_parameter_fallback { - tcx.lint_hir( + tcx.struct_span_lint_hir( lint::builtin::INVALID_TYPE_PARAM_DEFAULT, param.hir_id, param.span, - &format!( + |lint| { + lint.build(&format!( "defaults for type parameters are only allowed in \ `struct`, `enum`, `type`, or `trait` definitions." - ), + )).emit(); + }, ); } } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 9ecf6d53129..dfb75c5a98e 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -669,39 +669,41 @@ fn build_diagnostic( let attrs = &item.attrs; let sp = span_of_attrs(attrs).unwrap_or(item.source.span()); - let mut diag = cx.tcx.struct_span_lint_hir( + cx.tcx.struct_span_lint_hir( lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE, hir_id, sp, - &format!("`[{}]` {}", path_str, err_msg), + |lint| { + let mut diag = lint.build(&format!("`[{}]` {}", path_str, err_msg)); + if let Some(link_range) = link_range { + if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) { + diag.set_span(sp); + diag.span_label(sp, short_err_msg); + } else { + // blah blah blah\nblah\nblah [blah] blah blah\nblah blah + // ^ ~~~~ + // | link_range + // last_new_line_offset + let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); + let line = dox[last_new_line_offset..].lines().next().unwrap_or(""); + + // Print the line containing the `link_range` and manually mark it with '^'s. + diag.note(&format!( + "the link appears in this line:\n\n{line}\n\ + {indicator: <before$}{indicator:^<found$}", + line = line, + indicator = "", + before = link_range.start - last_new_line_offset, + found = link_range.len(), + )); + } + }; + if let Some(help_msg) = help_msg { + diag.help(help_msg); + } + diag.emit(); + }, ); - if let Some(link_range) = link_range { - if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) { - diag.set_span(sp); - diag.span_label(sp, short_err_msg); - } else { - // blah blah blah\nblah\nblah [blah] blah blah\nblah blah - // ^ ~~~~ - // | link_range - // last_new_line_offset - let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); - let line = dox[last_new_line_offset..].lines().next().unwrap_or(""); - - // Print the line containing the `link_range` and manually mark it with '^'s. - diag.note(&format!( - "the link appears in this line:\n\n{line}\n\ - {indicator: <before$}{indicator:^<found$}", - line = line, - indicator = "", - before = link_range.start - last_new_line_offset, - found = link_range.len(), - )); - } - }; - if let Some(help_msg) = help_msg { - diag.help(help_msg); - } - diag.emit(); } /// Reports a resolution failure diagnostic. @@ -766,105 +768,107 @@ fn ambiguity_error( let attrs = &item.attrs; let sp = span_of_attrs(attrs).unwrap_or(item.source.span()); - let mut msg = format!("`{}` is ", path_str); - - let candidates = [TypeNS, ValueNS, MacroNS] - .iter() - .filter_map(|&ns| candidates[ns].map(|res| (res, ns))) - .collect::<Vec<_>>(); - match candidates.as_slice() { - [(first_def, _), (second_def, _)] => { - msg += &format!( - "both {} {} and {} {}", - first_def.article(), - first_def.descr(), - second_def.article(), - second_def.descr(), - ); - } - _ => { - let mut candidates = candidates.iter().peekable(); - while let Some((res, _)) = candidates.next() { - if candidates.peek().is_some() { - msg += &format!("{} {}, ", res.article(), res.descr()); - } else { - msg += &format!("and {} {}", res.article(), res.descr()); - } - } - } - } - - let mut diag = cx.tcx.struct_span_lint_hir( + cx.tcx.struct_span_lint_hir( lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE, hir_id, sp, - &msg, - ); + |lint| { + let mut msg = format!("`{}` is ", path_str); - if let Some(link_range) = link_range { - if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) { - diag.set_span(sp); - diag.span_label(sp, "ambiguous link"); - - for (res, ns) in candidates { - let (action, mut suggestion) = match res { - Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => { - ("add parentheses", format!("{}()", path_str)) - } - Res::Def(DefKind::Macro(..), _) => { - ("add an exclamation mark", format!("{}!", path_str)) + let candidates = [TypeNS, ValueNS, MacroNS] + .iter() + .filter_map(|&ns| candidates[ns].map(|res| (res, ns))) + .collect::<Vec<_>>(); + match candidates.as_slice() { + [(first_def, _), (second_def, _)] => { + msg += &format!( + "both {} {} and {} {}", + first_def.article(), + first_def.descr(), + second_def.article(), + second_def.descr(), + ); + } + _ => { + let mut candidates = candidates.iter().peekable(); + while let Some((res, _)) = candidates.next() { + if candidates.peek().is_some() { + msg += &format!("{} {}, ", res.article(), res.descr()); + } else { + msg += &format!("and {} {}", res.article(), res.descr()); + } } - _ => { - let type_ = match (res, ns) { - (Res::Def(DefKind::Const, _), _) => "const", - (Res::Def(DefKind::Static, _), _) => "static", - (Res::Def(DefKind::Struct, _), _) => "struct", - (Res::Def(DefKind::Enum, _), _) => "enum", - (Res::Def(DefKind::Union, _), _) => "union", - (Res::Def(DefKind::Trait, _), _) => "trait", - (Res::Def(DefKind::Mod, _), _) => "module", - (_, TypeNS) => "type", - (_, ValueNS) => "value", - (_, MacroNS) => "macro", + } + } + + let mut diag = lint.build(&msg); + + if let Some(link_range) = link_range { + if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) { + diag.set_span(sp); + diag.span_label(sp, "ambiguous link"); + + for (res, ns) in candidates { + let (action, mut suggestion) = match res { + Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => { + ("add parentheses", format!("{}()", path_str)) + } + Res::Def(DefKind::Macro(..), _) => { + ("add an exclamation mark", format!("{}!", path_str)) + } + _ => { + let type_ = match (res, ns) { + (Res::Def(DefKind::Const, _), _) => "const", + (Res::Def(DefKind::Static, _), _) => "static", + (Res::Def(DefKind::Struct, _), _) => "struct", + (Res::Def(DefKind::Enum, _), _) => "enum", + (Res::Def(DefKind::Union, _), _) => "union", + (Res::Def(DefKind::Trait, _), _) => "trait", + (Res::Def(DefKind::Mod, _), _) => "module", + (_, TypeNS) => "type", + (_, ValueNS) => "value", + (_, MacroNS) => "macro", + }; + + // FIXME: if this is an implied shortcut link, it's bad style to suggest `@` + ("prefix with the item type", format!("{}@{}", type_, path_str)) + } }; - // FIXME: if this is an implied shortcut link, it's bad style to suggest `@` - ("prefix with the item type", format!("{}@{}", type_, path_str)) - } - }; + if dox.bytes().nth(link_range.start) == Some(b'`') { + suggestion = format!("`{}`", suggestion); + } - if dox.bytes().nth(link_range.start) == Some(b'`') { - suggestion = format!("`{}`", suggestion); + diag.span_suggestion( + sp, + &format!("to link to the {}, {}", res.descr(), action), + suggestion, + Applicability::MaybeIncorrect, + ); + } + } else { + // blah blah blah\nblah\nblah [blah] blah blah\nblah blah + // ^ ~~~~ + // | link_range + // last_new_line_offset + let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); + let line = dox[last_new_line_offset..].lines().next().unwrap_or(""); + + // Print the line containing the `link_range` and manually mark it with '^'s. + diag.note(&format!( + "the link appears in this line:\n\n{line}\n\ + {indicator: <before$}{indicator:^<found$}", + line = line, + indicator = "", + before = link_range.start - last_new_line_offset, + found = link_range.len(), + )); } - - diag.span_suggestion( - sp, - &format!("to link to the {}, {}", res.descr(), action), - suggestion, - Applicability::MaybeIncorrect, - ); } - } else { - // blah blah blah\nblah\nblah [blah] blah blah\nblah blah - // ^ ~~~~ - // | link_range - // last_new_line_offset - let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); - let line = dox[last_new_line_offset..].lines().next().unwrap_or(""); - - // Print the line containing the `link_range` and manually mark it with '^'s. - diag.note(&format!( - "the link appears in this line:\n\n{line}\n\ - {indicator: <before$}{indicator:^<found$}", - line = line, - indicator = "", - before = link_range.start - last_new_line_offset, - found = link_range.len(), - )); - } - } + diag.emit(); + }, + ); - diag.emit(); } /// Given an enum variant's res, return the res of its enum and the associated fragment. diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 355ea15223b..a5213fe8635 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -342,24 +342,22 @@ pub fn look_for_tests<'tcx>( if check_missing_code == true && tests.found_tests == 0 { let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); - let mut diag = cx.tcx.struct_span_lint_hir( + cx.tcx.struct_span_lint_hir( lint::builtin::MISSING_DOC_CODE_EXAMPLES, hir_id, sp, - "missing code example in this documentation", + |lint| lint.build("missing code example in this documentation").emit(), ); - diag.emit(); } else if check_missing_code == false && tests.found_tests > 0 && !cx.renderinfo.borrow().access_levels.is_public(item.def_id) { - let mut diag = cx.tcx.struct_span_lint_hir( + cx.tcx.struct_span_lint_hir( lint::builtin::PRIVATE_DOC_TESTS, hir_id, span_of_attrs(&item.attrs).unwrap_or(item.source.span()), - "documentation test in private item", + |lint| lint.build("documentation test in private item").emit(), ); - diag.emit(); } } |
