diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 58566bdcc3549..984f5609860de 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -1670,7 +1670,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 8c18b1368a9d8..004835b230ab4 100644 --- a/src/librustc/lint.rs +++ b/src/librustc/lint.rs @@ -174,132 +174,164 @@ impl<'a> HashStable> 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, 'd>( + sess: &'s Session, lint: &'static Lint, level: Level, src: LintSource, span: Option, - msg: &str, -) -> DiagnosticBuilder<'a> { - 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), - }; + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>) + 'd, +) { + // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to + // the "real" work. + fn struct_lint_level_impl( + sess: &'s Session, + lint: &'static Lint, + level: Level, + src: LintSource, + span: Option, + decorate: Box FnOnce(LintDiagnosticBuilder<'b>) + 'd>, + ) { + let mut err = match (level, span) { + (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. - let lint_id = LintId::of(lint); - let future_incompatible = lint.future_incompatible; - - // If this code originates in a foreign macro, aka something that this crate - // did not itself author, then it's likely that there's nothing this crate - // can do about it. We probably want to skip the lint entirely. - if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) { - // Any suggestions made here are likely to be incorrect, so anything we - // emit shouldn't be automatically fixed by rustfix. - err.allow_suggestions(false); - - // If this is a future incompatible lint it'll become a hard error, so - // we have to emit *something*. Also allow lints to whitelist themselves - // on a case-by-case basis for emission in a foreign macro. - if future_incompatible.is_none() && !lint.report_in_external_macro { - 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; + // Check for future incompatibility lints and issue a stronger warning. + let lint_id = LintId::of(lint); + let future_incompatible = lint.future_incompatible; + + // If this code originates in a foreign macro, aka something that this crate + // did not itself author, then it's likely that there's nothing this crate + // can do about it. We probably want to skip the lint entirely. + if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) { + // Any suggestions made here are likely to be incorrect, so anything we + // emit shouldn't be automatically fixed by rustfix. + err.allow_suggestions(false); + + // If this is a future incompatible lint it'll become a hard error, so + // we have to emit *something*. Also allow lints to whitelist themselves + // on a case-by-case basis for emission in a foreign macro. + if future_incompatible.is_none() && !lint.report_in_external_macro { + 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; + } } - } - let name = lint.name_lower(); - match src { - LintSource::Default => { - sess.diag_note_once( - &mut err, - DiagnosticMessageId::from(lint), - &format!("`#[{}({})]` on by default", level.as_str(), name), - ); - } - LintSource::CommandLine(lint_flag_val) => { - let flag = match level { - Level::Warn => "-W", - Level::Deny => "-D", - Level::Forbid => "-F", - Level::Allow => panic!(), - }; - let hyphen_case_lint_name = name.replace("_", "-"); - if lint_flag_val.as_str() == name { + let name = lint.name_lower(); + match src { + LintSource::Default => { sess.diag_note_once( &mut err, DiagnosticMessageId::from(lint), - &format!( - "requested on the command line with `{} {}`", - flag, hyphen_case_lint_name - ), - ); - } else { - let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-"); - sess.diag_note_once( - &mut err, - DiagnosticMessageId::from(lint), - &format!( - "`{} {}` implied by `{} {}`", - flag, hyphen_case_lint_name, flag, hyphen_case_flag_val - ), + &format!("`#[{}({})]` on by default", level.as_str(), name), ); } - } - LintSource::Node(lint_attr_name, src, reason) => { - if let Some(rationale) = reason { - err.note(&rationale.as_str()); + LintSource::CommandLine(lint_flag_val) => { + let flag = match level { + Level::Warn => "-W", + Level::Deny => "-D", + Level::Forbid => "-F", + Level::Allow => panic!(), + }; + let hyphen_case_lint_name = name.replace("_", "-"); + if lint_flag_val.as_str() == name { + sess.diag_note_once( + &mut err, + DiagnosticMessageId::from(lint), + &format!( + "requested on the command line with `{} {}`", + flag, hyphen_case_lint_name + ), + ); + } else { + let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-"); + sess.diag_note_once( + &mut err, + DiagnosticMessageId::from(lint), + &format!( + "`{} {}` implied by `{} {}`", + flag, hyphen_case_lint_name, flag, hyphen_case_flag_val + ), + ); + } } - sess.diag_span_note_once( - &mut err, - DiagnosticMessageId::from(lint), - src, - "the lint level is defined here", - ); - if lint_attr_name.as_str() != name { - let level_str = level.as_str(); - sess.diag_note_once( + LintSource::Node(lint_attr_name, src, reason) => { + if let Some(rationale) = reason { + err.note(&rationale.as_str()); + } + sess.diag_span_note_once( &mut err, DiagnosticMessageId::from(lint), - &format!( - "`#[{}({})]` implied by `#[{}({})]`", - level_str, name, level_str, lint_attr_name - ), + src, + "the lint level is defined here", ); + if lint_attr_name.as_str() != name { + let level_str = level.as_str(); + sess.diag_note_once( + &mut err, + DiagnosticMessageId::from(lint), + &format!( + "`#[{}({})]` implied by `#[{}({})]`", + level_str, name, level_str, lint_attr_name + ), + ); + } } } - } - err.code(DiagnosticId::Lint(name)); - - if let Some(future_incompatible) = future_incompatible { - const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \ - it will become a hard error"; - - let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) { - "once this method is added to the standard library, \ - the ambiguity may cause an error or change in behavior!" - .to_owned() - } else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) { - "this borrowing pattern was not meant to be accepted, \ - and may become a hard error in the future" - .to_owned() - } else if let Some(edition) = future_incompatible.edition { - format!("{} in the {} edition!", STANDARD_MESSAGE, edition) - } else { - format!("{} in a future release!", STANDARD_MESSAGE) - }; - let citation = format!("for more information, see {}", future_incompatible.reference); - err.warn(&explanation); - err.note(&citation); - } + err.code(DiagnosticId::Lint(name)); + + if let Some(future_incompatible) = future_incompatible { + const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \ + it will become a hard error"; + + let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) { + "once this method is added to the standard library, \ + the ambiguity may cause an error or change in behavior!" + .to_owned() + } else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) { + "this borrowing pattern was not meant to be accepted, \ + and may become a hard error in the future" + .to_owned() + } else if let Some(edition) = future_incompatible.edition { + format!("{} in the {} edition!", STANDARD_MESSAGE, edition) + } else { + format!("{} in a future release!", STANDARD_MESSAGE) + }; + let citation = format!("for more information, see {}", future_incompatible.reference); + err.warn(&explanation); + err.note(&citation); + } - return err; + // Finally, run `decorate`. This function is also responsible for emitting the diagnostic. + decorate(LintDiagnosticBuilder::new(err)); + } + struct_lint_level_impl(sess, lint, level, src, span, Box::new(decorate)) } /// 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 7cbe77b9e82f0..dd30fb23c2e76 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, 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 349dbd74ad1c9..df3971a5ac3f1 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, 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, ) -> 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,25 @@ impl<'tcx> ConstEvalErr<'tcx> { } } lint.emit(); - ErrorHandled::Reported - } + }, + 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, - ) -> Result, 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 +149,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 +176,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 15f81bb3f47ed..72a7fcb6590d6 100644 --- a/src/librustc/traits/object_safety.rs +++ b/src/librustc/traits/object_safety.rs @@ -183,13 +183,15 @@ fn object_safety_violations_for_trait( 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) - ), - ) - .note(&violation.error_msg()) - .emit(); + |lint| { + lint.build(&format!( + "the trait `{}` cannot be made into an object", + tcx.def_path_str(trait_def_id) + )) + .note(&violation.error_msg()) + .emit() + }, + ); false } else { true diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index e559ea391cde0..46c580a38b181 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -16,6 +16,7 @@ use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine}; use crate::ty::subst::{InternalSubsts, Subst, SubstsRef}; use crate::ty::{self, TyCtxt, TypeFoldable}; +use rustc::lint::LintDiagnosticBuilder; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir::def_id::DefId; @@ -316,23 +317,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 { + + // 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 { Some(FutureCompatOverlapErrorKind::Issue43355) | None => { - struct_span_err!(tcx.sess, impl_span, E0119, "{}", msg) + let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); + decorate(LintDiagnosticBuilder::new(err)); } Some(kind) => { let lint = match kind { @@ -347,47 +394,10 @@ 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 f12032943f91f..05dcc194f8b5d 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -41,6 +41,7 @@ use crate::ty::{ExistentialPredicate, InferTy, ParamTy, PolyFnSig, Predicate, Pr use crate::ty::{InferConst, ParamConst}; use crate::ty::{List, TyKind, TyS}; use crate::util::common::ErrorReported; +use rustc::lint::LintDiagnosticBuilder; use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::SelfProfilerRef; @@ -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}; @@ -2550,16 +2550,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, - 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 { @@ -2603,20 +2593,20 @@ impl<'tcx> TyCtxt<'tcx> { lint: &'static Lint, hir_id: HirId, span: impl Into, - 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> { diff --git a/src/librustc_attr/builtin.rs b/src/librustc_attr/builtin.rs index be7c164395b0a..e452981146171 100644 --- a/src/librustc_attr/builtin.rs +++ b/src/librustc_attr/builtin.rs @@ -37,7 +37,9 @@ 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(); } @@ -634,7 +636,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 5286e8c19b462..189b5bd0f9e87 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 3c217c1d64373..9d9d9d0f33717 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -106,7 +106,11 @@ 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, @@ -355,6 +359,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 536259e054718..2b532f8c56fd3 100644 --- a/src/librustc_expand/base.rs +++ b/src/librustc_expand/base.rs @@ -1113,7 +1113,11 @@ 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 fb11b6771e9ca..a91d735622f4e 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 345665de63c3c..37c4beb4786ba 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -23,6 +23,7 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc::hir::map::Map; +use rustc::lint::LintDiagnosticBuilder; use rustc::traits::misc::can_type_implement_copy; use rustc::ty::{self, layout::VariantIdx, Ty, TyCtxt}; use rustc_ast_pretty::pprust::{self, expr_to_string}; @@ -77,14 +78,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( - condition_span, - "use `loop`", - "loop".to_owned(), - Applicability::MachineApplicable, - ) - .emit(); + 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(); + }) } } } @@ -104,8 +107,9 @@ impl BoxPointers { fn check_heap_type(&self, cx: &LateContext<'_, '_>, span: Span, ty: Ty<'_>) { for leaf_ty in ty.walk() { if leaf_ty.is_box() { - let m = format!("type uses owned (Box type) pointers: {}", ty); - cx.span_lint(BOX_POINTERS, span, &m); + cx.struct_span_lint(BOX_POINTERS, span, |lint| { + lint.build(&format!("type uses owned (Box type) pointers: {}", ty)).emit() + }); } } } @@ -174,29 +178,28 @@ 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( - 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, - ); - err.emit(); + cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, |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(); + }); } } } @@ -213,26 +216,32 @@ declare_lint! { declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); impl UnsafeCode { - fn report_unsafe(&self, cx: &EarlyContext<'_>, span: Span, desc: &'static str) { + fn report_unsafe( + &self, + cx: &EarlyContext<'_>, + span: Span, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ) { // This comes from a macro that has `#[allow_internal_unsafe]`. if span.allows_unsafe() { return; } - cx.span_lint(UNSAFE_CODE, span, desc); + cx.struct_span_lint(UNSAFE_CODE, span, decorate); } } impl EarlyLintPass for UnsafeCode { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { if attr.check_name(sym::allow_internal_unsafe) { - self.report_unsafe( - cx, - attr.span, - "`allow_internal_unsafe` allows defining \ + self.report_unsafe(cx, attr.span, |lint| { + lint.build( + "`allow_internal_unsafe` allows defining \ macros using unsafe without triggering \ the `unsafe_code` lint at their call site", - ); + ) + .emit() + }); } } @@ -240,7 +249,9 @@ impl EarlyLintPass for UnsafeCode { if let ast::ExprKind::Block(ref blk, _) = e.kind { // Don't warn about generated blocks; that'll just pollute the output. if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) { - self.report_unsafe(cx, blk.span, "usage of an `unsafe` block"); + self.report_unsafe(cx, blk.span, |lint| { + lint.build("usage of an `unsafe` block").emit() + }); } } } @@ -248,11 +259,15 @@ impl EarlyLintPass for UnsafeCode { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { match it.kind { ast::ItemKind::Trait(_, ast::Unsafety::Unsafe, ..) => { - self.report_unsafe(cx, it.span, "declaration of an `unsafe` trait") + self.report_unsafe(cx, it.span, |lint| { + lint.build("declaration of an `unsafe` trait").emit() + }) } ast::ItemKind::Impl { unsafety: ast::Unsafety::Unsafe, .. } => { - self.report_unsafe(cx, it.span, "implementation of an `unsafe` trait") + self.report_unsafe(cx, it.span, |lint| { + lint.build("implementation of an `unsafe` trait").emit() + }) } _ => return, @@ -268,13 +283,16 @@ impl EarlyLintPass for UnsafeCode { _: ast::NodeId, ) { match fk { - FnKind::ItemFn(_, ast::FnHeader { unsafety: ast::Unsafety::Unsafe, .. }, ..) => { - self.report_unsafe(cx, span, "declaration of an `unsafe` function") - } + FnKind::ItemFn(_, ast::FnHeader { unsafety: ast::Unsafety::Unsafe, .. }, ..) => self + .report_unsafe(cx, span, |lint| { + lint.build("declaration of an `unsafe` function").emit() + }), FnKind::Method(_, sig, ..) => { if sig.header.unsafety == ast::Unsafety::Unsafe { - self.report_unsafe(cx, span, "implementation of an `unsafe` method") + self.report_unsafe(cx, span, |lint| { + lint.build("implementation of an `unsafe` method").emit() + }) } } @@ -285,7 +303,9 @@ impl EarlyLintPass for UnsafeCode { fn check_trait_item(&mut self, cx: &EarlyContext<'_>, item: &ast::AssocItem) { if let ast::AssocItemKind::Fn(ref sig, None) = item.kind { if sig.header.unsafety == ast::Unsafety::Unsafe { - self.report_unsafe(cx, item.span, "declaration of an `unsafe` method") + self.report_unsafe(cx, item.span, |lint| { + lint.build("declaration of an `unsafe` method").emit() + }) } } } @@ -371,11 +391,9 @@ impl MissingDoc { let has_doc = attrs.iter().any(|a| has_doc(a)); if !has_doc { - cx.span_lint( - MISSING_DOCS, - cx.tcx.sess.source_map().def_span(sp), - &format!("missing documentation for {}", desc), - ); + cx.struct_span_lint(MISSING_DOCS, cx.tcx.sess.source_map().def_span(sp), |lint| { + lint.build(&format!("missing documentation for {}", desc)).emit() + }); } } } @@ -403,10 +421,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { for macro_def in krate.exported_macros { let has_doc = macro_def.attrs.iter().any(|a| has_doc(a)); if !has_doc { - cx.span_lint( + cx.struct_span_lint( MISSING_DOCS, cx.tcx.sess.source_map().def_span(macro_def.span), - "missing documentation for macro", + |lint| lint.build("missing documentation for macro").emit(), ); } } @@ -554,12 +572,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations { return; } if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() { - cx.span_lint( - MISSING_COPY_IMPLEMENTATIONS, - item.span, - "type could implement `Copy`; consider adding `impl \ + cx.struct_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, |lint| { + lint.build( + "type could implement `Copy`; consider adding `impl \ Copy`", - ) + ) + .emit() + }) } } } @@ -608,12 +627,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDebugImplementations { } if !self.impling_types.as_ref().unwrap().contains(&item.hir_id) { - cx.span_lint( - MISSING_DEBUG_IMPLEMENTATIONS, - item.span, - "type does not implement `fmt::Debug`; consider adding `#[derive(Debug)]` \ + cx.struct_span_lint(MISSING_DEBUG_IMPLEMENTATIONS, item.span, |lint| { + lint.build( + "type does not implement `fmt::Debug`; consider adding `#[derive(Debug)]` \ or a manual implementation", - ) + ) + .emit() + }) } } } @@ -649,20 +669,20 @@ impl EarlyLintPass for AnonymousParameters { ("".to_owned(), Applicability::HasPlaceholders) }; - cx.struct_span_lint( - ANONYMOUS_PARAMETERS, - arg.pat.span, - "anonymous parameters are deprecated and will be \ + cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |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(); + ) + .span_suggestion( + arg.pat.span, + "try naming the parameter or explicitly \ + ignoring it", + format!("_: {}", ty_snip), + appl, + ) + .emit(); + }) } } _ => (), @@ -696,14 +716,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 { @@ -768,21 +790,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(); + }); } } } @@ -840,20 +861,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems { match param.kind { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { - let mut err = 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, - ); - err.emit(); + cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, it.span, |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(); + }); break; } } @@ -865,25 +886,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(); + }); } } _ => {} @@ -908,7 +931,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes { match get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (&ty1.kind, &ty2.kind)) { Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) => { if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { - cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg); + cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| { + lint.build(msg).emit() + }); } } _ => (), @@ -958,7 +983,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnstableFeatures { if attr.check_name(sym::feature) { if let Some(items) = attr.meta_item_list() { for item in items { - ctx.span_lint(UNSTABLE_FEATURES, item.span(), "unstable feature"); + ctx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| { + lint.build("unstable feature").emit() + }); } } } @@ -993,28 +1020,26 @@ impl UnreachablePub { applicability = Applicability::MaybeIncorrect; } let def_span = cx.tcx.sess.source_map().def_span(span); - let mut err = 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(); + cx.struct_span_lint(UNREACHABLE_PUB, def_span, |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, - ); - if exportable { - err.help("or consider exporting it for use by other crates"); - } - err.emit(); + 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(); + }); } _ => {} } @@ -1129,22 +1154,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() { @@ -1157,19 +1184,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds { }) .collect(); if !spans.is_empty() { - let mut err = cx.struct_span_lint( - TYPE_ALIAS_BOUNDS, - spans, - "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(); + cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans, |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(); + }); } } } @@ -1241,15 +1267,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints { ConstEvaluatable(..) => continue, }; if predicate.is_global() { - cx.span_lint( - TRIVIAL_BOUNDS, - span, - &format!( + cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| { + lint.build(&format!( "{} bound {} does not depend on any type \ or lifetime parameters", predicate_kind_name, predicate - ), - ); + )) + .emit() + }); } } } @@ -1331,23 +1356,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(); + }); }; } } @@ -1393,7 +1422,9 @@ 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() + }); } } @@ -1474,18 +1505,16 @@ impl KeywordIdents { return; } - let mut 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.emit() + cx.struct_span_lint(KEYWORD_IDENTS, ident.span, |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() + }); } } @@ -1789,17 +1818,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements { } if !lint_spans.is_empty() { - let mut err = 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::>(), - Applicability::MachineApplicable, - ); - err.emit(); + cx.struct_span_lint(EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), |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::>(), + Applicability::MachineApplicable, + ) + .emit(); + }); } } } @@ -1826,15 +1860,13 @@ impl EarlyLintPass for IncompleteFeatures { .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span))) .filter(|(name, _)| rustc_feature::INCOMPLETE_FEATURES.iter().any(|f| name == &f)) .for_each(|(name, &span)| { - cx.struct_span_lint( - INCOMPLETE_FEATURES, - span, - &format!( + cx.struct_span_lint(INCOMPLETE_FEATURES, span, |lint| { + lint.build(&format!( "the feature `{}` is incomplete and may cause the compiler to crash", name, - ), - ) - .emit(); + )) + .emit() + }) }); } } @@ -2024,30 +2056,28 @@ 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( - INVALID_VALUE, - expr.span, - &format!( + cx.struct_span_lint(INVALID_VALUE, expr.span, |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` 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(); + )); + err.span_label(expr.span, "this code causes undefined behavior when executed"); + err.span_label( + expr.span, + "help: use `MaybeUninit` 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(); + }); } } } diff --git a/src/librustc_lint/context.rs b/src/librustc_lint/context.rs index 3b8cce5635d07..232b56c88f2b0 100644 --- a/src/librustc_lint/context.rs +++ b/src/librustc_lint/context.rs @@ -20,13 +20,14 @@ use crate::levels::LintLevelsBuilder; use crate::passes::{EarlyLintPassObject, LateLintPassObject}; use rustc::hir::map::definitions::{DefPathData, DisambiguatedDefPathData}; use rustc::lint::add_elided_lifetime_in_path_suggestion; +use rustc::lint::LintDiagnosticBuilder; use rustc::middle::privacy::AccessLevels; use rustc::middle::stability; 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_hir as hir; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_session::lint::BuiltinLintDiagnostics; @@ -473,150 +474,121 @@ pub trait LintContext: Sized { fn sess(&self) -> &Session; fn lints(&self) -> &LintStore; - fn lookup_and_emit>(&self, lint: &'static Lint, span: Option, msg: &str) { - self.lookup(lint, span, msg).emit(); - } - - fn lookup_and_emit_with_diagnostics>( + fn lookup_with_diagnostics>( &self, lint: &'static Lint, span: Option, - msg: &str, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), 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 ".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::".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| { + // We first generate a blank diagnostic. + let mut db = lint.build(""); + + // Now, set up surrounding context. + 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 ".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::".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(); + // Rewrap `db`, and pass control to the user. + decorate(LintDiagnosticBuilder::new(db)); + }); } fn lookup>( &self, lint: &'static Lint, span: Option, - msg: &str, - ) -> DiagnosticBuilder<'_>; - - /// Emit a lint at the appropriate level, for a particular span. - fn span_lint>(&self, lint: &'static Lint, span: S, msg: &str) { - self.lookup_and_emit(lint, Some(span), msg); - } + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), + ); fn struct_span_lint>( &self, lint: &'static Lint, span: S, - msg: &str, - ) -> DiagnosticBuilder<'_> { - self.lookup(lint, Some(span), msg) - } - - /// Emit a lint and note at the appropriate level, for a particular span. - fn span_lint_note( - &self, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Span, - note: &str, + decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), ) { - 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), decorate); } - - /// 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(); - } - /// Emit a lint at the appropriate level, with no associated span. - fn lint(&self, lint: &'static Lint, msg: &str) { - self.lookup_and_emit(lint, None as Option, msg); + fn lint(&self, lint: &'static Lint, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)) { + self.lookup(lint, None as Option, decorate); } } @@ -654,13 +626,13 @@ impl LintContext for LateContext<'_, '_> { &self, lint: &'static Lint, span: Option, - 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 +653,9 @@ impl LintContext for EarlyContext<'_> { &self, lint: &'static Lint, span: Option, - 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/early.rs b/src/librustc_lint/early.rs index 490114b2d4d2a..e2fa0de35c026 100644 --- a/src/librustc_lint/early.rs +++ b/src/librustc_lint/early.rs @@ -37,11 +37,18 @@ struct EarlyContextAndPass<'a, T: EarlyLintPass> { impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { fn check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { - self.context.lookup_and_emit_with_diagnostics( + let rustc_session::lint::BufferedEarlyLint { + span, + msg, + node_id: _, + lint_id: _, + diagnostic, + } = early_lint; + self.context.lookup_with_diagnostics( early_lint.lint_id.lint, - Some(early_lint.span.clone()), - &early_lint.msg, - early_lint.diagnostic, + Some(span), + |lint| lint.build(&msg).emit(), + diagnostic, ); } } diff --git a/src/librustc_lint/internal.rs b/src/librustc_lint/internal.rs index 8480c85075dd4..79a06e81410d0 100644 --- a/src/librustc_lint/internal.rs +++ b/src/librustc_lint/internal.rs @@ -37,16 +37,22 @@ 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,14 +91,16 @@ 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::`") - .span_suggestion( - span, - "try using ty:: directly", - "ty".to_string(), - Applicability::MaybeIncorrect, // ty maybe needs an import - ) - .emit(); + cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, |lint| { + lint.build("usage of `ty::TyKind::`") + .span_suggestion( + span, + "try using ty:: directly", + "ty".to_string(), + Applicability::MaybeIncorrect, // ty maybe needs an import + ) + .emit(); + }) } } } @@ -103,32 +111,28 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind { if let QPath::Resolved(_, path) = qpath { if let Some(last) = path.segments.iter().last() { if lint_ty_kind_usage(cx, last) { - cx.struct_span_lint( - USAGE_OF_TY_TYKIND, - path.span, - "usage of `ty::TyKind`", - ) - .help("try using `Ty` instead") - .emit(); + cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| { + lint.build("usage of `ty::TyKind`") + .help("try using `Ty` instead") + .emit(); + }) } else { if ty.span.from_expansion() { return; } if let Some(t) = is_ty_or_ty_ctxt(cx, ty) { if path.segments.len() > 1 { - cx.struct_span_lint( - USAGE_OF_QUALIFIED_TY, - path.span, - &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(); + cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |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(); + }) } } } @@ -142,19 +146,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind { } } if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) { - 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, - ) - .emit(); + cx.struct_span_lint(TY_PASS_BY_REFERENCE, ty.span, |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(); + }) } } _ => {} @@ -234,10 +236,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 4f30d2b222684..b58acac316ef2 100644 --- a/src/librustc_lint/levels.rs +++ b/src/librustc_lint/levels.rs @@ -1,12 +1,13 @@ use crate::context::{CheckLintNameResult, LintStore}; use crate::late::unerased_lint_store; use rustc::hir::map::Map; +use rustc::lint::LintDiagnosticBuilder; use rustc::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource}; 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_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, 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,50 @@ 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 +395,10 @@ impl<'a> LintLevelsBuilder<'a> { &self, lint: &'static Lint, span: Option, - 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 3c85a1b31b244..a0ca7ad1860e8 100644 --- a/src/librustc_lint/non_ascii_idents.rs +++ b/src/librustc_lint/non_ascii_idents.rs @@ -22,19 +22,13 @@ impl EarlyLintPass for NonAsciiIdents { if name_str.is_ascii() { return; } - cx.struct_span_lint( - NON_ASCII_IDENTS, - ident.span, - "identifier contains non-ASCII characters", - ) - .emit(); + cx.struct_span_lint(NON_ASCII_IDENTS, ident.span, |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", - ) - .emit(); + cx.struct_span_lint(UNCOMMON_CODEPOINTS, ident.span, |lint| { + lint.build("identifier contains uncommon Unicode codepoints").emit() + }) } } } diff --git a/src/librustc_lint/nonstandard_style.rs b/src/librustc_lint/nonstandard_style.rs index 6fdbfea7f03b3..d45ea10dfbf58 100644 --- a/src/librustc_lint/nonstandard_style.rs +++ b/src/librustc_lint/nonstandard_style.rs @@ -107,15 +107,17 @@ impl NonCamelCaseTypes { let name = &ident.name.as_str(); 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( - ident.span, - "convert the identifier to upper camel case", - to_camel_case(name), - Applicability::MaybeIncorrect, - ) - .emit(); + cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, |lint| { + let msg = format!("{} `{}` should have an upper camel case name", sort, name); + lint.build(&msg) + .span_suggestion( + ident.span, + "convert the identifier to upper camel case", + to_camel_case(name), + Applicability::MaybeIncorrect, + ) + .emit() + }) } } } @@ -225,23 +227,24 @@ impl NonSnakeCase { if !is_snake_case(name) { 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); - - // 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)); - } + cx.struct_span_lint(NON_SNAKE_CASE, ident.span, |lint| { + let msg = format!("{} `{}` should have a snake case name", sort, name); + 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)); + } - err.emit(); + err.emit(); + }); } } } @@ -386,19 +389,18 @@ declare_lint_pass!(NonUpperCaseGlobals => [NON_UPPER_CASE_GLOBALS]); impl NonUpperCaseGlobals { fn check_upper_case(cx: &LateContext<'_, '_>, sort: &str, ident: &Ident) { let name = &ident.name.as_str(); - 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) - .span_suggestion( - ident.span, - "convert the identifier to upper case", - uc, - Applicability::MaybeIncorrect, - ) - .emit(); + cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, |lint| { + let uc = NonSnakeCase::to_snake_case(&name).to_uppercase(); + lint.build(&format!("{} `{}` should have an upper case name", sort, name)) + .span_suggestion( + ident.span, + "convert the identifier to upper case", + uc, + Applicability::MaybeIncorrect, + ) + .emit(); + }) } } } diff --git a/src/librustc_lint/redundant_semicolon.rs b/src/librustc_lint/redundant_semicolon.rs index 21b244ad75d4e..68cb0b54f56c5 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 6bc6f58f3e7fd..5ffa9c1747fa9 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,32 @@ 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( - OVERFLOWING_LITERALS, - parent_expr.span, - &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(); - return true; - } + cx.struct_span_lint(OVERFLOWING_LITERALS, parent_expr.span, |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; + } + }); } } - - false + overwritten } // For `isize` & `usize`, be conservative with the warnings, so that the @@ -153,41 +151,39 @@ fn report_bin_hex_error( negative: bool, ) { let size = layout::Integer::from_attr(&cx.tcx, ty).size(); - let (t, actually) = match ty { - attr::IntType::SignedInt(t) => { - let actually = sign_extend(val, size) as i128; - (t.name_str(), actually.to_string()) - } - attr::IntType::UnsignedInt(t) => { - let actually = truncate(val, size); - (t.name_str(), actually.to_string()) - } - }; - let mut err = cx.struct_span_lint( - OVERFLOWING_LITERALS, - expr.span, - &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)); + cx.struct_span_lint(OVERFLOWING_LITERALS, expr.span, |lint| { + let (t, actually) = match ty { + attr::IntType::SignedInt(t) => { + let actually = sign_extend(val, size) as i128; + (t.name_str(), actually.to_string()) + } + attr::IntType::UnsignedInt(t) => { + let actually = truncate(val, size); + (t.name_str(), actually.to_string()) + } + }; + 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.emit(); + }); } // This function finds the next fitting type and generates a suggestion string. @@ -270,11 +266,9 @@ fn lint_int_literal<'a, 'tcx>( } } - cx.span_lint( - OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for `{}`", t.name_str()), - ); + cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { + lint.build(&format!("literal out of range for `{}`", t.name_str())).emit() + }); } } @@ -298,18 +292,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; } } @@ -327,11 +319,9 @@ fn lint_uint_literal<'a, 'tcx>( report_bin_hex_error(cx, e, attr::IntType::UnsignedInt(t), repr_str, lit_val, false); return; } - cx.span_lint( - OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for `{}`", t.name_str()), - ); + cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { + lint.build(&format!("literal out of range for `{}`", t.name_str())).emit() + }); } } @@ -361,11 +351,9 @@ fn lint_literal<'a, 'tcx>( _ => bug!(), }; if is_infinite == Ok(true) { - cx.span_lint( - OVERFLOWING_LITERALS, - e.span, - &format!("literal out of range for `{}`", t.name_str()), - ); + cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { + lint.build(&format!("literal out of range for `{}`", t.name_str())).emit() + }); } } _ => {} @@ -383,11 +371,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { } hir::ExprKind::Binary(binop, ref l, ref r) => { if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { - cx.span_lint( - UNUSED_COMPARISONS, - e.span, - "comparison is useless due to type limits", - ); + cx.struct_span_lint(UNUSED_COMPARISONS, e.span, |lint| { + lint.build("comparison is useless due to type limits").emit() + }); } } hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), @@ -883,22 +869,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 { @@ -1062,14 +1047,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { // We only warn if the largest variant is at least thrice as large as // the second-largest. if largest > slargest * 3 && slargest > 0 { - cx.span_lint( + cx.struct_span_lint( VARIANT_SIZE_DIFFERENCES, enum_definition.variants[largest_index].span, - &format!( - "enum variant is more than three times \ + |lint| { + lint.build(&format!( + "enum variant is more than three times \ larger ({} bytes) than the next largest", - largest - ), + largest + )) + .emit() + }, ); } } diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 272c4f29203d8..9681c81d7350b 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -104,16 +104,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { }; if let Some(must_use_op) = must_use_op { - cx.span_lint( - UNUSED_MUST_USE, - expr.span, - &format!("unused {} that must be used", must_use_op), - ); + cx.struct_span_lint(UNUSED_MUST_USE, expr.span, |lint| { + lint.build(&format!("unused {} that must be used", must_use_op)).emit() + }); op_warned = true; } if !(type_permits_lack_of_use || fn_warned || op_warned) { - cx.span_lint(UNUSED_RESULTS, s.span, "unused result"); + cx.struct_span_lint(UNUSED_RESULTS, s.span, |lint| lint.build("unused result").emit()); } // Returns whether an error has been emitted (and thus another does not need to be later). @@ -213,18 +211,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; } } @@ -245,7 +245,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathStatements { fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) { if let hir::StmtKind::Semi(ref expr) = s.kind { if let hir::ExprKind::Path(_) = expr.kind { - cx.span_lint(PATH_STATEMENTS, s.span, "path statement with no effect"); + cx.struct_span_lint(PATH_STATEMENTS, s.span, |lint| { + lint.build("path statement with no effect").emit() + }); } } } @@ -286,17 +288,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes { if !attr::is_used(attr) { debug!("emitting warning for: {:?}", attr); - cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute"); + cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| { + lint.build("unused attribute").emit() + }); // Is it a builtin attribute that must be used at the crate level? if attr_info.map_or(false, |(_, ty, ..)| ty == &AttributeType::CrateLevel) { - let msg = match attr.style { - ast::AttrStyle::Outer => { - "crate-level attribute should be an inner attribute: add an exclamation \ - mark: `#![foo]`" - } - ast::AttrStyle::Inner => "crate-level attribute should be in the root module", - }; - cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg); + cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| { + let msg = match attr.style { + ast::AttrStyle::Outer => { + "crate-level attribute should be an inner attribute: add an exclamation \ + mark: `#![foo]`" + } + ast::AttrStyle::Inner => "crate-level attribute should be in the root module", + }; + lint.build(msg).emit() + }); } } else { debug!("Attr was used: {:?}", attr); @@ -406,52 +412,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(); + }); } } @@ -623,8 +631,9 @@ impl UnusedImportBraces { ast::UseTreeKind::Nested(_) => return, }; - let msg = format!("braces around {} is unnecessary", node_name); - cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg); + cx.struct_span_lint(UNUSED_IMPORT_BRACES, item.span, |lint| { + lint.build(&format!("braces around {} is unnecessary", node_name)).emit() + }); } } } @@ -654,15 +663,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation { for adj in cx.tables.expr_adjustments(e) { if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind { - let msg = match m { - adjustment::AutoBorrowMutability::Not => { - "unnecessary allocation, use `&` instead" - } - adjustment::AutoBorrowMutability::Mut { .. } => { - "unnecessary allocation, use `&mut` instead" - } - }; - cx.span_lint(UNUSED_ALLOCATION, e.span, msg); + cx.struct_span_lint(UNUSED_ALLOCATION, e.span, |lint| { + let msg = match m { + adjustment::AutoBorrowMutability::Not => { + "unnecessary allocation, use `&` instead" + } + adjustment::AutoBorrowMutability::Mut { .. } => { + "unnecessary allocation, use `&mut` instead" + } + }; + lint.build(msg).emit() + }); } } } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 717359d75c3be..6ba72d25927c0 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 @@ -377,19 +379,16 @@ fn do_mir_borrowck<'a, 'tcx>( } let mut_span = tcx.sess.source_map().span_until_non_whitespace(span); - tcx.struct_span_lint_hir( - UNUSED_MUT, - lint_root, - span, - "variable does not need to be mutable", - ) - .span_suggestion_short( - mut_span, - "remove this `mut`", - String::new(), - Applicability::MachineApplicable, - ) - .emit(); + tcx.struct_span_lint_hir(UNUSED_MUT, lint_root, span, |lint| { + lint.build("variable does not need to be mutable") + .span_suggestion_short( + mut_span, + "remove this `mut`", + String::new(), + Applicability::MachineApplicable, + ) + .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 428db8356b122..2e8e4dac237bc 100644 --- a/src/librustc_mir/const_eval/eval_queries.rs +++ b/src/librustc_mir/const_eval/eval_queries.rs @@ -209,12 +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(); - ErrorHandled::Reported - } + 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 0c65b77a38240..9fea82d78c94f 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>( // 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 8dc185cd82b3b..08ef60aa58ea5 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, 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 { @@ -619,13 +623,15 @@ 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 d645f6cf183b4..9a2e2c56f1426 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,49 @@ 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 4f0e5bb45822b..7ed6c81eb63bd 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>, ) { 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 82822f0c471a4..eea439194a04a 100644 --- a/src/librustc_mir_build/hair/pattern/check_match.rs +++ b/src/librustc_mir_build/hair/pattern/check_match.rs @@ -286,25 +286,26 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa }) { let ty_path = cx.tcx.def_path_str(edef.did); - cx.tcx - .struct_span_lint_hir( - BINDINGS_WITH_VARIANT_NAME, - p.hir_id, - p.span, - &format!( + cx.tcx.struct_span_lint_hir( + BINDINGS_WITH_VARIANT_NAME, + p.hir_id, + p.span, + |lint| { + lint.build(&format!( "pattern binding `{}` is named the same as one \ - of the variants of the type `{}`", + 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(); + }, + ) } } } @@ -324,13 +325,15 @@ fn pat_is_catchall(pat: &super::Pat<'_>) -> bool { } fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option) { - 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) { @@ -339,7 +342,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 a21a0ee8a1b20..5fbe764430c54 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 4244e1b8d80a6..0017f800de702 100644 --- a/src/librustc_mir_build/lints.rs +++ b/src/librustc_mir_build/lints.rs @@ -124,18 +124,15 @@ 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( - UNCONDITIONAL_RECURSION, - hir_id, - sp, - "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(); + tcx.struct_span_lint_hir(UNCONDITIONAL_RECURSION, hir_id, sp, |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(); + }); } } diff --git a/src/librustc_parse/config.rs b/src/librustc_parse/config.rs index 0edd56680f933..8dec64c579e88 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)) - .note(CFG_ATTR_NOTE_REF) - .emit(), + Err(mut e) => { + e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) + .note(CFG_ATTR_NOTE_REF) + .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 88762dabd8a29..d41775a143ad6 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 e58eb9ffc51e7..905f6bef58e85 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 b1cab591fd97c..19ba8c8c0da9b 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -1054,7 +1054,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 8c1839da1cb8f..abb3ba5fb0cf3 100644 --- a/src/librustc_parse/parser/mod.rs +++ b/src/librustc_parse/parser/mod.rs @@ -1374,6 +1374,8 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec, 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 e07b0733739d1..a17b97507a0de 100644 --- a/src/librustc_parse/parser/pat.rs +++ b/src/librustc_parse/parser/pat.rs @@ -569,7 +569,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 84562fbb46ff2..f5e47608d589d 100644 --- a/src/librustc_parse/validate_attr.rs +++ b/src/librustc_parse/validate_attr.rs @@ -27,7 +27,11 @@ 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 +156,8 @@ 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 3ff1ba3bbfc8c..73881908dabae 100644 --- a/src/librustc_passes/check_attr.rs +++ b/src/librustc_passes/check_attr.rs @@ -92,14 +92,9 @@ impl CheckAttrVisitor<'tcx> { | Target::Method(MethodKind::Trait { body: true }) | Target::Method(MethodKind::Inherent) => true, Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { - self.tcx - .struct_span_lint_hir( - UNUSED_ATTRIBUTES, - hir_id, - attr.span, - "`#[inline]` is ignored on function prototypes", - ) - .emit(); + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build("`#[inline]` is ignored on function prototypes").emit() + }); true } // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with @@ -107,23 +102,19 @@ impl CheckAttrVisitor<'tcx> { // accidentally, to to be compatible with crates depending on them, we can't throw an // error here. Target::AssocConst => { - self.tcx - .struct_span_lint_hir( - 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( - "for more information, see issue #65833 \ - ", - ) - .emit(); + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |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( + "for more information, see issue #65833 \ + ", + ) + .emit(); + }); true } _ => { @@ -331,15 +322,16 @@ impl CheckAttrVisitor<'tcx> { || (is_simd && is_c) || (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item))) { - self.tcx - .struct_span_lint_hir( - CONFLICTING_REPR_HINTS, - hir_id, - hint_spans.collect::>(), - "conflicting representation hints", - ) - .code(rustc_errors::error_code!(E0566)) - .emit(); + self.tcx.struct_span_lint_hir( + CONFLICTING_REPR_HINTS, + hir_id, + hint_spans.collect::>(), + |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 2ff9d744f2c4d..1d92078146eb0 100644 --- a/src/librustc_passes/dead.rs +++ b/src/librustc_passes/dead.rs @@ -555,12 +555,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 7718139f6e924..f1ce92d13ad65 100644 --- a/src/librustc_passes/liveness.rs +++ b/src/librustc_passes/liveness.rs @@ -1513,45 +1513,47 @@ impl<'tcx> Liveness<'_, 'tcx> { if ln == self.s.exit_ln { false } else { self.assigned_on_exit(ln, var).is_some() }; if is_assigned { - self.ir - .tcx - .struct_span_lint_hir( - lint::builtin::UNUSED_VARIABLES, - hir_id, - spans, - &format!("variable `{}` is assigned to, but never used", name), - ) - .note(&format!("consider using `_{}` instead", name)) - .emit(); + self.ir.tcx.struct_span_lint_hir( + lint::builtin::UNUSED_VARIABLES, + hir_id, + spans, + |lint| { + lint.build(&format!("variable `{}` is assigned to, but never used", name)) + .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() } } } @@ -1565,27 +1567,27 @@ impl<'tcx> Liveness<'_, 'tcx> { fn report_dead_assign(&self, hir_id: HirId, spans: Vec, var: Variable, is_param: bool) { if let Some(name) = self.should_warn(var) { if is_param { - self.ir - .tcx - .struct_span_lint_hir( - lint::builtin::UNUSED_ASSIGNMENTS, - hir_id, - spans, - &format!("value passed to `{}` is never read", name), - ) - .help("maybe it is overwritten before being read?") - .emit(); + self.ir.tcx.struct_span_lint_hir( + lint::builtin::UNUSED_ASSIGNMENTS, + hir_id, + spans, + |lint| { + lint.build(&format!("value passed to `{}` is never read", name)) + .help("maybe it is overwritten before being read?") + .emit(); + }, + ) } else { - self.ir - .tcx - .struct_span_lint_hir( - lint::builtin::UNUSED_ASSIGNMENTS, - hir_id, - spans, - &format!("value assigned to `{}` is never read", name), - ) - .help("maybe it is overwritten before being read?") - .emit(); + self.ir.tcx.struct_span_lint_hir( + lint::builtin::UNUSED_ASSIGNMENTS, + hir_id, + spans, + |lint| { + lint.build(&format!("value assigned to `{}` is never read", name)) + .help("maybe it is overwritten before being read?") + .emit(); + }, + ) } } } diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs index 4009cc6d725ab..8083553a90e8e 100644 --- a/src/librustc_passes/stability.rs +++ b/src/librustc_passes/stability.rs @@ -603,16 +603,14 @@ 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", + an attribute to enable", feature, since - ), - ); + )) + .emit(); + }); } fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) { diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 74bb72d6fad7f..7f51bc25642b5 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -1781,17 +1781,20 @@ 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,11 +1817,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { err.emit(); } else { let err_code = if kind == "trait" { "E0445" } else { "E0446" }; - self.tcx.lint_hir( + self.tcx.struct_span_lint_hir( lint::builtin::PRIVATE_IN_PUBLIC, hir_id, self.span, - &format!("{} (error {})", msg, err_code), + |lint| lint.build(&format!("{} (error {})", msg, err_code)).emit(), ); } } diff --git a/src/librustc_resolve/lifetimes.rs b/src/librustc_resolve/lifetimes.rs index 6e9ed5fdc179c..6efe46dec4d2e 100644 --- a/src/librustc_resolve/lifetimes.rs +++ b/src/librustc_resolve/lifetimes.rs @@ -1556,22 +1556,28 @@ 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) => { @@ -1591,26 +1597,32 @@ 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 c2123876b679b..964d7c5e15098 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); } @@ -2211,34 +2211,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 d254a84df72ce..909f40ee98499 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -468,23 +468,20 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else { ("", lint::builtin::TRIVIAL_CASTS) }; - let mut err = fcx.tcx.struct_span_lint_hir( - lint, - self.expr.hir_id, - self.span, - &format!( + fcx.tcx.struct_span_lint_hir(lint, self.expr.hir_id, self.span, |err| { + err.build(&format!( "trivial {}cast: `{}` as `{}`", adjective, fcx.ty_to_string(t_expr), fcx.ty_to_string(t_cast) - ), - ); - err.help(&format!( - "cast can be replaced by coercion; this might \ - require {}a temporary variable", - type_asc_or - )); - err.emit(); + )) + .help(&format!( + "cast can be replaced by coercion; this might \ + require {}a temporary variable", + type_asc_or + )) + .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 2444fc60f77ba..9fad22673ad66 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,36 @@ 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 0a917a1853eb5..69284139c2fe9 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2801,15 +2801,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(); + self.tcx().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 ec098c1d89679..dd833d9751cb9 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(); + }); } } @@ -121,23 +123,23 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { // We do this in any edition. if extern_crate.warn_if_unused { if let Some(&span) = unused_extern_crates.get(&extern_crate.def_id) { - let msg = "unused extern crate"; - - // Removal suggestion span needs to include attributes (Issue #54400) - let span_with_attrs = tcx - .get_attrs(extern_crate.def_id) - .iter() - .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| { + // Removal suggestion span needs to include attributes (Issue #54400) + let span_with_attrs = tcx + .get_attrs(extern_crate.def_id) + .iter() + .map(|attr| attr.span) + .fold(span, |acc, attr_span| acc.to(attr_span)); + + lint.build("unused extern crate") + .span_suggestion_short( + span_with_attrs, + "remove it", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + }); continue; } } @@ -168,23 +170,26 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { if !tcx.get_attrs(extern_crate.def_id).is_empty() { continue; } - - // 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| { + // Otherwise, we can convert it into a `use` of some kind. + 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); + 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 4d812d2621c61..9782d602db4ee 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1149,14 +1149,17 @@ 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!( - "defaults for type parameters are only allowed in \ + |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 50d5f70f4889a..0e68a40df103c 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -669,39 +669,43 @@ 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: >(); - 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::>(); + 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: ( 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( - lint::builtin::MISSING_DOC_CODE_EXAMPLES, - hir_id, - sp, - "missing code example in this documentation", - ); - diag.emit(); + cx.tcx.struct_span_lint_hir(lint::builtin::MISSING_DOC_CODE_EXAMPLES, hir_id, sp, |lint| { + lint.build("missing code example in this documentation").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(); } } diff --git a/src/test/ui/type-alias-enum-variants/enum-variant-priority-lint-ambiguous_associated_items.rs b/src/test/ui/type-alias-enum-variants/enum-variant-priority-lint-ambiguous_associated_items.rs index 7f69590400b3c..5f3b711b31aa9 100644 --- a/src/test/ui/type-alias-enum-variants/enum-variant-priority-lint-ambiguous_associated_items.rs +++ b/src/test/ui/type-alias-enum-variants/enum-variant-priority-lint-ambiguous_associated_items.rs @@ -32,6 +32,7 @@ impl Tr for E { fn f() -> Self::V { 0 } //~^ ERROR ambiguous associated item //~| WARN this was previously accepted + //~| HELP use fully-qualified syntax } fn main() {}