1
Fork 0

Auto merge of #68725 - jumbatm:invert-control-in-struct_lint_level, r=Centril

Invert control in struct_lint_level.

Closes #67927

Changes the `struct_lint*` methods to take a  `decorate` function instead of a message string. This decorate function is also responsible for eventually stashing, emitting or cancelling the diagnostic. If the lint was allowed after all, the decorate function is not run at all, saving us from spending time formatting messages (and potentially other expensive work) for lints that don't end up being emitted.

r? @Centril
This commit is contained in:
bors 2020-02-11 14:45:00 +00:00
commit 95e0a2c50d
59 changed files with 1803 additions and 1619 deletions

View file

@ -1701,7 +1701,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
sub: Region<'tcx>, sub: Region<'tcx>,
) { ) {
self.construct_generic_bound_failure(region_scope_tree, span, origin, bound_kind, sub) self.construct_generic_bound_failure(region_scope_tree, span, origin, bound_kind, sub)
.emit() .emit();
} }
pub fn construct_generic_bound_failure( pub fn construct_generic_bound_failure(

View file

@ -174,20 +174,49 @@ impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
} }
} }
pub fn struct_lint_level<'a>( pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a>);
sess: &'a Session,
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, lint: &'static Lint,
level: Level, level: Level,
src: LintSource, src: LintSource,
span: Option<MultiSpan>, span: Option<MultiSpan>,
msg: &str, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>) + 'd,
) -> DiagnosticBuilder<'a> { ) {
// 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<MultiSpan>,
decorate: Box<dyn for<'b> FnOnce(LintDiagnosticBuilder<'b>) + 'd>,
) {
let mut err = match (level, span) { let mut err = match (level, span) {
(Level::Allow, _) => return sess.diagnostic().struct_dummy(), (Level::Allow, _) => {
(Level::Warn, Some(span)) => sess.struct_span_warn(span, msg), return;
(Level::Warn, None) => sess.struct_warn(msg), }
(Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => sess.struct_span_err(span, msg), (Level::Warn, Some(span)) => sess.struct_span_warn(span, ""),
(Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(msg), (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. // Check for future incompatibility lints and issue a stronger warning.
@ -209,7 +238,7 @@ pub fn struct_lint_level<'a>(
err.cancel(); err.cancel();
// Don't continue further, since we don't want to have // Don't continue further, since we don't want to have
// `diag_span_note_once` called for a diagnostic that isn't emitted. // `diag_span_note_once` called for a diagnostic that isn't emitted.
return err; return;
} }
} }
@ -299,7 +328,10 @@ pub fn struct_lint_level<'a>(
err.note(&citation); 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. /// Returns whether `span` originates in a foreign crate's external macro.

View file

@ -222,11 +222,13 @@ fn late_report_deprecation(
return; return;
} }
let mut diag = tcx.struct_span_lint_hir(lint, hir_id, span, message); 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) { if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
deprecation_suggestion(&mut diag, suggestion, span); deprecation_suggestion(&mut diag, suggestion, span);
} }
diag.emit(); diag.emit()
});
if hir_id == hir::DUMMY_HIR_ID { if hir_id == hir::DUMMY_HIR_ID {
span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_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 /// 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. /// not `None`, a deprecated lint attached to `id` will be emitted.
pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) { pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) {
let soft_handler = let soft_handler = |lint, span, msg: &_| {
|lint, span, msg: &_| self.lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), 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) { match self.eval_stability(def_id, id, span) {
EvalResult::Allow => {} EvalResult::Allow => {}
EvalResult::Deny { feature, reason, issue, is_soft } => { EvalResult::Deny { feature, reason, issue, is_soft } => {

View file

@ -83,18 +83,15 @@ impl<'tcx> ConstEvalErr<'tcx> {
&self, &self,
tcx: TyCtxtAt<'tcx>, tcx: TyCtxtAt<'tcx>,
message: &str, message: &str,
) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> { emit: impl FnOnce(DiagnosticBuilder<'_>),
self.struct_generic(tcx, message, None) ) -> Result<(), ErrorHandled> {
self.struct_generic(tcx, message, emit, None)
} }
pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled { pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
let err = self.struct_error(tcx, message); match self.struct_error(tcx, message, |mut e| e.emit()) {
match err { Ok(_) => ErrorHandled::Reported,
Ok(mut err) => { Err(x) => x,
err.emit();
ErrorHandled::Reported
}
Err(err) => err,
} }
} }
@ -105,9 +102,11 @@ impl<'tcx> ConstEvalErr<'tcx> {
lint_root: hir::HirId, lint_root: hir::HirId,
span: Option<Span>, span: Option<Span>,
) -> ErrorHandled { ) -> ErrorHandled {
let lint = self.struct_generic(tcx, message, Some(lint_root)); match self.struct_generic(
match lint { tcx,
Ok(mut lint) => { message,
|mut lint: DiagnosticBuilder<'_>| {
// Apply the span.
if let Some(span) = span { if let Some(span) = span {
let primary_spans = lint.span.primary_spans().to_vec(); let primary_spans = lint.span.primary_spans().to_vec();
// point at the actual error as the primary span // point at the actual error as the primary span
@ -121,18 +120,25 @@ impl<'tcx> ConstEvalErr<'tcx> {
} }
} }
lint.emit(); lint.emit();
ErrorHandled::Reported },
} Some(lint_root),
) {
Ok(_) => ErrorHandled::Reported,
Err(err) => err, 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( fn struct_generic(
&self, &self,
tcx: TyCtxtAt<'tcx>, tcx: TyCtxtAt<'tcx>,
message: &str, message: &str,
emit: impl FnOnce(DiagnosticBuilder<'_>),
lint_root: Option<hir::HirId>, lint_root: Option<hir::HirId>,
) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> { ) -> Result<(), ErrorHandled> {
let must_error = match self.error { let must_error = match self.error {
InterpError::MachineStop(_) => bug!("CTFE does not stop"), InterpError::MachineStop(_) => bug!("CTFE does not stop"),
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
@ -143,25 +149,8 @@ impl<'tcx> ConstEvalErr<'tcx> {
_ => false, _ => false,
}; };
trace!("reporting const eval failure at {:?}", self.span); trace!("reporting const eval failure at {:?}", self.span);
let mut err = if let (Some(lint_root), false) = (lint_root, must_error) {
let hir_id = self let add_span_labels = |err: &mut DiagnosticBuilder<'_>| {
.stacktrace
.iter()
.rev()
.filter_map(|frame| frame.lint_root)
.next()
.unwrap_or(lint_root);
tcx.struct_span_lint_hir(
rustc_session::lint::builtin::CONST_ERR,
hir_id,
tcx.span,
message,
)
} else if must_error {
struct_error(tcx, &self.error.to_string())
} else {
struct_error(tcx, message)
};
if !must_error { if !must_error {
err.span_label(self.span, self.error.to_string()); err.span_label(self.span, self.error.to_string());
} }
@ -173,7 +162,36 @@ impl<'tcx> ConstEvalErr<'tcx> {
err.span_label(frame_info.call_site, frame_info.to_string()); err.span_label(frame_info.call_site, frame_info.to_string());
} }
} }
Ok(err) };
if let (Some(lint_root), false) = (lint_root, must_error) {
let hir_id = self
.stacktrace
.iter()
.rev()
.filter_map(|frame| frame.lint_root)
.next()
.unwrap_or(lint_root);
tcx.struct_span_lint_hir(
rustc_session::lint::builtin::CONST_ERR,
hir_id,
tcx.span,
|lint| {
let mut err = lint.build(message);
add_span_labels(&mut err);
emit(err);
},
);
} else {
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);
};
Ok(())
} }
} }

View file

@ -227,18 +227,21 @@ fn object_safety_violations_for_trait(
{ {
// Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id.
// It's also hard to get a use site span, so we use the method definition span. // It's also hard to get a use site span, so we use the method definition span.
let mut err = tcx.struct_span_lint_hir( tcx.struct_span_lint_hir(
WHERE_CLAUSES_OBJECT_SAFETY, WHERE_CLAUSES_OBJECT_SAFETY,
hir::CRATE_HIR_ID, hir::CRATE_HIR_ID,
*span, *span,
&format!( |lint| {
let mut err = lint.build(&format!(
"the trait `{}` cannot be made into an object", "the trait `{}` cannot be made into an object",
tcx.def_path_str(trait_def_id) tcx.def_path_str(trait_def_id)
), ));
);
let node = tcx.hir().get_if_local(trait_def_id); let node = tcx.hir().get_if_local(trait_def_id);
let msg = if let Some(hir::Node::Item(item)) = node { let msg = if let Some(hir::Node::Item(item)) = node {
err.span_label(item.ident.span, "this trait cannot be made into an object..."); err.span_label(
item.ident.span,
"this trait cannot be made into an object...",
);
format!("...because {}", violation.error_msg()) format!("...because {}", violation.error_msg())
} else { } else {
format!( format!(
@ -252,12 +255,19 @@ fn object_safety_violations_for_trait(
err.help(&note); err.help(&note);
} }
(Some(_), Some((note, Some((sugg, span))))) => { (Some(_), Some((note, Some((sugg, span))))) => {
err.span_suggestion(span, &note, sugg, Applicability::MachineApplicable); err.span_suggestion(
span,
&note,
sugg,
Applicability::MachineApplicable,
);
} }
// Only provide the help if its a local trait, otherwise it's not actionable. // Only provide the help if its a local trait, otherwise it's not actionable.
_ => {} _ => {}
} }
err.emit(); err.emit();
},
);
false false
} else { } else {
true true

View file

@ -16,6 +16,7 @@ use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine}; use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine};
use crate::ty::subst::{InternalSubsts, Subst, SubstsRef}; use crate::ty::subst::{InternalSubsts, Subst, SubstsRef};
use crate::ty::{self, TyCtxt, TypeFoldable}; use crate::ty::{self, TyCtxt, TypeFoldable};
use rustc::lint::LintDiagnosticBuilder;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err; use rustc_errors::struct_span_err;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -317,6 +318,13 @@ pub(super) fn specialization_graph_provider(
}; };
if let Some(overlap) = overlap { if let Some(overlap) = overlap {
let impl_span =
tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
// 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!( let msg = format!(
"conflicting implementations of trait `{}`{}:{}", "conflicting implementations of trait `{}`{}:{}",
overlap.trait_desc, overlap.trait_desc,
@ -329,32 +337,14 @@ pub(super) fn specialization_graph_provider(
_ => "", _ => "",
} }
); );
let impl_span = let mut err = err.build(&msg);
tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
let mut err = match used_to_be_allowed {
None => struct_span_err!(tcx.sess, impl_span, E0119, "{}", msg),
Some(kind) => {
let lint = match kind {
FutureCompatOverlapErrorKind::Issue33140 => {
ORDER_DEPENDENT_TRAIT_OBJECTS
}
FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK,
};
tcx.struct_span_lint_hir(
lint,
tcx.hir().as_local_hir_id(impl_def_id).unwrap(),
impl_span,
&msg,
)
}
};
match tcx.span_of_impl(overlap.with_impl) { match tcx.span_of_impl(overlap.with_impl) {
Ok(span) => { Ok(span) => {
err.span_label( err.span_label(
tcx.sess.source_map().def_span(span), tcx.sess.source_map().def_span(span),
"first implementation here".to_string(), "first implementation here".to_string(),
); );
err.span_label( err.span_label(
impl_span, impl_span,
format!( format!(
@ -367,9 +357,10 @@ pub(super) fn specialization_graph_provider(
} }
Err(cname) => { Err(cname) => {
let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { let msg = match to_pretty_impl_header(tcx, overlap.with_impl) {
Some(s) => { Some(s) => format!(
format!("conflicting implementation in crate `{}`:\n- {}", cname, s) "conflicting implementation in crate `{}`:\n- {}",
} cname, s
),
None => format!("conflicting implementation in crate `{}`", cname), None => format!("conflicting implementation in crate `{}`", cname),
}; };
err.note(&msg); err.note(&msg);
@ -383,8 +374,29 @@ pub(super) fn specialization_graph_provider(
if overlap.involves_placeholder { if overlap.involves_placeholder {
coherence::add_placeholder_note(&mut err); coherence::add_placeholder_note(&mut err);
} }
err.emit()
};
err.emit(); match used_to_be_allowed {
None => {
let err = struct_span_err!(tcx.sess, impl_span, E0119, "");
decorate(LintDiagnosticBuilder::new(err));
}
Some(kind) => {
let lint = match kind {
FutureCompatOverlapErrorKind::Issue33140 => {
ORDER_DEPENDENT_TRAIT_OBJECTS
}
FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK,
};
tcx.struct_span_lint_hir(
lint,
tcx.hir().as_local_hir_id(impl_def_id).unwrap(),
impl_span,
decorate,
)
}
};
} }
} else { } else {
let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id);

View file

@ -41,6 +41,7 @@ use crate::ty::{ExistentialPredicate, InferTy, ParamTy, PolyFnSig, Predicate, Pr
use crate::ty::{InferConst, ParamConst}; use crate::ty::{InferConst, ParamConst};
use crate::ty::{List, TyKind, TyS}; use crate::ty::{List, TyKind, TyS};
use crate::util::common::ErrorReported; use crate::util::common::ErrorReported;
use rustc::lint::LintDiagnosticBuilder;
use rustc_attr as attr; use rustc_attr as attr;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::profiling::SelfProfilerRef;
@ -49,7 +50,6 @@ use rustc_data_structures::stable_hasher::{
hash_stable_hashmap, HashStable, StableHasher, StableVec, hash_stable_hashmap, HashStable, StableHasher, StableVec,
}; };
use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal};
use rustc_errors::DiagnosticBuilder;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, DefIndex, LOCAL_CRATE}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, DefIndex, LOCAL_CRATE};
@ -2551,16 +2551,6 @@ impl<'tcx> TyCtxt<'tcx> {
iter.intern_with(|xs| self.intern_goals(xs)) iter.intern_with(|xs| self.intern_goals(xs))
} }
pub fn lint_hir(
self,
lint: &'static Lint,
hir_id: HirId,
span: impl Into<MultiSpan>,
msg: &str,
) {
self.struct_span_lint_hir(lint, hir_id, span.into(), msg).emit()
}
/// Walks upwards from `id` to find a node which might change lint levels with attributes. /// 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. /// It stops at `bound` and just returns it if reached.
pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId { pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId {
@ -2604,20 +2594,20 @@ impl<'tcx> TyCtxt<'tcx> {
lint: &'static Lint, lint: &'static Lint,
hir_id: HirId, hir_id: HirId,
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
msg: &str, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
) -> DiagnosticBuilder<'tcx> { ) {
let (level, src) = self.lint_level_at_node(lint, hir_id); 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( pub fn struct_lint_node(
self, self,
lint: &'static Lint, lint: &'static Lint,
id: HirId, id: HirId,
msg: &str, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
) -> DiagnosticBuilder<'tcx> { ) {
let (level, src) = self.lint_level_at_node(lint, id); let (level, src) = self.lint_level_at_node(lint, id);
struct_lint_level(self.sess, lint, level, src, None, msg) struct_lint_level(self.sess, lint, level, src, None, decorate);
} }
pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx StableVec<TraitCandidate>> { pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx StableVec<TraitCandidate>> {

View file

@ -37,7 +37,9 @@ fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
.span_label(span, format!("expected one of {}", expected.join(", "))) .span_label(span, format!("expected one of {}", expected.join(", ")))
.emit(); .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 => { AttrError::MissingFeature => {
struct_span_err!(diag, span, E0546, "missing 'feature'").emit(); struct_span_err!(diag, span, E0546, "missing 'feature'").emit();
} }
@ -639,7 +641,7 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F
let (cfg, feature, has_feature) = gated_cfg; let (cfg, feature, has_feature) = gated_cfg;
if !has_feature(features) && !cfg_span.allows_unstable(*feature) { if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
let explain = format!("`cfg({})` is experimental and subject to change", cfg); 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();
} }
} }

View file

@ -194,6 +194,7 @@ impl Diagnostic {
found_extra: &dyn fmt::Display, found_extra: &dyn fmt::Display,
) -> &mut Self { ) -> &mut Self {
let expected_label = format!("expected {}", expected_label); let expected_label = format!("expected {}", expected_label);
let found_label = format!("found {}", found_label); let found_label = format!("found {}", found_label);
let (found_padding, expected_padding) = if expected_label.len() > found_label.len() { let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
(expected_label.len() - found_label.len(), 0) (expected_label.len() - found_label.len(), 0)

View file

@ -106,7 +106,11 @@ impl<'a> DiagnosticBuilder<'a> {
/// ///
/// See `emit` and `delay_as_bug` for details. /// See `emit` and `delay_as_bug` for details.
pub fn emit_unless(&mut self, delay: bool) { 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, /// Stashes diagnostic for possible later improvement in a different,
@ -369,6 +373,7 @@ impl<'a> DiagnosticBuilder<'a> {
/// Creates a new `DiagnosticBuilder` with an already constructed /// Creates a new `DiagnosticBuilder` with an already constructed
/// diagnostic. /// diagnostic.
crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> { crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
debug!("Created new diagnostic");
DiagnosticBuilder(Box::new(DiagnosticBuilderInner { DiagnosticBuilder(Box::new(DiagnosticBuilderInner {
handler, handler,
diagnostic, diagnostic,

View file

@ -1113,7 +1113,11 @@ pub fn expr_to_string(
err_msg: &str, err_msg: &str,
) -> Option<(Symbol, ast::StrStyle)> { ) -> Option<(Symbol, ast::StrStyle)> {
expr_to_spanned_string(cx, expr, err_msg) 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() .ok()
.map(|(symbol, style, _)| (symbol, style)) .map(|(symbol, style, _)| (symbol, style))
} }

View file

@ -77,13 +77,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter {
// to an array or to a slice. // to an array or to a slice.
_ => bug!("array type coerced to something other than array or 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 \ "this method call currently resolves to `<&{} as IntoIterator>::into_iter` (due \
to autoref coercions), but that might change in the future when \ to autoref coercions), but that might change in the future when \
`IntoIterator` impls for arrays are added.", `IntoIterator` impls for arrays are added.",
target, target,
); ))
cx.struct_span_lint(ARRAY_INTO_ITER, *span, &msg)
.span_suggestion( .span_suggestion(
call.ident.span, call.ident.span,
"use `.iter()` instead of `.into_iter()` to avoid ambiguity", "use `.iter()` instead of `.into_iter()` to avoid ambiguity",
@ -91,6 +91,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter {
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )
.emit(); .emit();
})
} }
} }
} }

View file

@ -23,6 +23,7 @@
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc::hir::map::Map; use rustc::hir::map::Map;
use rustc::lint::LintDiagnosticBuilder;
use rustc::traits::misc::can_type_implement_copy; use rustc::traits::misc::can_type_implement_copy;
use rustc::ty::{self, layout::VariantIdx, Ty, TyCtxt}; use rustc::ty::{self, layout::VariantIdx, Ty, TyCtxt};
use rustc_ast_pretty::pprust::{self, expr_to_string}; use rustc_ast_pretty::pprust::{self, expr_to_string};
@ -77,7 +78,8 @@ impl EarlyLintPass for WhileTrue {
if !lit.span.from_expansion() { if !lit.span.from_expansion() {
let msg = "denote infinite loops with `loop { ... }`"; let msg = "denote infinite loops with `loop { ... }`";
let condition_span = cx.sess.source_map().def_span(e.span); let condition_span = cx.sess.source_map().def_span(e.span);
cx.struct_span_lint(WHILE_TRUE, condition_span, msg) cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| {
lint.build(msg)
.span_suggestion_short( .span_suggestion_short(
condition_span, condition_span,
"use `loop`", "use `loop`",
@ -85,6 +87,7 @@ impl EarlyLintPass for WhileTrue {
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )
.emit(); .emit();
})
} }
} }
} }
@ -104,8 +107,9 @@ impl BoxPointers {
fn check_heap_type(&self, cx: &LateContext<'_, '_>, span: Span, ty: Ty<'_>) { fn check_heap_type(&self, cx: &LateContext<'_, '_>, span: Span, ty: Ty<'_>) {
for leaf_ty in ty.walk() { for leaf_ty in ty.walk() {
if leaf_ty.is_box() { if leaf_ty.is_box() {
let m = format!("type uses owned (Box type) pointers: {}", ty); cx.struct_span_lint(BOX_POINTERS, span, |lint| {
cx.span_lint(BOX_POINTERS, span, &m); lint.build(&format!("type uses owned (Box type) pointers: {}", ty)).emit()
});
} }
} }
} }
@ -174,11 +178,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
if cx.tcx.find_field_index(ident, &variant) if cx.tcx.find_field_index(ident, &variant)
== Some(cx.tcx.field_index(fieldpat.hir_id, cx.tables)) == Some(cx.tcx.field_index(fieldpat.hir_id, cx.tables))
{ {
let mut err = cx.struct_span_lint( cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, |lint| {
NON_SHORTHAND_FIELD_PATTERNS, let mut err = lint
fieldpat.span, .build(&format!("the `{}:` in this pattern is redundant", ident));
&format!("the `{}:` in this pattern is redundant", ident),
);
let binding = match binding_annot { let binding = match binding_annot {
hir::BindingAnnotation::Unannotated => None, hir::BindingAnnotation::Unannotated => None,
hir::BindingAnnotation::Mutable => Some("mut"), hir::BindingAnnotation::Mutable => Some("mut"),
@ -197,6 +199,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
err.emit(); err.emit();
});
} }
} }
} }
@ -213,26 +216,32 @@ declare_lint! {
declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]);
impl UnsafeCode { 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]`. // This comes from a macro that has `#[allow_internal_unsafe]`.
if span.allows_unsafe() { if span.allows_unsafe() {
return; return;
} }
cx.span_lint(UNSAFE_CODE, span, desc); cx.struct_span_lint(UNSAFE_CODE, span, decorate);
} }
} }
impl EarlyLintPass for UnsafeCode { impl EarlyLintPass for UnsafeCode {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
if attr.check_name(sym::allow_internal_unsafe) { if attr.check_name(sym::allow_internal_unsafe) {
self.report_unsafe( self.report_unsafe(cx, attr.span, |lint| {
cx, lint.build(
attr.span,
"`allow_internal_unsafe` allows defining \ "`allow_internal_unsafe` allows defining \
macros using unsafe without triggering \ macros using unsafe without triggering \
the `unsafe_code` lint at their call site", 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 { if let ast::ExprKind::Block(ref blk, _) = e.kind {
// Don't warn about generated blocks; that'll just pollute the output. // Don't warn about generated blocks; that'll just pollute the output.
if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) { 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) { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
match it.kind { match it.kind {
ast::ItemKind::Trait(_, ast::Unsafety::Unsafe, ..) => { 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, .. } => { 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, _ => return,
@ -274,7 +289,7 @@ impl EarlyLintPass for UnsafeCode {
FnCtxt::Assoc(_) if body.is_none() => "declaration of an `unsafe` method", FnCtxt::Assoc(_) if body.is_none() => "declaration of an `unsafe` method",
FnCtxt::Assoc(_) => "implementation of an `unsafe` method", FnCtxt::Assoc(_) => "implementation of an `unsafe` method",
}; };
self.report_unsafe(cx, span, msg); self.report_unsafe(cx, span, |lint| lint.build(msg).emit());
} }
} }
} }
@ -359,11 +374,9 @@ impl MissingDoc {
let has_doc = attrs.iter().any(|a| has_doc(a)); let has_doc = attrs.iter().any(|a| has_doc(a));
if !has_doc { if !has_doc {
cx.span_lint( cx.struct_span_lint(MISSING_DOCS, cx.tcx.sess.source_map().def_span(sp), |lint| {
MISSING_DOCS, lint.build(&format!("missing documentation for {}", desc)).emit()
cx.tcx.sess.source_map().def_span(sp), });
&format!("missing documentation for {}", desc),
);
} }
} }
} }
@ -391,10 +404,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
for macro_def in krate.exported_macros { for macro_def in krate.exported_macros {
let has_doc = macro_def.attrs.iter().any(|a| has_doc(a)); let has_doc = macro_def.attrs.iter().any(|a| has_doc(a));
if !has_doc { if !has_doc {
cx.span_lint( cx.struct_span_lint(
MISSING_DOCS, MISSING_DOCS,
cx.tcx.sess.source_map().def_span(macro_def.span), cx.tcx.sess.source_map().def_span(macro_def.span),
"missing documentation for macro", |lint| lint.build("missing documentation for macro").emit(),
); );
} }
} }
@ -542,12 +555,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations {
return; return;
} }
if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() { if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() {
cx.span_lint( cx.struct_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, |lint| {
MISSING_COPY_IMPLEMENTATIONS, lint.build(
item.span,
"type could implement `Copy`; consider adding `impl \ "type could implement `Copy`; consider adding `impl \
Copy`", Copy`",
) )
.emit()
})
} }
} }
} }
@ -596,15 +610,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDebugImplementations {
} }
if !self.impling_types.as_ref().unwrap().contains(&item.hir_id) { if !self.impling_types.as_ref().unwrap().contains(&item.hir_id) {
cx.span_lint( cx.struct_span_lint(MISSING_DEBUG_IMPLEMENTATIONS, item.span, |lint| {
MISSING_DEBUG_IMPLEMENTATIONS, lint.build(&format!(
item.span,
&format!(
"type does not implement `{}`; consider adding `#[derive(Debug)]` \ "type does not implement `{}`; consider adding `#[derive(Debug)]` \
or a manual implementation", or a manual implementation",
cx.tcx.def_path_str(debug) cx.tcx.def_path_str(debug)
), ))
); .emit()
});
} }
} }
} }
@ -632,17 +645,16 @@ impl EarlyLintPass for AnonymousParameters {
match arg.pat.kind { match arg.pat.kind {
ast::PatKind::Ident(_, ident, None) => { ast::PatKind::Ident(_, ident, None) => {
if ident.name == kw::Invalid { if ident.name == kw::Invalid {
cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| {
let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span); let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span);
let (ty_snip, appl) = if let Ok(snip) = ty_snip { let (ty_snip, appl) = if let Ok(ref snip) = ty_snip {
(snip, Applicability::MachineApplicable) (snip.as_str(), Applicability::MachineApplicable)
} else { } else {
("<type>".to_owned(), Applicability::HasPlaceholders) ("<type>", Applicability::HasPlaceholders)
}; };
cx.struct_span_lint( lint.build(
ANONYMOUS_PARAMETERS,
arg.pat.span,
"anonymous parameters are deprecated and will be \ "anonymous parameters are deprecated and will be \
removed in the next edition.", removed in the next edition.",
) )
@ -654,6 +666,7 @@ impl EarlyLintPass for AnonymousParameters {
appl, appl,
) )
.emit(); .emit();
})
} }
} }
_ => (), _ => (),
@ -687,7 +700,8 @@ fn lint_deprecated_attr(
msg: &str, msg: &str,
suggestion: Option<&str>, suggestion: Option<&str>,
) { ) {
cx.struct_span_lint(DEPRECATED, attr.span, &msg) cx.struct_span_lint(DEPRECATED, attr.span, |lint| {
lint.build(msg)
.span_suggestion_short( .span_suggestion_short(
attr.span, attr.span,
suggestion.unwrap_or("remove this attribute"), suggestion.unwrap_or("remove this attribute"),
@ -695,6 +709,7 @@ fn lint_deprecated_attr(
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )
.emit(); .emit();
})
} }
impl EarlyLintPass for DeprecatedAttr { impl EarlyLintPass for DeprecatedAttr {
@ -759,21 +774,20 @@ impl UnusedDocComment {
let span = sugared_span.take().unwrap_or_else(|| attr.span); let span = sugared_span.take().unwrap_or_else(|| attr.span);
if attr.is_doc_comment() || attr.check_name(sym::doc) { if attr.is_doc_comment() || attr.check_name(sym::doc) {
let mut err = cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, "unused doc comment"); cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| {
let mut err = lint.build("unused doc comment");
err.span_label( err.span_label(
node_span, node_span,
format!("rustdoc does not generate documentation for {}", node_kind), format!("rustdoc does not generate documentation for {}", node_kind),
); );
if is_macro_expansion { if is_macro_expansion {
err.help( err.help(
"to document an item produced by a macro, \ "to document an item produced by a macro, \
the macro must produce the documentation as part of its expansion", the macro must produce the documentation as part of its expansion",
); );
} }
err.emit(); err.emit();
});
} }
} }
} }
@ -831,20 +845,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems {
match param.kind { match param.kind {
GenericParamKind::Lifetime { .. } => {} GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
let mut err = cx.struct_span_lint( cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, it.span, |lint| {
NO_MANGLE_GENERIC_ITEMS, lint.build(
it.span,
"functions generic over types or consts must be mangled", "functions generic over types or consts must be mangled",
); )
err.span_suggestion_short( .span_suggestion_short(
no_mangle_attr.span, no_mangle_attr.span,
"remove this attribute", "remove this attribute",
String::new(), String::new(),
// Use of `#[no_mangle]` suggests FFI intent; correct // Use of `#[no_mangle]` suggests FFI intent; correct
// fix may be to monomorphize source by hand // fix may be to monomorphize source by hand
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); )
err.emit(); .emit();
});
break; break;
} }
} }
@ -855,8 +869,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems {
if attr::contains_name(&it.attrs, sym::no_mangle) { if attr::contains_name(&it.attrs, sym::no_mangle) {
// Const items do not refer to a particular location in memory, and therefore // Const items do not refer to a particular location in memory, and therefore
// don't have anything to attach a symbol to // don't have anything to attach a symbol to
cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| {
let msg = "const items should never be `#[no_mangle]`"; let msg = "const items should never be `#[no_mangle]`";
let mut err = cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg); let mut err = lint.build(msg);
// account for "pub const" (#45562) // account for "pub const" (#45562)
let start = cx let start = cx
@ -875,6 +890,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems {
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
err.emit(); err.emit();
});
} }
} }
_ => {} _ => {}
@ -894,12 +910,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableTransmutes {
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) {
use rustc_target::spec::abi::Abi::RustIntrinsic; use rustc_target::spec::abi::Abi::RustIntrinsic;
let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \
consider instead using an UnsafeCell";
match get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (&ty1.kind, &ty2.kind)) { match get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (&ty1.kind, &ty2.kind)) {
Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) => { Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) => {
if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not {
cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg); let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \
consider instead using an UnsafeCell";
cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| {
lint.build(msg).emit()
});
} }
} }
_ => (), _ => (),
@ -949,7 +967,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnstableFeatures {
if attr.check_name(sym::feature) { if attr.check_name(sym::feature) {
if let Some(items) = attr.meta_item_list() { if let Some(items) = attr.meta_item_list() {
for item in items { 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()
});
} }
} }
} }
@ -984,11 +1004,8 @@ impl UnreachablePub {
applicability = Applicability::MaybeIncorrect; applicability = Applicability::MaybeIncorrect;
} }
let def_span = cx.tcx.sess.source_map().def_span(span); let def_span = cx.tcx.sess.source_map().def_span(span);
let mut err = cx.struct_span_lint( cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| {
UNREACHABLE_PUB, let mut err = lint.build(&format!("unreachable `pub` {}", what));
def_span,
&format!("unreachable `pub` {}", what),
);
let replacement = if cx.tcx.features().crate_visibility_modifier { let replacement = if cx.tcx.features().crate_visibility_modifier {
"crate" "crate"
} else { } else {
@ -1006,6 +1023,7 @@ impl UnreachablePub {
err.help("or consider exporting it for use by other crates"); err.help("or consider exporting it for use by other crates");
} }
err.emit(); err.emit();
});
} }
_ => {} _ => {}
} }
@ -1114,17 +1132,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds {
let mut suggested_changing_assoc_types = false; let mut suggested_changing_assoc_types = false;
// There must not be a where clause // There must not be a where clause
if !type_alias_generics.where_clause.predicates.is_empty() { if !type_alias_generics.where_clause.predicates.is_empty() {
cx.lint(
TYPE_ALIAS_BOUNDS,
|lint| {
let mut err = lint.build("where clauses are not enforced in type aliases");
let spans: Vec<_> = type_alias_generics let spans: Vec<_> = type_alias_generics
.where_clause .where_clause
.predicates .predicates
.iter() .iter()
.map(|pred| pred.span()) .map(|pred| pred.span())
.collect(); .collect();
let mut err = cx.struct_span_lint( err.set_span(spans);
TYPE_ALIAS_BOUNDS,
spans,
"where clauses are not enforced in type aliases",
);
err.span_suggestion( err.span_suggestion(
type_alias_generics.where_clause.span_for_predicates_or_empty_place(), 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", "the clause will not be checked when the type alias is used, and should be removed",
@ -1136,6 +1154,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds {
suggested_changing_assoc_types = true; suggested_changing_assoc_types = true;
} }
err.emit(); err.emit();
},
);
} }
// The parameters must not have bounds // The parameters must not have bounds
for param in type_alias_generics.params.iter() { for param in type_alias_generics.params.iter() {
@ -1148,11 +1168,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds {
}) })
.collect(); .collect();
if !spans.is_empty() { if !spans.is_empty() {
let mut err = cx.struct_span_lint( cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans, |lint| {
TYPE_ALIAS_BOUNDS, let mut err =
spans, lint.build("bounds on generic parameters are not enforced in type aliases");
"bounds on generic parameters are not enforced in type aliases",
);
let msg = "the bound will not be checked when the type alias is used, \ let msg = "the bound will not be checked when the type alias is used, \
and should be removed"; and should be removed";
err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable); err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable);
@ -1161,6 +1179,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds {
suggested_changing_assoc_types = true; suggested_changing_assoc_types = true;
} }
err.emit(); err.emit();
});
} }
} }
} }
@ -1232,15 +1251,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints {
ConstEvaluatable(..) => continue, ConstEvaluatable(..) => continue,
}; };
if predicate.is_global() { if predicate.is_global() {
cx.span_lint( cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| {
TRIVIAL_BOUNDS, lint.build(&format!(
span,
&format!(
"{} bound {} does not depend on any type \ "{} bound {} does not depend on any type \
or lifetime parameters", or lifetime parameters",
predicate_kind_name, predicate predicate_kind_name, predicate
), ))
); .emit()
});
} }
} }
} }
@ -1317,28 +1335,32 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
let suggestion = "use `..=` for an inclusive range"; let suggestion = "use `..=` for an inclusive range";
if parenthesise { if parenthesise {
self.node_id = Some(pat.id); self.node_id = Some(pat.id);
cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| {
let end = expr_to_string(&end); let end = expr_to_string(&end);
let replace = match start { let replace = match start {
Some(start) => format!("&({}..={})", expr_to_string(&start), end), Some(start) => format!("&({}..={})", expr_to_string(&start), end),
None => format!("&(..={})", end), None => format!("&(..={})", end),
}; };
let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg); lint.build(msg)
err.span_suggestion( .span_suggestion(
pat.span, pat.span,
suggestion, suggestion,
replace, replace,
Applicability::MachineApplicable, Applicability::MachineApplicable,
); )
err.emit(); .emit();
});
} else { } else {
let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, msg); cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| {
err.span_suggestion_short( lint.build(msg)
.span_suggestion_short(
join, join,
suggestion, suggestion,
"..=".to_owned(), "..=".to_owned(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); )
err.emit(); .emit();
});
}; };
} }
} }
@ -1384,7 +1406,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestItems {
} }
if let Some(attr) = attr::find_by_name(&it.attrs, sym::rustc_test_marker) { if let Some(attr) = attr::find_by_name(&it.attrs, sym::rustc_test_marker) {
cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, "cannot test inner items").emit(); cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| {
lint.build("cannot test inner items").emit()
});
} }
} }
@ -1465,18 +1489,16 @@ impl KeywordIdents {
return; return;
} }
let mut lint = cx.struct_span_lint( cx.struct_span_lint(KEYWORD_IDENTS, ident.span, |lint| {
KEYWORD_IDENTS, lint.build(&format!("`{}` is a keyword in the {} edition", ident, next_edition))
ident.span, .span_suggestion(
&format!("`{}` is a keyword in the {} edition", ident, next_edition),
);
lint.span_suggestion(
ident.span, ident.span,
"you can use a raw identifier to stay compatible", "you can use a raw identifier to stay compatible",
format!("r#{}", ident), format!("r#{}", ident),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); )
lint.emit() .emit()
});
} }
} }
@ -1780,17 +1802,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements {
} }
if !lint_spans.is_empty() { if !lint_spans.is_empty() {
let mut err = cx.struct_span_lint( cx.struct_span_lint(EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), |lint| {
EXPLICIT_OUTLIVES_REQUIREMENTS, lint.build("outlives requirements can be inferred")
lint_spans.clone(), .multipart_suggestion(
"outlives requirements can be inferred", if bound_count == 1 {
); "remove this bound"
err.multipart_suggestion( } else {
if bound_count == 1 { "remove this bound" } else { "remove these bounds" }, "remove these bounds"
lint_spans.into_iter().map(|span| (span, "".to_owned())).collect::<Vec<_>>(), },
lint_spans
.into_iter()
.map(|span| (span, "".to_owned()))
.collect::<Vec<_>>(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); )
err.emit(); .emit();
});
} }
} }
} }
@ -1817,15 +1844,13 @@ impl EarlyLintPass for IncompleteFeatures {
.chain(features.declared_lib_features.iter().map(|(name, span)| (name, span))) .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
.filter(|(name, _)| rustc_feature::INCOMPLETE_FEATURES.iter().any(|f| name == &f)) .filter(|(name, _)| rustc_feature::INCOMPLETE_FEATURES.iter().any(|f| name == &f))
.for_each(|(name, &span)| { .for_each(|(name, &span)| {
cx.struct_span_lint( cx.struct_span_lint(INCOMPLETE_FEATURES, span, |lint| {
INCOMPLETE_FEATURES, lint.build(&format!(
span,
&format!(
"the feature `{}` is incomplete and may cause the compiler to crash", "the feature `{}` is incomplete and may cause the compiler to crash",
name, name,
), ))
) .emit()
.emit(); })
}); });
} }
} }
@ -2015,18 +2040,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
// We are extremely conservative with what we warn about. // We are extremely conservative with what we warn about.
let conjured_ty = cx.tables.expr_ty(expr); let conjured_ty = cx.tables.expr_ty(expr);
if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) { if let Some((msg, span)) = ty_find_init_error(cx.tcx, conjured_ty, init) {
let mut err = cx.struct_span_lint( cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| {
INVALID_VALUE, let mut err = lint.build(&format!(
expr.span,
&format!(
"the type `{}` does not permit {}", "the type `{}` does not permit {}",
conjured_ty, conjured_ty,
match init { match init {
InitKind::Zeroed => "zero-initialization", InitKind::Zeroed => "zero-initialization",
InitKind::Uninit => "being left uninitialized", InitKind::Uninit => "being left uninitialized",
}, },
), ));
);
err.span_label(expr.span, "this code causes undefined behavior when executed"); err.span_label(expr.span, "this code causes undefined behavior when executed");
err.span_label( err.span_label(
expr.span, expr.span,
@ -2039,6 +2061,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
err.note(&msg); err.note(&msg);
} }
err.emit(); err.emit();
});
} }
} }
} }

View file

@ -20,13 +20,14 @@ use crate::levels::LintLevelsBuilder;
use crate::passes::{EarlyLintPassObject, LateLintPassObject}; use crate::passes::{EarlyLintPassObject, LateLintPassObject};
use rustc::hir::map::definitions::{DefPathData, DisambiguatedDefPathData}; use rustc::hir::map::definitions::{DefPathData, DisambiguatedDefPathData};
use rustc::lint::add_elided_lifetime_in_path_suggestion; use rustc::lint::add_elided_lifetime_in_path_suggestion;
use rustc::lint::LintDiagnosticBuilder;
use rustc::middle::privacy::AccessLevels; use rustc::middle::privacy::AccessLevels;
use rustc::middle::stability; use rustc::middle::stability;
use rustc::ty::layout::{LayoutError, LayoutOf, TyLayout}; use rustc::ty::layout::{LayoutError, LayoutOf, TyLayout};
use rustc::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; use rustc::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync; 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 as hir;
use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::def_id::{CrateNum, DefId};
use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::lint::BuiltinLintDiagnostics;
@ -473,19 +474,18 @@ pub trait LintContext: Sized {
fn sess(&self) -> &Session; fn sess(&self) -> &Session;
fn lints(&self) -> &LintStore; fn lints(&self) -> &LintStore;
fn lookup_and_emit<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: Option<S>, msg: &str) { fn lookup_with_diagnostics(
self.lookup(lint, span, msg).emit();
}
fn lookup_and_emit_with_diagnostics<S: Into<MultiSpan>>(
&self, &self,
lint: &'static Lint, lint: &'static Lint,
span: Option<S>, span: Option<impl Into<MultiSpan>>,
msg: &str, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
diagnostic: BuiltinLintDiagnostics, diagnostic: BuiltinLintDiagnostics,
) { ) {
let mut db = self.lookup(lint, span, msg); 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(); let sess = self.sess();
match diagnostic { match diagnostic {
BuiltinLintDiagnostics::Normal => (), BuiltinLintDiagnostics::Normal => (),
@ -504,7 +504,8 @@ pub trait LintContext: Sized {
Ok(ref s) => { Ok(ref s) => {
// FIXME(Manishearth) ideally the emitting code // FIXME(Manishearth) ideally the emitting code
// can tell us whether or not this is global // can tell us whether or not this is global
let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; let opt_colon =
if s.trim_start().starts_with("::") { "" } else { "::" };
(format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable) (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
} }
@ -518,7 +519,9 @@ pub trait LintContext: Sized {
"names from parent modules are not accessible without an explicit import", "names from parent modules are not accessible without an explicit import",
); );
} }
BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(
span_def,
) => {
db.span_note(span_def, "the macro is defined here"); db.span_note(span_def, "the macro is defined here");
} }
BuiltinLintDiagnostics::ElidedLifetimesInPaths( BuiltinLintDiagnostics::ElidedLifetimesInPaths(
@ -563,60 +566,31 @@ pub trait LintContext: Sized {
stability::deprecation_suggestion(&mut db, suggestion, span) stability::deprecation_suggestion(&mut db, suggestion, span)
} }
} }
// Rewrap `db`, and pass control to the user.
db.emit(); decorate(LintDiagnosticBuilder::new(db));
});
} }
// FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to
// set the span in their `decorate` function (preferably using set_span).
fn lookup<S: Into<MultiSpan>>( fn lookup<S: Into<MultiSpan>>(
&self, &self,
lint: &'static Lint, lint: &'static Lint,
span: Option<S>, span: Option<S>,
msg: &str, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
) -> DiagnosticBuilder<'_>; );
/// Emit a lint at the appropriate level, for a particular span.
fn span_lint<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: S, msg: &str) {
self.lookup_and_emit(lint, Some(span), msg);
}
fn struct_span_lint<S: Into<MultiSpan>>( fn struct_span_lint<S: Into<MultiSpan>>(
&self, &self,
lint: &'static Lint, lint: &'static Lint,
span: S, span: S,
msg: &str, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
) -> 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,
) { ) {
let mut err = self.lookup(lint, Some(span), msg); self.lookup(lint, Some(span), decorate);
if note_span == span {
err.note(note);
} else {
err.span_note(note_span, note);
} }
err.emit();
}
/// Emit a lint and help at the appropriate level, for a particular span.
fn span_lint_help(&self, lint: &'static Lint, span: Span, msg: &str, help: &str) {
let mut err = self.lookup(lint, Some(span), msg);
self.span_lint(lint, span, msg);
err.span_help(span, help);
err.emit();
}
/// Emit a lint at the appropriate level, with no associated span. /// Emit a lint at the appropriate level, with no associated span.
fn lint(&self, lint: &'static Lint, msg: &str) { fn lint(&self, lint: &'static Lint, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)) {
self.lookup_and_emit(lint, None as Option<Span>, msg); self.lookup(lint, None as Option<Span>, decorate);
} }
} }
@ -654,13 +628,13 @@ impl LintContext for LateContext<'_, '_> {
&self, &self,
lint: &'static Lint, lint: &'static Lint,
span: Option<S>, span: Option<S>,
msg: &str, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
) -> DiagnosticBuilder<'_> { ) {
let hir_id = self.last_node_with_lint_attrs; let hir_id = self.last_node_with_lint_attrs;
match span { match span {
Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, msg), Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, decorate),
None => self.tcx.struct_lint_node(lint, hir_id, msg), None => self.tcx.struct_lint_node(lint, hir_id, decorate),
} }
} }
} }
@ -681,9 +655,9 @@ impl LintContext for EarlyContext<'_> {
&self, &self,
lint: &'static Lint, lint: &'static Lint,
span: Option<S>, span: Option<S>,
msg: &str, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
) -> DiagnosticBuilder<'_> { ) {
self.builder.struct_lint(lint, span.map(|s| s.into()), msg) self.builder.struct_lint(lint, span.map(|s| s.into()), decorate)
} }
} }

View file

@ -37,11 +37,18 @@ struct EarlyContextAndPass<'a, T: EarlyLintPass> {
impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
fn check_id(&mut self, id: ast::NodeId) { fn check_id(&mut self, id: ast::NodeId) {
for early_lint in self.context.buffered.take(id) { for early_lint in self.context.buffered.take(id) {
self.context.lookup_and_emit_with_diagnostics( let rustc_session::lint::BufferedEarlyLint {
early_lint.lint_id.lint, span,
Some(early_lint.span.clone()), msg,
&early_lint.msg, node_id: _,
early_lint.diagnostic, lint_id,
diagnostic,
} = early_lint;
self.context.lookup_with_diagnostics(
lint_id.lint,
Some(span),
|lint| lint.build(&msg).emit(),
diagnostic,
); );
} }
} }

View file

@ -37,16 +37,22 @@ impl_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]);
impl EarlyLintPass for DefaultHashTypes { impl EarlyLintPass for DefaultHashTypes {
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
if let Some(replace) = self.map.get(&ident.name) { if let Some(replace) = self.map.get(&ident.name) {
cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, |lint| {
// 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 msg = format!("Prefer {} over {}, it has better performance", replace, ident);
let mut db = cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, &msg); lint.build(&msg)
db.span_suggestion( .span_suggestion(
ident.span, ident.span,
"use", "use",
replace.to_string(), replace.to_string(),
Applicability::MaybeIncorrect, // FxHashMap, ... needs another import Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
); )
db.note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace)) .note(&format!(
"a `use rustc_data_structures::fx::{}` may be necessary",
replace
))
.emit(); .emit();
});
} }
} }
} }
@ -85,7 +91,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
if let Some(last) = segments.last() { if let Some(last) = segments.last() {
let span = path.span.with_hi(last.ident.span.hi()); let span = path.span.with_hi(last.ident.span.hi());
if lint_ty_kind_usage(cx, last) { if lint_ty_kind_usage(cx, last) {
cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, "usage of `ty::TyKind::<kind>`") cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, |lint| {
lint.build("usage of `ty::TyKind::<kind>`")
.span_suggestion( .span_suggestion(
span, span,
"try using ty::<kind> directly", "try using ty::<kind> directly",
@ -93,6 +100,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
Applicability::MaybeIncorrect, // ty maybe needs an import Applicability::MaybeIncorrect, // ty maybe needs an import
) )
.emit(); .emit();
})
} }
} }
} }
@ -103,24 +111,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
if let QPath::Resolved(_, path) = qpath { if let QPath::Resolved(_, path) = qpath {
if let Some(last) = path.segments.iter().last() { if let Some(last) = path.segments.iter().last() {
if lint_ty_kind_usage(cx, last) { if lint_ty_kind_usage(cx, last) {
cx.struct_span_lint( cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| {
USAGE_OF_TY_TYKIND, lint.build("usage of `ty::TyKind`")
path.span,
"usage of `ty::TyKind`",
)
.help("try using `Ty` instead") .help("try using `Ty` instead")
.emit(); .emit();
})
} else { } else {
if ty.span.from_expansion() { if ty.span.from_expansion() {
return; return;
} }
if let Some(t) = is_ty_or_ty_ctxt(cx, ty) { if let Some(t) = is_ty_or_ty_ctxt(cx, ty) {
if path.segments.len() > 1 { if path.segments.len() > 1 {
cx.struct_span_lint( cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| {
USAGE_OF_QUALIFIED_TY, lint.build(&format!("usage of qualified `ty::{}`", t))
path.span,
&format!("usage of qualified `ty::{}`", t),
)
.span_suggestion( .span_suggestion(
path.span, path.span,
"try using it unqualified", "try using it unqualified",
@ -129,6 +132,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
) )
.emit(); .emit();
})
} }
} }
} }
@ -142,11 +146,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
} }
} }
if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) { if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) {
cx.struct_span_lint( cx.struct_span_lint(TY_PASS_BY_REFERENCE, ty.span, |lint| {
TY_PASS_BY_REFERENCE, lint.build(&format!("passing `{}` by reference", t))
ty.span,
&format!("passing `{}` by reference", t),
)
.span_suggestion( .span_suggestion(
ty.span, ty.span,
"try passing by value", "try passing by value",
@ -155,6 +156,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
) )
.emit(); .emit();
})
} }
} }
_ => {} _ => {}
@ -234,10 +236,12 @@ impl EarlyLintPass for LintPassImpl {
cx.struct_span_lint( cx.struct_span_lint(
LINT_PASS_IMPL_WITHOUT_MACRO, LINT_PASS_IMPL_WITHOUT_MACRO,
lint_pass.path.span, 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") .help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
.emit(); .emit();
},
)
} }
} }
} }

View file

@ -1,12 +1,13 @@
use crate::context::{CheckLintNameResult, LintStore}; use crate::context::{CheckLintNameResult, LintStore};
use crate::late::unerased_lint_store; use crate::late::unerased_lint_store;
use rustc::hir::map::Map; use rustc::hir::map::Map;
use rustc::lint::LintDiagnosticBuilder;
use rustc::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource}; use rustc::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource};
use rustc::ty::query::Providers; use rustc::ty::query::Providers;
use rustc::ty::TyCtxt; use rustc::ty::TyCtxt;
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap; 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 as hir;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_hir::{intravisit, HirId}; use rustc_hir::{intravisit, HirId};
@ -39,8 +40,8 @@ fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap {
tcx.arena.alloc(builder.levels.build_map()) tcx.arena.alloc(builder.levels.build_map())
} }
pub struct LintLevelsBuilder<'a> { pub struct LintLevelsBuilder<'s> {
sess: &'a Session, sess: &'s Session,
sets: LintLevelSets, sets: LintLevelSets,
id_to_set: FxHashMap<HirId, u32>, id_to_set: FxHashMap<HirId, u32>,
cur: u32, cur: u32,
@ -52,8 +53,8 @@ pub struct BuilderPush {
pub changed: bool, pub changed: bool,
} }
impl<'a> LintLevelsBuilder<'a> { impl<'s> LintLevelsBuilder<'s> {
pub fn new(sess: &'a Session, warn_about_weird_lints: bool, store: &LintStore) -> Self { pub fn new(sess: &'s Session, warn_about_weird_lints: bool, store: &LintStore) -> Self {
let mut builder = LintLevelsBuilder { let mut builder = LintLevelsBuilder {
sess, sess,
sets: LintLevelSets::new(), sets: LintLevelSets::new(),
@ -233,20 +234,20 @@ impl<'a> LintLevelsBuilder<'a> {
let lint = builtin::RENAMED_AND_REMOVED_LINTS; let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (lvl, src) = let (lvl, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess); self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
let msg = format!(
"lint name `{}` is deprecated \
and may not have an effect in the future. \
Also `cfg_attr(cargo-clippy)` won't be necessary anymore",
name
);
struct_lint_level( struct_lint_level(
self.sess, self.sess,
lint, lint,
lvl, lvl,
src, src,
Some(li.span().into()), Some(li.span().into()),
&msg, |lint| {
) let msg = format!(
"lint name `{}` is deprecated \
and may not have an effect in the future. \
Also `cfg_attr(cargo-clippy)` won't be necessary anymore",
name
);
lint.build(&msg)
.span_suggestion( .span_suggestion(
li.span(), li.span(),
"change it to", "change it to",
@ -254,6 +255,8 @@ impl<'a> LintLevelsBuilder<'a> {
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )
.emit(); .emit();
},
);
let src = LintSource::Node( let src = LintSource::Node(
Symbol::intern(&new_lint_name), Symbol::intern(&new_lint_name),
@ -279,14 +282,14 @@ impl<'a> LintLevelsBuilder<'a> {
let lint = builtin::RENAMED_AND_REMOVED_LINTS; let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (level, src) = let (level, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess); self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
let mut err = struct_lint_level( struct_lint_level(
self.sess, self.sess,
lint, lint,
level, level,
src, src,
Some(li.span().into()), Some(li.span().into()),
&msg, |lint| {
); let mut err = lint.build(&msg);
if let Some(new_name) = renamed { if let Some(new_name) = renamed {
err.span_suggestion( err.span_suggestion(
li.span(), li.span(),
@ -296,21 +299,21 @@ impl<'a> LintLevelsBuilder<'a> {
); );
} }
err.emit(); err.emit();
},
);
} }
CheckLintNameResult::NoLint(suggestion) => { CheckLintNameResult::NoLint(suggestion) => {
let lint = builtin::UNKNOWN_LINTS; let lint = builtin::UNKNOWN_LINTS;
let (level, src) = let (level, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess); self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
let msg = format!("unknown lint: `{}`", name); struct_lint_level(
let mut db = struct_lint_level(
self.sess, self.sess,
lint, lint,
level, level,
src, src,
Some(li.span().into()), Some(li.span().into()),
&msg, |lint| {
); let mut db = lint.build(&format!("unknown lint: `{}`", name));
if let Some(suggestion) = suggestion { if let Some(suggestion) = suggestion {
db.span_suggestion( db.span_suggestion(
li.span(), li.span(),
@ -319,8 +322,9 @@ impl<'a> LintLevelsBuilder<'a> {
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
db.emit(); db.emit();
},
);
} }
} }
} }
@ -390,10 +394,10 @@ impl<'a> LintLevelsBuilder<'a> {
&self, &self,
lint: &'static Lint, lint: &'static Lint,
span: Option<MultiSpan>, span: Option<MultiSpan>,
msg: &str, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
) -> DiagnosticBuilder<'a> { ) {
let (level, src) = self.sets.get_lint_level(lint, self.cur, None, self.sess); 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 /// Registers the ID provided with the current set of lints stored in

View file

@ -22,19 +22,13 @@ impl EarlyLintPass for NonAsciiIdents {
if name_str.is_ascii() { if name_str.is_ascii() {
return; return;
} }
cx.struct_span_lint( cx.struct_span_lint(NON_ASCII_IDENTS, ident.span, |lint| {
NON_ASCII_IDENTS, lint.build("identifier contains non-ASCII characters").emit()
ident.span, });
"identifier contains non-ASCII characters",
)
.emit();
if !name_str.chars().all(GeneralSecurityProfile::identifier_allowed) { if !name_str.chars().all(GeneralSecurityProfile::identifier_allowed) {
cx.struct_span_lint( cx.struct_span_lint(UNCOMMON_CODEPOINTS, ident.span, |lint| {
UNCOMMON_CODEPOINTS, lint.build("identifier contains uncommon Unicode codepoints").emit()
ident.span, })
"identifier contains uncommon Unicode codepoints",
)
.emit();
} }
} }
} }

View file

@ -107,15 +107,17 @@ impl NonCamelCaseTypes {
let name = &ident.name.as_str(); let name = &ident.name.as_str();
if !is_camel_case(name) { if !is_camel_case(name) {
cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, |lint| {
let msg = format!("{} `{}` should have an upper camel case name", sort, name); let msg = format!("{} `{}` should have an upper camel case name", sort, name);
cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, &msg) lint.build(&msg)
.span_suggestion( .span_suggestion(
ident.span, ident.span,
"convert the identifier to upper camel case", "convert the identifier to upper camel case",
to_camel_case(name), to_camel_case(name),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
) )
.emit(); .emit()
})
} }
} }
} }
@ -223,11 +225,10 @@ impl NonSnakeCase {
let name = &ident.name.as_str(); let name = &ident.name.as_str();
if !is_snake_case(name) { if !is_snake_case(name) {
cx.struct_span_lint(NON_SNAKE_CASE, ident.span, |lint| {
let sc = NonSnakeCase::to_snake_case(name); let sc = NonSnakeCase::to_snake_case(name);
let msg = format!("{} `{}` should have a snake case name", sort, 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); 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 // 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. // name provided via the command line.
if !ident.span.is_dummy() { if !ident.span.is_dummy() {
@ -242,6 +243,7 @@ impl NonSnakeCase {
} }
err.emit(); err.emit();
});
} }
} }
} }
@ -386,12 +388,10 @@ declare_lint_pass!(NonUpperCaseGlobals => [NON_UPPER_CASE_GLOBALS]);
impl NonUpperCaseGlobals { impl NonUpperCaseGlobals {
fn check_upper_case(cx: &LateContext<'_, '_>, sort: &str, ident: &Ident) { fn check_upper_case(cx: &LateContext<'_, '_>, sort: &str, ident: &Ident) {
let name = &ident.name.as_str(); let name = &ident.name.as_str();
if name.chars().any(|c| c.is_lowercase()) { if name.chars().any(|c| c.is_lowercase()) {
cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, |lint| {
let uc = NonSnakeCase::to_snake_case(&name).to_uppercase(); let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
lint.build(&format!("{} `{}` should have an upper case name", sort, name))
let msg = format!("{} `{}` should have an upper case name", sort, name);
cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, &msg)
.span_suggestion( .span_suggestion(
ident.span, ident.span,
"convert the identifier to upper case", "convert the identifier to upper case",
@ -399,6 +399,7 @@ impl NonUpperCaseGlobals {
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
) )
.emit(); .emit();
})
} }
} }
} }

View file

@ -26,7 +26,8 @@ impl EarlyLintPass for RedundantSemicolon {
} else { } else {
"unnecessary trailing semicolon" "unnecessary trailing semicolon"
}; };
let mut err = cx.struct_span_lint(REDUNDANT_SEMICOLON, stmt.span, &msg); cx.struct_span_lint(REDUNDANT_SEMICOLON, stmt.span, |lint| {
let mut err = lint.build(&msg);
let suggest_msg = if multiple { let suggest_msg = if multiple {
"remove these semicolons" "remove these semicolons"
} else { } else {
@ -39,6 +40,7 @@ impl EarlyLintPass for RedundantSemicolon {
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
err.emit(); err.emit();
});
} }
} }
} }

View file

@ -67,6 +67,7 @@ fn lint_overflowing_range_endpoint<'a, 'tcx>(
) -> bool { ) -> bool {
// We only want to handle exclusive (`..`) ranges, // We only want to handle exclusive (`..`) ranges,
// which are represented as `ExprKind::Struct`. // which are represented as `ExprKind::Struct`.
let mut overwritten = false;
if let ExprKind::Struct(_, eps, _) = &parent_expr.kind { if let ExprKind::Struct(_, eps, _) = &parent_expr.kind {
if eps.len() != 2 { if eps.len() != 2 {
return false; return false;
@ -75,11 +76,8 @@ fn lint_overflowing_range_endpoint<'a, 'tcx>(
// (`..=`) instead only if it is the `end` that is // (`..=`) instead only if it is the `end` that is
// overflowing and only by 1. // overflowing and only by 1.
if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max { if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max {
let mut err = cx.struct_span_lint( cx.struct_span_lint(OVERFLOWING_LITERALS, parent_expr.span, |lint| {
OVERFLOWING_LITERALS, let mut err = lint.build(&format!("range endpoint is out of range for `{}`", ty));
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) { if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) {
use ast::{LitIntType, LitKind}; use ast::{LitIntType, LitKind};
// We need to preserve the literal's suffix, // We need to preserve the literal's suffix,
@ -98,12 +96,12 @@ fn lint_overflowing_range_endpoint<'a, 'tcx>(
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
err.emit(); err.emit();
return true; overwritten = true;
}
});
} }
} }
} overwritten
false
} }
// For `isize` & `usize`, be conservative with the warnings, so that the // For `isize` & `usize`, be conservative with the warnings, so that the
@ -153,6 +151,7 @@ fn report_bin_hex_error(
negative: bool, negative: bool,
) { ) {
let size = layout::Integer::from_attr(&cx.tcx, ty).size(); let size = layout::Integer::from_attr(&cx.tcx, ty).size();
cx.struct_span_lint(OVERFLOWING_LITERALS, expr.span, |lint| {
let (t, actually) = match ty { let (t, actually) = match ty {
attr::IntType::SignedInt(t) => { attr::IntType::SignedInt(t) => {
let actually = sign_extend(val, size) as i128; let actually = sign_extend(val, size) as i128;
@ -163,17 +162,14 @@ fn report_bin_hex_error(
(t.name_str(), actually.to_string()) (t.name_str(), actually.to_string())
} }
}; };
let mut err = cx.struct_span_lint( let mut err = lint.build(&format!("literal out of range for {}", t));
OVERFLOWING_LITERALS,
expr.span,
&format!("literal out of range for {}", t),
);
err.note(&format!( err.note(&format!(
"the literal `{}` (decimal `{}`) does not fit into \ "the literal `{}` (decimal `{}`) does not fit into \
an `{}` and will become `{}{}`", an `{}` and will become `{}{}`",
repr_str, val, t, actually, t 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(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') { if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
let (sans_suffix, _) = repr_str.split_at(pos); let (sans_suffix, _) = repr_str.split_at(pos);
err.span_suggestion( err.span_suggestion(
@ -186,8 +182,8 @@ fn report_bin_hex_error(
err.help(&format!("consider using `{}` instead", sugg_ty)); err.help(&format!("consider using `{}` instead", sugg_ty));
} }
} }
err.emit(); err.emit();
});
} }
// This function finds the next fitting type and generates a suggestion string. // 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( cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
OVERFLOWING_LITERALS, lint.build(&format!("literal out of range for `{}`", t.name_str())).emit()
e.span, });
&format!("literal out of range for `{}`", t.name_str()),
);
} }
} }
@ -298,18 +292,16 @@ fn lint_uint_literal<'a, 'tcx>(
match par_e.kind { match par_e.kind {
hir::ExprKind::Cast(..) => { hir::ExprKind::Cast(..) => {
if let ty::Char = cx.tables.expr_ty(par_e).kind { if let ty::Char = cx.tables.expr_ty(par_e).kind {
let mut err = cx.struct_span_lint( cx.struct_span_lint(OVERFLOWING_LITERALS, par_e.span, |lint| {
OVERFLOWING_LITERALS, lint.build("only `u8` can be cast into `char`")
par_e.span, .span_suggestion(
"only `u8` can be cast into `char`",
);
err.span_suggestion(
par_e.span, par_e.span,
&"use a `char` literal instead", &"use a `char` literal instead",
format!("'\\u{{{:X}}}'", lit_val), format!("'\\u{{{:X}}}'", lit_val),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); )
err.emit(); .emit();
});
return; 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); report_bin_hex_error(cx, e, attr::IntType::UnsignedInt(t), repr_str, lit_val, false);
return; return;
} }
cx.span_lint( cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
OVERFLOWING_LITERALS, lint.build(&format!("literal out of range for `{}`", t.name_str())).emit()
e.span, });
&format!("literal out of range for `{}`", t.name_str()),
);
} }
} }
@ -361,11 +351,9 @@ fn lint_literal<'a, 'tcx>(
_ => bug!(), _ => bug!(),
}; };
if is_infinite == Ok(true) { if is_infinite == Ok(true) {
cx.span_lint( cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
OVERFLOWING_LITERALS, lint.build(&format!("literal out of range for `{}`", t.name_str())).emit()
e.span, });
&format!("literal out of range for `{}`", t.name_str()),
);
} }
} }
_ => {} _ => {}
@ -383,11 +371,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
} }
hir::ExprKind::Binary(binop, ref l, ref r) => { hir::ExprKind::Binary(binop, ref l, ref r) => {
if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
cx.span_lint( cx.struct_span_lint(UNUSED_COMPARISONS, e.span, |lint| {
UNUSED_COMPARISONS, lint.build("comparison is useless due to type limits").emit()
e.span, });
"comparison is useless due to type limits",
);
} }
} }
hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
@ -883,11 +869,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
note: &str, note: &str,
help: Option<&str>, help: Option<&str>,
) { ) {
let mut diag = self.cx.struct_span_lint( self.cx.struct_span_lint(IMPROPER_CTYPES, sp, |lint| {
IMPROPER_CTYPES, let mut diag =
sp, lint.build(&format!("`extern` block uses type `{}`, which is not FFI-safe", ty));
&format!("`extern` block uses type `{}`, which is not FFI-safe", ty),
);
diag.span_label(sp, "not FFI-safe"); diag.span_label(sp, "not FFI-safe");
if let Some(help) = help { if let Some(help) = help {
diag.help(help); diag.help(help);
@ -899,6 +883,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
} }
} }
diag.emit(); diag.emit();
});
} }
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { 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 // We only warn if the largest variant is at least thrice as large as
// the second-largest. // the second-largest.
if largest > slargest * 3 && slargest > 0 { if largest > slargest * 3 && slargest > 0 {
cx.span_lint( cx.struct_span_lint(
VARIANT_SIZE_DIFFERENCES, VARIANT_SIZE_DIFFERENCES,
enum_definition.variants[largest_index].span, enum_definition.variants[largest_index].span,
&format!( |lint| {
lint.build(&format!(
"enum variant is more than three times \ "enum variant is more than three times \
larger ({} bytes) than the next largest", larger ({} bytes) than the next largest",
largest largest
), ))
.emit()
},
); );
} }
} }

View file

@ -104,16 +104,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
}; };
if let Some(must_use_op) = must_use_op { if let Some(must_use_op) = must_use_op {
cx.span_lint( cx.struct_span_lint(UNUSED_MUST_USE, expr.span, |lint| {
UNUSED_MUST_USE, lint.build(&format!("unused {} that must be used", must_use_op)).emit()
expr.span, });
&format!("unused {} that must be used", must_use_op),
);
op_warned = true; op_warned = true;
} }
if !(type_permits_lack_of_use || fn_warned || op_warned) { 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). // Returns whether an error has been emitted (and thus another does not need to be later).
@ -204,6 +202,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
} }
// Returns whether an error has been emitted (and thus another does not need to be later). // Returns whether an error has been emitted (and thus another does not need to be later).
// FIXME: Args desc_{pre,post}_path could be made lazy by taking Fn() -> &str, but this
// would make calling it a big awkward. Could also take String (so args are moved), but
// this would still require a copy into the format string, which would only be executed
// when needed.
fn check_must_use_def( fn check_must_use_def(
cx: &LateContext<'_, '_>, cx: &LateContext<'_, '_>,
def_id: DefId, def_id: DefId,
@ -213,18 +215,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
) -> bool { ) -> bool {
for attr in cx.tcx.get_attrs(def_id).iter() { for attr in cx.tcx.get_attrs(def_id).iter() {
if attr.check_name(sym::must_use) { if attr.check_name(sym::must_use) {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
let msg = format!( let msg = format!(
"unused {}`{}`{} that must be used", "unused {}`{}`{} that must be used",
descr_pre_path, descr_pre_path,
cx.tcx.def_path_str(def_id), cx.tcx.def_path_str(def_id),
descr_post_path descr_post_path
); );
let mut err = cx.struct_span_lint(UNUSED_MUST_USE, span, &msg); let mut err = lint.build(&msg);
// check for #[must_use = "..."] // check for #[must_use = "..."]
if let Some(note) = attr.value_str() { if let Some(note) = attr.value_str() {
err.note(&note.as_str()); err.note(&note.as_str());
} }
err.emit(); err.emit();
});
return true; return true;
} }
} }
@ -245,7 +249,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathStatements {
fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) { fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) {
if let hir::StmtKind::Semi(ref expr) = s.kind { if let hir::StmtKind::Semi(ref expr) = s.kind {
if let hir::ExprKind::Path(_) = expr.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,9 +292,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
if !attr::is_used(attr) { if !attr::is_used(attr) {
debug!("emitting warning for: {:?}", 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? // Is it a builtin attribute that must be used at the crate level?
if attr_info.map_or(false, |(_, ty, ..)| ty == &AttributeType::CrateLevel) { if attr_info.map_or(false, |(_, ty, ..)| ty == &AttributeType::CrateLevel) {
cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| {
let msg = match attr.style { let msg = match attr.style {
ast::AttrStyle::Outer => { ast::AttrStyle::Outer => {
"crate-level attribute should be an inner attribute: add an exclamation \ "crate-level attribute should be an inner attribute: add an exclamation \
@ -296,7 +305,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
} }
ast::AttrStyle::Inner => "crate-level attribute should be in the root module", ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
}; };
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg); lint.build(msg).emit()
});
} }
} else { } else {
debug!("Attr was used: {:?}", attr); debug!("Attr was used: {:?}", attr);
@ -406,8 +416,9 @@ impl UnusedParens {
msg: &str, msg: &str,
keep_space: (bool, bool), keep_space: (bool, bool),
) { ) {
cx.struct_span_lint(UNUSED_PARENS, span, |lint| {
let span_msg = format!("unnecessary parentheses around {}", msg); let span_msg = format!("unnecessary parentheses around {}", msg);
let mut err = cx.struct_span_lint(UNUSED_PARENS, span, &span_msg); let mut err = lint.build(&span_msg);
let mut ate_left_paren = false; let mut ate_left_paren = false;
let mut ate_right_paren = false; let mut ate_right_paren = false;
let parens_removed = pattern.trim_matches(|c| match c { let parens_removed = pattern.trim_matches(|c| match c {
@ -452,6 +463,7 @@ impl UnusedParens {
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
err.emit(); err.emit();
});
} }
} }
@ -631,8 +643,9 @@ impl UnusedImportBraces {
ast::UseTreeKind::Nested(_) => return, ast::UseTreeKind::Nested(_) => return,
}; };
let msg = format!("braces around {} is unnecessary", node_name); cx.struct_span_lint(UNUSED_IMPORT_BRACES, item.span, |lint| {
cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg); lint.build(&format!("braces around {} is unnecessary", node_name)).emit()
});
} }
} }
} }
@ -662,6 +675,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
for adj in cx.tables.expr_adjustments(e) { for adj in cx.tables.expr_adjustments(e) {
if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind { if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
cx.struct_span_lint(UNUSED_ALLOCATION, e.span, |lint| {
let msg = match m { let msg = match m {
adjustment::AutoBorrowMutability::Not => { adjustment::AutoBorrowMutability::Not => {
"unnecessary allocation, use `&` instead" "unnecessary allocation, use `&` instead"
@ -670,7 +684,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
"unnecessary allocation, use `&mut` instead" "unnecessary allocation, use `&mut` instead"
} }
}; };
cx.span_lint(UNUSED_ALLOCATION, e.span, msg); lint.build(msg).emit()
});
} }
} }
} }

View file

@ -319,18 +319,20 @@ fn do_mir_borrowck<'a, 'tcx>(
}; };
// Span and message don't matter; we overwrite them below anyway // 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, MUTABLE_BORROW_RESERVATION_CONFLICT,
lint_root, lint_root,
DUMMY_SP, DUMMY_SP,
"", |lint| {
); let mut diag = lint.build("");
diag.message = initial_diag.styled_message().clone(); diag.message = initial_diag.styled_message().clone();
diag.span = initial_diag.span.clone(); diag.span = initial_diag.span.clone();
initial_diag.cancel();
diag.buffer(&mut mbcx.errors_buffer); diag.buffer(&mut mbcx.errors_buffer);
},
);
initial_diag.cancel();
} }
// For each non-user used mutable variable, check if it's been assigned from // For each non-user used mutable variable, check if it's been assigned from
@ -376,13 +378,9 @@ fn do_mir_borrowck<'a, 'tcx>(
continue; continue;
} }
tcx.struct_span_lint_hir(UNUSED_MUT, lint_root, span, |lint| {
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span); let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
tcx.struct_span_lint_hir( lint.build("variable does not need to be mutable")
UNUSED_MUT,
lint_root,
span,
"variable does not need to be mutable",
)
.span_suggestion_short( .span_suggestion_short(
mut_span, mut_span,
"remove this `mut`", "remove this `mut`",
@ -390,6 +388,7 @@ fn do_mir_borrowck<'a, 'tcx>(
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )
.emit(); .emit();
})
} }
// Buffer any move errors that we collected and de-duplicated. // Buffer any move errors that we collected and de-duplicated.

View file

@ -209,12 +209,11 @@ fn validate_and_turn_into_const<'tcx>(
val.map_err(|error| { val.map_err(|error| {
let err = error_to_const_error(&ecx, error); let err = error_to_const_error(&ecx, error);
match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") { match err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| {
Ok(mut diag) => {
diag.note(note_on_undefined_behavior_error()); diag.note(note_on_undefined_behavior_error());
diag.emit(); diag.emit();
ErrorHandled::Reported }) {
} Ok(_) => ErrorHandled::Reported,
Err(err) => err, Err(err) => err,
} }
}) })

View file

@ -326,12 +326,15 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
// to read enum discriminants in order to find references in enum variant fields. // to read enum discriminants in order to find references in enum variant fields.
if let err_unsup!(ValidationFailure(_)) = error.kind { if let err_unsup!(ValidationFailure(_)) = error.kind {
let err = crate::const_eval::error_to_const_error(&ecx, error); 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") { match err.struct_error(
Ok(mut diag) => { ecx.tcx,
"it is undefined behavior to use this value",
|mut diag| {
diag.note(crate::const_eval::note_on_undefined_behavior_error()); diag.note(crate::const_eval::note_on_undefined_behavior_error());
diag.emit(); diag.emit();
} },
Err(ErrorHandled::TooGeneric) | Err(ErrorHandled::Reported) => {} ) {
Ok(()) | Err(ErrorHandled::TooGeneric) | Err(ErrorHandled::Reported) => {}
} }
} }
} }

View file

@ -516,6 +516,7 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: DefId) {
.as_local_hir_id(def_id) .as_local_hir_id(def_id)
.unwrap_or_else(|| bug!("checking unsafety for non-local def id {:?}", def_id)); .unwrap_or_else(|| bug!("checking unsafety for non-local def id {:?}", def_id));
tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| {
// FIXME: when we make this a hard error, this should have its // FIXME: when we make this a hard error, this should have its
// own error code. // own error code.
let message = if tcx.generics_of(def_id).own_requires_monomorphization() { let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
@ -527,7 +528,8 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: DefId) {
does not derive Copy (error E0133)" does not derive Copy (error E0133)"
.to_string() .to_string()
}; };
tcx.lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), &message); lint.build(&message).emit()
});
} }
/// Returns the `HirId` for an enclosing scope that is also `unsafe`. /// Returns the `HirId` for an enclosing scope that is also `unsafe`.
@ -558,8 +560,9 @@ fn is_enclosed(
fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) { fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) {
let span = tcx.sess.source_map().def_span(tcx.hir().span(id)); let span = tcx.sess.source_map().def_span(tcx.hir().span(id));
tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
let msg = "unnecessary `unsafe` block"; let msg = "unnecessary `unsafe` block";
let mut db = tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, msg); let mut db = lint.build(msg);
db.span_label(span, msg); db.span_label(span, msg);
if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) { if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
db.span_label( db.span_label(
@ -568,6 +571,7 @@ fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id
); );
} }
db.emit(); db.emit();
});
} }
fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> { fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
@ -619,13 +623,15 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) {
SAFE_PACKED_BORROWS, SAFE_PACKED_BORROWS,
lint_hir_id, lint_hir_id,
source_info.span, source_info.span,
&format!( |lint| {
lint.build(&format!(
"{} is unsafe and requires unsafe function or block (error E0133)", "{} is unsafe and requires unsafe function or block (error E0133)",
description description
), ))
)
.note(&details.as_str()) .note(&details.as_str())
.emit(); .emit()
},
)
} }
} }
} }

View file

@ -556,12 +556,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size)); let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
if r_bits.map_or(false, |b| b >= left_bits as u128) { if r_bits.map_or(false, |b| b >= left_bits as u128) {
let lint_root = self.lint_root(source_info)?; let lint_root = self.lint_root(source_info)?;
let dir = if op == BinOp::Shr { "right" } else { "left" }; self.tcx.struct_span_lint_hir(
self.tcx.lint_hir(
::rustc::lint::builtin::EXCEEDING_BITSHIFTS, ::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
lint_root, lint_root,
source_info.span, source_info.span,
&format!("attempt to shift {} with overflow", dir), |lint| {
let dir = if op == BinOp::Shr { "right" } else { "left" };
lint.build(&format!("attempt to shift {} with overflow", dir)).emit()
},
); );
return None; return None;
} }
@ -912,23 +914,35 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
.hir() .hir()
.as_local_hir_id(self.source.def_id()) .as_local_hir_id(self.source.def_id())
.expect("some part of a failing const eval must be local"); .expect("some part of a failing const eval must be local");
self.tcx.struct_span_lint_hir(
::rustc::lint::builtin::CONST_ERR,
hir_id,
span,
|lint| {
let msg = match msg { let msg = match msg {
PanicInfo::Overflow(_) PanicInfo::Overflow(_)
| PanicInfo::OverflowNeg | PanicInfo::OverflowNeg
| PanicInfo::DivisionByZero | PanicInfo::DivisionByZero
| PanicInfo::RemainderByZero => msg.description().to_owned(), | PanicInfo::RemainderByZero => msg.description().to_owned(),
PanicInfo::BoundsCheck { ref len, ref index } => { PanicInfo::BoundsCheck { ref len, ref index } => {
let len = let len = self
self.eval_operand(len, source_info).expect("len must be const"); .eval_operand(len, source_info)
.expect("len must be const");
let len = match self.ecx.read_scalar(len) { let len = match self.ecx.read_scalar(len) {
Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data, Ok(ScalarMaybeUndef::Scalar(Scalar::Raw {
data,
..
})) => data,
other => bug!("const len not primitive: {:?}", other), other => bug!("const len not primitive: {:?}", other),
}; };
let index = self let index = self
.eval_operand(index, source_info) .eval_operand(index, source_info)
.expect("index must be const"); .expect("index must be const");
let index = match self.ecx.read_scalar(index) { let index = match self.ecx.read_scalar(index) {
Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data, Ok(ScalarMaybeUndef::Scalar(Scalar::Raw {
data,
..
})) => data,
other => bug!("const index not primitive: {:?}", other), other => bug!("const index not primitive: {:?}", other),
}; };
format!( format!(
@ -940,7 +954,9 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
// Need proper const propagator for these // Need proper const propagator for these
_ => return, _ => return,
}; };
self.tcx.lint_hir(::rustc::lint::builtin::CONST_ERR, hir_id, span, &msg); lint.build(&msg).emit()
},
);
} else { } else {
if self.should_const_prop(value) { if self.should_const_prop(value) {
if let ScalarMaybeUndef::Scalar(scalar) = value_const { if let ScalarMaybeUndef::Scalar(scalar) = value_const {

View file

@ -2235,12 +2235,12 @@ fn lint_overlapping_patterns<'tcx>(
overlaps: Vec<IntRange<'tcx>>, overlaps: Vec<IntRange<'tcx>>,
) { ) {
if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) { 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, lint::builtin::OVERLAPPING_PATTERNS,
hir_id, hir_id,
ctor_range.span, 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"); err.span_label(ctor_range.span, "overlapping patterns");
for int_range in overlaps { for int_range in overlaps {
// Use the real type for user display of the ranges: // Use the real type for user display of the ranges:
@ -2253,6 +2253,8 @@ fn lint_overlapping_patterns<'tcx>(
); );
} }
err.emit(); err.emit();
},
);
} }
} }

View file

@ -286,18 +286,17 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa
variant.ident == ident && variant.ctor_kind == CtorKind::Const variant.ident == ident && variant.ctor_kind == CtorKind::Const
}) })
{ {
let ty_path = cx.tcx.def_path_str(edef.did); cx.tcx.struct_span_lint_hir(
cx.tcx
.struct_span_lint_hir(
BINDINGS_WITH_VARIANT_NAME, BINDINGS_WITH_VARIANT_NAME,
p.hir_id, p.hir_id,
p.span, p.span,
&format!( |lint| {
let ty_path = cx.tcx.def_path_str(edef.did);
lint.build(&format!(
"pattern binding `{}` is named the same as one \ "pattern binding `{}` is named the same as one \
of the variants of the type `{}`", of the variants of the type `{}`",
ident, ty_path ident, ty_path
), ))
)
.code(error_code!(E0170)) .code(error_code!(E0170))
.span_suggestion( .span_suggestion(
p.span, p.span,
@ -306,6 +305,8 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )
.emit(); .emit();
},
)
} }
} }
} }
@ -325,22 +326,26 @@ fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
} }
fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) { fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
let mut err = tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern"); tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| {
let mut err = lint.build("unreachable pattern");
if let Some(catchall) = catchall { if let Some(catchall) = catchall {
// We had a catchall pattern, hint at that. // We had a catchall pattern, hint at that.
err.span_label(span, "unreachable pattern"); err.span_label(span, "unreachable pattern");
err.span_label(catchall, "matches any value"); err.span_label(catchall, "matches any value");
} }
err.emit(); err.emit();
});
} }
fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) { fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) {
tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| {
let msg = match source { let msg = match source {
hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern", hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern", hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
_ => bug!(), _ => bug!(),
}; };
tcx.lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, msg); lint.build(msg).emit()
});
} }
/// Check for unreachable patterns. /// Check for unreachable patterns.

View file

@ -109,11 +109,14 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
} }
}; };
let path = self.tcx().def_path_str(adt_def.did); let path = self.tcx().def_path_str(adt_def.did);
let msg = format!(
let make_msg = || -> String {
format!(
"to use a constant of type `{}` in a pattern, \ "to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`", `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path, path, path,
); )
};
// double-check there even *is* a semantic `PartialEq` to dispatch to. // 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 { if !ty_is_partial_eq {
// span_fatal avoids ICE from resolution of non-existent method (rare case). // 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 { } else {
self.tcx().lint_hir( self.tcx().struct_span_lint_hir(
lint::builtin::INDIRECT_STRUCTURAL_MATCH, lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id, self.id,
self.span, 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 { let kind = match cv.ty.kind {
ty::Float(_) => { ty::Float(_) => {
tcx.lint_hir( tcx.struct_span_lint_hir(
::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id, id,
span, 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 } PatKind::Constant { value: cv }
} }

View file

@ -124,12 +124,8 @@ fn check_fn_for_unconditional_recursion<'tcx>(
if !reached_exit_without_self_call && !self_call_locations.is_empty() { if !reached_exit_without_self_call && !self_call_locations.is_empty() {
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); 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 sp = tcx.sess.source_map().def_span(tcx.hir().span(hir_id));
let mut db = tcx.struct_span_lint_hir( tcx.struct_span_lint_hir(UNCONDITIONAL_RECURSION, hir_id, sp, |lint| {
UNCONDITIONAL_RECURSION, let mut db = lint.build("function cannot return without recursing");
hir_id,
sp,
"function cannot return without recursing",
);
db.span_label(sp, "cannot return without recursing"); db.span_label(sp, "cannot return without recursing");
// offer some help to the programmer. // offer some help to the programmer.
for location in &self_call_locations { for location in &self_call_locations {
@ -137,5 +133,6 @@ fn check_fn_for_unconditional_recursion<'tcx>(
} }
db.help("a `loop` may express intention better if this is on purpose"); db.help("a `loop` may express intention better if this is on purpose");
db.emit(); db.emit();
});
} }
} }

View file

@ -315,10 +315,11 @@ impl<'a> StripUnconfigured<'a> {
validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg); 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()) { match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
Ok(r) => return Some(r), Ok(r) => return Some(r),
Err(mut e) => e Err(mut e) => {
.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
.note(CFG_ATTR_NOTE_REF) .note(CFG_ATTR_NOTE_REF)
.emit(), .emit();
}
} }
} }
_ => self.error_malformed_cfg_attr_missing(attr.span), _ => self.error_malformed_cfg_attr_missing(attr.span),

View file

@ -69,7 +69,7 @@ pub(crate) fn emit_unescape_error(
format!("\"{}\"", lit), format!("\"{}\"", lit),
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )
.emit() .emit();
} }
EscapeError::EscapeOnlyChar => { EscapeError::EscapeOnlyChar => {
let (c, _span) = last_char(); let (c, _span) = last_char();

View file

@ -149,7 +149,7 @@ impl<'a> Parser<'a> {
source files. Outer attributes, like `#[test]`, annotate the \ source files. Outer attributes, like `#[test]`, annotate the \
item following them.", item following them.",
) )
.emit() .emit();
} }
} }
@ -239,7 +239,7 @@ impl<'a> Parser<'a> {
(`1u8`, `1.0f32`, etc.), use an unsuffixed version \ (`1u8`, `1.0f32`, etc.), use an unsuffixed version \
(`1`, `1.0`, etc.)", (`1`, `1.0`, etc.)",
) )
.emit() .emit();
} }
Ok(lit) Ok(lit)

View file

@ -1014,7 +1014,7 @@ impl<'a> Parser<'a> {
String::new(), String::new(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )
.emit() .emit();
} }
} }

View file

@ -1407,6 +1407,8 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, sess: &Pa
*sess.reached_eof.borrow_mut() |= *sess.reached_eof.borrow_mut() |=
unclosed_delims.iter().any(|unmatched_delim| unmatched_delim.found_delim.is_none()); unclosed_delims.iter().any(|unmatched_delim| unmatched_delim.found_delim.is_none());
for unmatched in unclosed_delims.drain(..) { 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();
});
} }
} }

View file

@ -572,7 +572,7 @@ impl<'a> Parser<'a> {
self.struct_span_err(span, problem) self.struct_span_err(span, problem)
.span_suggestion(span, suggestion, fix, Applicability::MachineApplicable) .span_suggestion(span, suggestion, fix, Applicability::MachineApplicable)
.note("`mut` may be followed by `variable` and `variable @ pattern`") .note("`mut` may be followed by `variable` and `variable @ pattern`")
.emit() .emit();
} }
/// Eat any extraneous `mut`s and error + recover if we ate any. /// Eat any extraneous `mut`s and error + recover if we ate any.

View file

@ -27,7 +27,11 @@ pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
_ => { _ => {
if let MacArgs::Eq(..) = attr.get_normal_item().args { if let MacArgs::Eq(..) = attr.get_normal_item().args {
// All key-value attributes are restricted to meta-item syntax. // 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();
}
} }
} }

View file

@ -92,14 +92,9 @@ impl CheckAttrVisitor<'tcx> {
| Target::Method(MethodKind::Trait { body: true }) | Target::Method(MethodKind::Trait { body: true })
| Target::Method(MethodKind::Inherent) => true, | Target::Method(MethodKind::Inherent) => true,
Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
self.tcx self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
.struct_span_lint_hir( lint.build("`#[inline]` is ignored on function prototypes").emit()
UNUSED_ATTRIBUTES, });
hir_id,
attr.span,
"`#[inline]` is ignored on function prototypes",
)
.emit();
true true
} }
// FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
@ -107,13 +102,8 @@ impl CheckAttrVisitor<'tcx> {
// accidentally, to to be compatible with crates depending on them, we can't throw an // accidentally, to to be compatible with crates depending on them, we can't throw an
// error here. // error here.
Target::AssocConst => { Target::AssocConst => {
self.tcx self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
.struct_span_lint_hir( lint.build("`#[inline]` is ignored on constants")
UNUSED_ATTRIBUTES,
hir_id,
attr.span,
"`#[inline]` is ignored on constants",
)
.warn( .warn(
"this was previously accepted by the compiler but is \ "this was previously accepted by the compiler but is \
being phased out; it will become a hard error in \ being phased out; it will become a hard error in \
@ -124,6 +114,7 @@ impl CheckAttrVisitor<'tcx> {
for more information", for more information",
) )
.emit(); .emit();
});
true true
} }
_ => { _ => {
@ -331,15 +322,16 @@ impl CheckAttrVisitor<'tcx> {
|| (is_simd && is_c) || (is_simd && is_c)
|| (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item))) || (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item)))
{ {
self.tcx self.tcx.struct_span_lint_hir(
.struct_span_lint_hir(
CONFLICTING_REPR_HINTS, CONFLICTING_REPR_HINTS,
hir_id, hir_id,
hint_spans.collect::<Vec<Span>>(), hint_spans.collect::<Vec<Span>>(),
"conflicting representation hints", |lint| {
) lint.build("conflicting representation hints")
.code(rustc_errors::error_code!(E0566)) .code(rustc_errors::error_code!(E0566))
.emit(); .emit();
},
);
} }
} }

View file

@ -554,12 +554,9 @@ impl DeadVisitor<'tcx> {
participle: &str, participle: &str,
) { ) {
if !name.as_str().starts_with("_") { if !name.as_str().starts_with("_") {
self.tcx.lint_hir( self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| {
lint::builtin::DEAD_CODE, lint.build(&format!("{} is never {}: `{}`", node_type, participle, name)).emit()
id, });
span,
&format!("{} is never {}: `{}`", node_type, participle, name),
);
} }
} }
} }

View file

@ -1521,29 +1521,30 @@ impl<'tcx> Liveness<'_, 'tcx> {
if ln == self.s.exit_ln { false } else { self.assigned_on_exit(ln, var).is_some() }; if ln == self.s.exit_ln { false } else { self.assigned_on_exit(ln, var).is_some() };
if is_assigned { if is_assigned {
self.ir self.ir.tcx.struct_span_lint_hir(
.tcx
.struct_span_lint_hir(
lint::builtin::UNUSED_VARIABLES, lint::builtin::UNUSED_VARIABLES,
hir_id, hir_id,
spans, spans,
&format!("variable `{}` is assigned to, but never used", name), |lint| {
) lint.build(&format!("variable `{}` is assigned to, but never used", name))
.note(&format!("consider using `_{}` instead", name)) .note(&format!("consider using `_{}` instead", name))
.emit(); .emit();
},
)
} else { } else {
let mut err = self.ir.tcx.struct_span_lint_hir( self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_VARIABLES, lint::builtin::UNUSED_VARIABLES,
hir_id, hir_id,
spans.clone(), spans.clone(),
&format!("unused variable: `{}`", name), |lint| {
); let mut err = lint.build(&format!("unused variable: `{}`", name));
if self.ir.variable_is_shorthand(var) { if self.ir.variable_is_shorthand(var) {
if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) { if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
// Handle `ref` and `ref mut`. // Handle `ref` and `ref mut`.
let spans = let spans = spans
spans.iter().map(|_span| (pat.span, format!("{}: _", name))).collect(); .iter()
.map(|_span| (pat.span, format!("{}: _", name)))
.collect();
err.multipart_suggestion( err.multipart_suggestion(
"try ignoring the field", "try ignoring the field",
@ -1558,8 +1559,9 @@ impl<'tcx> Liveness<'_, 'tcx> {
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
err.emit() err.emit()
},
);
} }
} }
} }
@ -1573,27 +1575,27 @@ impl<'tcx> Liveness<'_, 'tcx> {
fn report_dead_assign(&self, hir_id: HirId, spans: Vec<Span>, var: Variable, is_param: bool) { fn report_dead_assign(&self, hir_id: HirId, spans: Vec<Span>, var: Variable, is_param: bool) {
if let Some(name) = self.should_warn(var) { if let Some(name) = self.should_warn(var) {
if is_param { if is_param {
self.ir self.ir.tcx.struct_span_lint_hir(
.tcx
.struct_span_lint_hir(
lint::builtin::UNUSED_ASSIGNMENTS, lint::builtin::UNUSED_ASSIGNMENTS,
hir_id, hir_id,
spans, spans,
&format!("value passed to `{}` is never read", name), |lint| {
) lint.build(&format!("value passed to `{}` is never read", name))
.help("maybe it is overwritten before being read?") .help("maybe it is overwritten before being read?")
.emit(); .emit();
},
)
} else { } else {
self.ir self.ir.tcx.struct_span_lint_hir(
.tcx
.struct_span_lint_hir(
lint::builtin::UNUSED_ASSIGNMENTS, lint::builtin::UNUSED_ASSIGNMENTS,
hir_id, hir_id,
spans, spans,
&format!("value assigned to `{}` is never read", name), |lint| {
) lint.build(&format!("value assigned to `{}` is never read", name))
.help("maybe it is overwritten before being read?") .help("maybe it is overwritten before being read?")
.emit(); .emit();
},
)
} }
} }
} }

View file

@ -604,16 +604,14 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
} }
fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) { fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) {
tcx.lint_hir( tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
lint::builtin::STABLE_FEATURES, lint.build(&format!(
hir::CRATE_HIR_ID,
span,
&format!(
"the feature `{}` has been stable since {} and no longer requires \ "the feature `{}` has been stable since {} and no longer requires \
an attribute to enable", an attribute to enable",
feature, since feature, since
), ))
); .emit();
});
} }
fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) { fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) {

View file

@ -1781,17 +1781,20 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> {
fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
if self.leaks_private_dep(def_id) { if self.leaks_private_dep(def_id) {
self.tcx.lint_hir( self.tcx.struct_span_lint_hir(
lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES, lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
self.item_id, self.item_id,
self.span, self.span,
&format!( |lint| {
lint.build(&format!(
"{} `{}` from private dependency '{}' in public \ "{} `{}` from private dependency '{}' in public \
interface", interface",
kind, kind,
descr, descr,
self.tcx.crate_name(def_id.krate) self.tcx.crate_name(def_id.krate)
), ))
.emit()
},
); );
} }
@ -1802,23 +1805,23 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> {
let (vis, vis_span, vis_descr) = def_id_visibility(self.tcx, def_id); let (vis, vis_span, vis_descr) = def_id_visibility(self.tcx, def_id);
if !vis.is_at_least(self.required_visibility, self.tcx) { if !vis.is_at_least(self.required_visibility, self.tcx) {
let msg = format!("{} {} `{}` in public interface", vis_descr, kind, descr); let make_msg = || format!("{} {} `{}` in public interface", vis_descr, kind, descr);
if self.has_pub_restricted || self.has_old_errors || self.in_assoc_ty { if self.has_pub_restricted || self.has_old_errors || self.in_assoc_ty {
let mut err = if kind == "trait" { let mut err = if kind == "trait" {
struct_span_err!(self.tcx.sess, self.span, E0445, "{}", msg) struct_span_err!(self.tcx.sess, self.span, E0445, "{}", make_msg())
} else { } else {
struct_span_err!(self.tcx.sess, self.span, E0446, "{}", msg) struct_span_err!(self.tcx.sess, self.span, E0446, "{}", make_msg())
}; };
err.span_label(self.span, format!("can't leak {} {}", vis_descr, kind)); err.span_label(self.span, format!("can't leak {} {}", vis_descr, kind));
err.span_label(vis_span, format!("`{}` declared as {}", descr, vis_descr)); err.span_label(vis_span, format!("`{}` declared as {}", descr, vis_descr));
err.emit(); err.emit();
} else { } else {
let err_code = if kind == "trait" { "E0445" } else { "E0446" }; let err_code = if kind == "trait" { "E0445" } else { "E0446" };
self.tcx.lint_hir( self.tcx.struct_span_lint_hir(
lint::builtin::PRIVATE_IN_PUBLIC, lint::builtin::PRIVATE_IN_PUBLIC,
hir_id, hir_id,
self.span, self.span,
&format!("{} (error {})", msg, err_code), |lint| lint.build(&format!("{} (error {})", make_msg(), err_code)).emit(),
); );
} }
} }

View file

@ -1575,13 +1575,15 @@ 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, lint::builtin::SINGLE_USE_LIFETIMES,
id, id,
span, 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 { if span == lifetime.span {
// spans are the same for in-band lifetime declarations // spans are the same for in-band lifetime declarations
err.span_label(span, "this lifetime is only used here"); err.span_label(span, "this lifetime is only used here");
@ -1589,8 +1591,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
err.span_label(span, "this lifetime..."); err.span_label(span, "this lifetime...");
err.span_label(lifetime.span, "...is used only here"); err.span_label(lifetime.span, "...is used only here");
} }
self.suggest_eliding_single_use_lifetime(&mut err, def_id, lifetime); self.suggest_eliding_single_use_lifetime(
&mut err, def_id, lifetime,
);
err.emit(); err.emit();
},
);
} }
} }
Some(LifetimeUseSet::Many) => { Some(LifetimeUseSet::Many) => {
@ -1610,15 +1616,19 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
_ => None, _ => None,
} { } {
debug!("id ={:?} span = {:?} name = {:?}", id, span, name); 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, lint::builtin::UNUSED_LIFETIMES,
id, id,
span, span,
&format!("lifetime parameter `{}` never used", name), |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(parent_def_id) = self.tcx.parent(def_id) {
if let Some(generics) = self.tcx.hir().get_generics(parent_def_id) { if let Some(generics) =
let unused_lt_span = self.lifetime_deletion_span(name, 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 { if let Some(span) = unused_lt_span {
err.span_suggestion( err.span_suggestion(
span, span,
@ -1630,6 +1640,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
} }
} }
err.emit(); err.emit();
},
);
} }
} }
} }

View file

@ -331,11 +331,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
} else { } else {
let mut multispan = MultiSpan::from_span(span); let mut multispan = MultiSpan::from_span(span);
multispan.push_span_label(span_late, note.to_string()); multispan.push_span_label(span_late, note.to_string());
tcx.lint_hir( tcx.struct_span_lint_hir(
lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
args.args[0].id(), args.args[0].id(),
multispan, multispan,
msg, |lint| lint.build(msg).emit(),
); );
reported_late_bound_region_err = Some(false); reported_late_bound_region_err = Some(false);
} }
@ -2216,13 +2216,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
tcx.check_stability(item.def_id, Some(hir_ref_id), span); tcx.check_stability(item.def_id, Some(hir_ref_id), span);
if let Some(variant_def_id) = variant_resolution { if let Some(variant_def_id) = variant_resolution {
let mut err = tcx.struct_span_lint_hir( tcx.struct_span_lint_hir(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| {
AMBIGUOUS_ASSOCIATED_ITEMS, let mut err = lint.build("ambiguous associated item");
hir_ref_id,
span,
"ambiguous associated item",
);
let mut could_refer_to = |kind: DefKind, def_id, also| { let mut could_refer_to = |kind: DefKind, def_id, also| {
let note_msg = format!( let note_msg = format!(
"`{}` could{} refer to the {} defined here", "`{}` could{} refer to the {} defined here",
@ -2232,6 +2227,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
); );
err.span_note(tcx.def_span(def_id), &note_msg); err.span_note(tcx.def_span(def_id), &note_msg);
}; };
could_refer_to(DefKind::Variant, variant_def_id, ""); could_refer_to(DefKind::Variant, variant_def_id, "");
could_refer_to(kind, item.def_id, " also"); could_refer_to(kind, item.def_id, " also");
@ -2240,10 +2236,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
"use fully-qualified syntax", "use fully-qualified syntax",
format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident),
Applicability::MachineApplicable, Applicability::MachineApplicable,
) );
.emit();
}
err.emit();
});
}
Ok((ty, kind, item.def_id)) Ok((ty, kind, item.def_id))
} }

View file

@ -468,23 +468,20 @@ impl<'a, 'tcx> CastCheck<'tcx> {
} else { } else {
("", lint::builtin::TRIVIAL_CASTS) ("", lint::builtin::TRIVIAL_CASTS)
}; };
let mut err = fcx.tcx.struct_span_lint_hir( fcx.tcx.struct_span_lint_hir(lint, self.expr.hir_id, self.span, |err| {
lint, err.build(&format!(
self.expr.hir_id,
self.span,
&format!(
"trivial {}cast: `{}` as `{}`", "trivial {}cast: `{}` as `{}`",
adjective, adjective,
fcx.ty_to_string(t_expr), fcx.ty_to_string(t_expr),
fcx.ty_to_string(t_cast) fcx.ty_to_string(t_cast)
), ))
); .help(&format!(
err.help(&format!(
"cast can be replaced by coercion; this might \ "cast can be replaced by coercion; this might \
require {}a temporary variable", require {}a temporary variable",
type_asc_or type_asc_or
)); ))
err.emit(); .emit();
});
} }
pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) { pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) {

View file

@ -382,11 +382,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) )
.emit(); .emit();
} else { } else {
self.tcx.lint_hir( self.tcx.struct_span_lint_hir(
lint::builtin::TYVAR_BEHIND_RAW_POINTER, lint::builtin::TYVAR_BEHIND_RAW_POINTER,
scope_expr_id, scope_expr_id,
span, span,
"type annotations needed", |lint| lint.build("type annotations needed").emit(),
); );
} }
} else { } else {
@ -1280,13 +1280,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
stable_pick: &Pick<'_>, stable_pick: &Pick<'_>,
unstable_candidates: &[(&Candidate<'tcx>, Symbol)], 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, lint::builtin::UNSTABLE_NAME_COLLISIONS,
self.fcx.body_id, self.fcx.body_id,
self.span, self.span,
|lint| {
let mut diag = lint.build(
"a method with this name may be added to the standard library in the future", "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` // FIXME: This should be a `span_suggestion` instead of `help`
// However `self.span` only // However `self.span` only
// highlights the method name, so we can't use it. Also consider reusing the code from // highlights the method name, so we can't use it. Also consider reusing the code from
@ -1307,6 +1308,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
} }
diag.emit(); diag.emit();
},
);
} }
fn select_trait_candidate( fn select_trait_candidate(

View file

@ -2895,15 +2895,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
let msg = format!("unreachable {}", kind); let msg = format!("unreachable {}", kind);
self.tcx() lint.build(&msg)
.struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, &msg)
.span_label(span, &msg) .span_label(span, &msg)
.span_label( .span_label(
orig_span, orig_span,
custom_note.unwrap_or("any code following this expression is unreachable"), custom_note
.unwrap_or("any code following this expression is unreachable"),
) )
.emit(); .emit();
})
} }
} }
} }

View file

@ -55,12 +55,14 @@ impl CheckVisitor<'tcx> {
return; return;
} }
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) { let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
format!("unused import: `{}`", snippet) format!("unused import: `{}`", snippet)
} else { } else {
"unused import".to_owned() "unused import".to_owned()
}; };
self.tcx.lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, &msg); lint.build(&msg).emit();
});
} }
} }
@ -121,8 +123,7 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
// We do this in any edition. // We do this in any edition.
if extern_crate.warn_if_unused { if extern_crate.warn_if_unused {
if let Some(&span) = unused_extern_crates.get(&extern_crate.def_id) { if let Some(&span) = unused_extern_crates.get(&extern_crate.def_id) {
let msg = "unused extern crate"; tcx.struct_span_lint_hir(lint, id, span, |lint| {
// Removal suggestion span needs to include attributes (Issue #54400) // Removal suggestion span needs to include attributes (Issue #54400)
let span_with_attrs = tcx let span_with_attrs = tcx
.get_attrs(extern_crate.def_id) .get_attrs(extern_crate.def_id)
@ -130,7 +131,7 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
.map(|attr| attr.span) .map(|attr| attr.span)
.fold(span, |acc, attr_span| acc.to(attr_span)); .fold(span, |acc, attr_span| acc.to(attr_span));
tcx.struct_span_lint_hir(lint, id, span, msg) lint.build("unused extern crate")
.span_suggestion_short( .span_suggestion_short(
span_with_attrs, span_with_attrs,
"remove it", "remove it",
@ -138,6 +139,7 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )
.emit(); .emit();
});
continue; continue;
} }
} }
@ -168,16 +170,18 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
if !tcx.get_attrs(extern_crate.def_id).is_empty() { if !tcx.get_attrs(extern_crate.def_id).is_empty() {
continue; continue;
} }
tcx.struct_span_lint_hir(lint, id, extern_crate.span, |lint| {
// Otherwise, we can convert it into a `use` of some kind. // 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 { let base_replacement = match extern_crate.orig_name {
Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name),
None => format!("use {};", item.ident.name), None => format!("use {};", item.ident.name),
}; };
let replacement = visibility_qualified(&item.vis, base_replacement); let replacement = visibility_qualified(&item.vis, base_replacement);
tcx.struct_span_lint_hir(lint, id, extern_crate.span, msg) 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( .span_suggestion_short(
extern_crate.span, extern_crate.span,
&help, &help,
@ -185,6 +189,7 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )
.emit(); .emit();
})
} }
} }

View file

@ -1151,14 +1151,17 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics {
GenericParamKind::Type { ref default, synthetic, .. } => { GenericParamKind::Type { ref default, synthetic, .. } => {
if !allow_defaults && default.is_some() { if !allow_defaults && default.is_some() {
if !tcx.features().default_type_parameter_fallback { if !tcx.features().default_type_parameter_fallback {
tcx.lint_hir( tcx.struct_span_lint_hir(
lint::builtin::INVALID_TYPE_PARAM_DEFAULT, lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
param.hir_id, param.hir_id,
param.span, param.span,
&format!( |lint| {
lint.build(&format!(
"defaults for type parameters are only allowed in \ "defaults for type parameters are only allowed in \
`struct`, `enum`, `type`, or `trait` definitions." `struct`, `enum`, `type`, or `trait` definitions."
), ))
.emit();
},
); );
} }
} }
@ -2956,10 +2959,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
lint::builtin::INLINE_NO_SANITIZE, lint::builtin::INLINE_NO_SANITIZE,
hir_id, hir_id,
no_sanitize_span, no_sanitize_span,
"`no_sanitize` will have no effect after inlining", |lint| {
) lint.build("`no_sanitize` will have no effect after inlining")
.span_note(inline_span, "inlining requested here") .span_note(inline_span, "inlining requested here")
.emit(); .emit();
},
)
} }
} }
} }

View file

@ -669,14 +669,15 @@ fn build_diagnostic(
let attrs = &item.attrs; let attrs = &item.attrs;
let sp = span_of_attrs(attrs).unwrap_or(item.source.span()); 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, lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
hir_id, hir_id,
sp, 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(link_range) = link_range {
if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) { if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs)
{
diag.set_span(sp); diag.set_span(sp);
diag.span_label(sp, short_err_msg); diag.span_label(sp, short_err_msg);
} else { } else {
@ -684,7 +685,8 @@ fn build_diagnostic(
// ^ ~~~~ // ^ ~~~~
// | link_range // | link_range
// last_new_line_offset // last_new_line_offset
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); 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(""); let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
// Print the line containing the `link_range` and manually mark it with '^'s. // Print the line containing the `link_range` and manually mark it with '^'s.
@ -702,6 +704,8 @@ fn build_diagnostic(
diag.help(help_msg); diag.help(help_msg);
} }
diag.emit(); diag.emit();
},
);
} }
/// Reports a resolution failure diagnostic. /// Reports a resolution failure diagnostic.
@ -766,6 +770,11 @@ fn ambiguity_error(
let attrs = &item.attrs; let attrs = &item.attrs;
let sp = span_of_attrs(attrs).unwrap_or(item.source.span()); let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
cx.tcx.struct_span_lint_hir(
lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
hir_id,
sp,
|lint| {
let mut msg = format!("`{}` is ", path_str); let mut msg = format!("`{}` is ", path_str);
let candidates = [TypeNS, ValueNS, MacroNS] let candidates = [TypeNS, ValueNS, MacroNS]
@ -794,15 +803,11 @@ fn ambiguity_error(
} }
} }
let mut diag = cx.tcx.struct_span_lint_hir( let mut diag = lint.build(&msg);
lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
hir_id,
sp,
&msg,
);
if let Some(link_range) = link_range { if let Some(link_range) = link_range {
if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) { if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs)
{
diag.set_span(sp); diag.set_span(sp);
diag.span_label(sp, "ambiguous link"); diag.span_label(sp, "ambiguous link");
@ -849,7 +854,8 @@ fn ambiguity_error(
// ^ ~~~~ // ^ ~~~~
// | link_range // | link_range
// last_new_line_offset // last_new_line_offset
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); 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(""); let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
// Print the line containing the `link_range` and manually mark it with '^'s. // Print the line containing the `link_range` and manually mark it with '^'s.
@ -863,8 +869,9 @@ fn ambiguity_error(
)); ));
} }
} }
diag.emit(); diag.emit();
},
);
} }
/// Given an enum variant's res, return the res of its enum and the associated fragment. /// Given an enum variant's res, return the res of its enum and the associated fragment.

View file

@ -342,24 +342,19 @@ pub fn look_for_tests<'tcx>(
if check_missing_code == true && tests.found_tests == 0 { if check_missing_code == true && tests.found_tests == 0 {
let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span());
let mut diag = cx.tcx.struct_span_lint_hir( cx.tcx.struct_span_lint_hir(lint::builtin::MISSING_DOC_CODE_EXAMPLES, hir_id, sp, |lint| {
lint::builtin::MISSING_DOC_CODE_EXAMPLES, lint.build("missing code example in this documentation").emit()
hir_id, });
sp,
"missing code example in this documentation",
);
diag.emit();
} else if check_missing_code == false } else if check_missing_code == false
&& tests.found_tests > 0 && tests.found_tests > 0
&& !cx.renderinfo.borrow().access_levels.is_public(item.def_id) && !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, lint::builtin::PRIVATE_DOC_TESTS,
hir_id, hir_id,
span_of_attrs(&item.attrs).unwrap_or(item.source.span()), 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();
} }
} }

View file

@ -4,17 +4,19 @@
extern crate rustc_ast_pretty; extern crate rustc_ast_pretty;
extern crate rustc_driver; extern crate rustc_driver;
extern crate rustc_hir; extern crate rustc_hir;
#[macro_use] extern crate rustc_lint; #[macro_use]
#[macro_use] extern crate rustc_session; extern crate rustc_lint;
#[macro_use]
extern crate rustc_session;
extern crate rustc_span; extern crate rustc_span;
extern crate syntax; extern crate syntax;
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_hir::intravisit;
use rustc_hir as hir;
use rustc_hir::Node;
use rustc_lint::{LateContext, LintPass, LintArray, LateLintPass, LintContext};
use rustc_driver::plugin::Registry; use rustc_driver::plugin::Registry;
use rustc_hir as hir;
use rustc_hir::intravisit;
use rustc_hir::Node;
use rustc_lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc_span::source_map; use rustc_span::source_map;
#[plugin_registrar] #[plugin_registrar]
@ -32,14 +34,15 @@ declare_lint! {
declare_lint_pass!(MissingWhitelistedAttrPass => [MISSING_WHITELISTED_ATTR]); declare_lint_pass!(MissingWhitelistedAttrPass => [MISSING_WHITELISTED_ATTR]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingWhitelistedAttrPass { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingWhitelistedAttrPass {
fn check_fn(&mut self, fn check_fn(
&mut self,
cx: &LateContext<'a, 'tcx>, cx: &LateContext<'a, 'tcx>,
_: intravisit::FnKind<'tcx>, _: intravisit::FnKind<'tcx>,
_: &'tcx hir::FnDecl, _: &'tcx hir::FnDecl,
_: &'tcx hir::Body, _: &'tcx hir::Body,
span: source_map::Span, span: source_map::Span,
id: hir::HirId) { id: hir::HirId,
) {
let item = match cx.tcx.hir().get(id) { let item = match cx.tcx.hir().get(id) {
Node::Item(item) => item, Node::Item(item) => item,
_ => cx.tcx.hir().expect_item(cx.tcx.hir().get_parent_item(id)), _ => cx.tcx.hir().expect_item(cx.tcx.hir().get_parent_item(id)),
@ -47,8 +50,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingWhitelistedAttrPass {
let whitelisted = |attr| pprust::attribute_to_string(attr).contains("whitelisted_attr"); let whitelisted = |attr| pprust::attribute_to_string(attr).contains("whitelisted_attr");
if !item.attrs.iter().any(whitelisted) { if !item.attrs.iter().any(whitelisted) {
cx.span_lint(MISSING_WHITELISTED_ATTR, span, cx.lint(MISSING_WHITELISTED_ATTR, |lint| {
"Missing 'whitelisted_attr' attribute"); lint.build("Missing 'whitelisted_attr' attribute").set_span(span).emit()
});
} }
} }
} }

View file

@ -5,12 +5,14 @@
extern crate rustc_driver; extern crate rustc_driver;
extern crate rustc_hir; extern crate rustc_hir;
extern crate rustc_span; extern crate rustc_span;
#[macro_use] extern crate rustc_lint; #[macro_use]
#[macro_use] extern crate rustc_session; extern crate rustc_lint;
#[macro_use]
extern crate rustc_session;
extern crate syntax; extern crate syntax;
use rustc_lint::{LateContext, LintContext, LintPass, LateLintPass};
use rustc_driver::plugin::Registry; use rustc_driver::plugin::Registry;
use rustc_lint::{LateContext, LateLintPass, LintContext, LintPass};
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use syntax::attr; use syntax::attr;
@ -28,8 +30,10 @@ macro_rules! fake_lint_pass {
fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) { fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) {
$( $(
if !attr::contains_name(&krate.attrs, $attr) { if !attr::contains_name(&krate.attrs, $attr) {
cx.span_lint(CRATE_NOT_OKAY, krate.span, cx.lint(CRATE_NOT_OKAY, |lint| {
&format!("crate is not marked with #![{}]", $attr)); let msg = format!("crate is not marked with #![{}]", $attr);
lint.build(&msg).set_span(krate.span).emit()
});
} }
)* )*
} }

View file

@ -5,13 +5,15 @@
extern crate rustc_driver; extern crate rustc_driver;
extern crate rustc_hir; extern crate rustc_hir;
#[macro_use] extern crate rustc_lint; #[macro_use]
#[macro_use] extern crate rustc_session; extern crate rustc_lint;
#[macro_use]
extern crate rustc_session;
extern crate rustc_span; extern crate rustc_span;
extern crate syntax; extern crate syntax;
use rustc_lint::{LateContext, LintContext, LintPass, LateLintPass, LintArray};
use rustc_driver::plugin::Registry; use rustc_driver::plugin::Registry;
use rustc_lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use syntax::attr; use syntax::attr;
@ -26,8 +28,11 @@ declare_lint_pass!(Pass => [CRATE_NOT_OKAY]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) { fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) {
if !attr::contains_name(&krate.attrs, Symbol::intern("crate_okay")) { if !attr::contains_name(&krate.attrs, Symbol::intern("crate_okay")) {
cx.span_lint(CRATE_NOT_OKAY, krate.span, cx.lint(CRATE_NOT_OKAY, |lint| {
"crate is not marked with #![crate_okay]"); lint.build("crate is not marked with #![crate_okay]")
.set_span(krate.span)
.emit()
});
} }
} }
} }

View file

@ -6,11 +6,13 @@
// Load rustc as a plugin to get macros. // Load rustc as a plugin to get macros.
extern crate rustc_driver; extern crate rustc_driver;
extern crate rustc_hir; extern crate rustc_hir;
#[macro_use] extern crate rustc_lint; #[macro_use]
#[macro_use] extern crate rustc_session; extern crate rustc_lint;
#[macro_use]
extern crate rustc_session;
use rustc_lint::{LateContext, LintContext, LintPass, LateLintPass, LintArray, LintId};
use rustc_driver::plugin::Registry; use rustc_driver::plugin::Registry;
use rustc_lint::{LateContext, LateLintPass, LintArray, LintContext, LintId, LintPass};
declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
@ -21,8 +23,12 @@ declare_lint_pass!(Pass => [TEST_LINT, PLEASE_LINT]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_item(&mut self, cx: &LateContext, it: &rustc_hir::Item) { fn check_item(&mut self, cx: &LateContext, it: &rustc_hir::Item) {
match &*it.ident.as_str() { match &*it.ident.as_str() {
"lintme" => cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'"), "lintme" => cx.lint(TEST_LINT, |lint| {
"pleaselintme" => cx.span_lint(PLEASE_LINT, it.span, "item is named 'pleaselintme'"), lint.build("item is named 'lintme'").set_span(it.span).emit()
}),
"pleaselintme" => cx.lint(PLEASE_LINT, |lint| {
lint.build("item is named 'pleaselintme'").set_span(it.span).emit()
}),
_ => {} _ => {}
} }
} }
@ -32,6 +38,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
pub fn plugin_registrar(reg: &mut Registry) { pub fn plugin_registrar(reg: &mut Registry) {
reg.lint_store.register_lints(&[&TEST_LINT, &PLEASE_LINT]); reg.lint_store.register_lints(&[&TEST_LINT, &PLEASE_LINT]);
reg.lint_store.register_late_pass(|| box Pass); reg.lint_store.register_late_pass(|| box Pass);
reg.lint_store.register_group(true, "lint_me", None, reg.lint_store.register_group(
vec![LintId::of(&TEST_LINT), LintId::of(&PLEASE_LINT)]); true,
"lint_me",
None,
vec![LintId::of(&TEST_LINT), LintId::of(&PLEASE_LINT)],
);
} }

View file

@ -7,11 +7,13 @@ extern crate syntax;
// Load rustc as a plugin to get macros // Load rustc as a plugin to get macros
extern crate rustc_driver; extern crate rustc_driver;
#[macro_use] extern crate rustc_lint; #[macro_use]
#[macro_use] extern crate rustc_session; extern crate rustc_lint;
#[macro_use]
extern crate rustc_session;
use rustc_lint::{EarlyContext, LintContext, LintPass, EarlyLintPass, LintArray};
use rustc_driver::plugin::Registry; use rustc_driver::plugin::Registry;
use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
use syntax::ast; use syntax::ast;
declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
@ -20,7 +22,9 @@ declare_lint_pass!(Pass => [TEST_LINT]);
impl EarlyLintPass for Pass { impl EarlyLintPass for Pass {
fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
if it.ident.name.as_str() == "lintme" { if it.ident.name.as_str() == "lintme" {
cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'"); cx.lint(TEST_LINT, |lint| {
lint.build("item is named 'lintme'").set_span(it.span).emit()
});
} }
} }
} }

View file

@ -5,11 +5,13 @@ extern crate syntax;
// Load rustc as a plugin to get macros // Load rustc as a plugin to get macros
extern crate rustc_driver; extern crate rustc_driver;
#[macro_use] extern crate rustc_lint; #[macro_use]
#[macro_use] extern crate rustc_session; extern crate rustc_lint;
#[macro_use]
extern crate rustc_session;
use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass, LintId};
use rustc_driver::plugin::Registry; use rustc_driver::plugin::Registry;
use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintId, LintPass};
use syntax::ast; use syntax::ast;
declare_tool_lint!(pub clippy::TEST_LINT, Warn, "Warn about stuff"); declare_tool_lint!(pub clippy::TEST_LINT, Warn, "Warn about stuff");
declare_tool_lint!( declare_tool_lint!(
@ -30,10 +32,14 @@ declare_lint_pass!(Pass => [TEST_LINT, TEST_GROUP, TEST_RUSTC_TOOL_LINT]);
impl EarlyLintPass for Pass { impl EarlyLintPass for Pass {
fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
if it.ident.name.as_str() == "lintme" { if it.ident.name.as_str() == "lintme" {
cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'"); cx.lint(TEST_LINT, |lint| {
lint.build("item is named 'lintme'").set_span(it.span).emit()
});
} }
if it.ident.name.as_str() == "lintmetoo" { if it.ident.name.as_str() == "lintmetoo" {
cx.span_lint(TEST_GROUP, it.span, "item is named 'lintmetoo'"); cx.lint(TEST_GROUP, |lint| {
lint.build("item is named 'lintmetoo'").set_span(it.span).emit()
});
} }
} }
} }
@ -42,6 +48,10 @@ impl EarlyLintPass for Pass {
pub fn plugin_registrar(reg: &mut Registry) { pub fn plugin_registrar(reg: &mut Registry) {
reg.lint_store.register_lints(&[&TEST_RUSTC_TOOL_LINT, &TEST_LINT, &TEST_GROUP]); reg.lint_store.register_lints(&[&TEST_RUSTC_TOOL_LINT, &TEST_LINT, &TEST_GROUP]);
reg.lint_store.register_early_pass(|| box Pass); reg.lint_store.register_early_pass(|| box Pass);
reg.lint_store.register_group(true, "clippy::group", Some("clippy_group"), reg.lint_store.register_group(
vec![LintId::of(&TEST_LINT), LintId::of(&TEST_GROUP)]); true,
"clippy::group",
Some("clippy_group"),
vec![LintId::of(&TEST_LINT), LintId::of(&TEST_GROUP)],
);
} }

View file

@ -32,6 +32,7 @@ impl Tr for E {
fn f() -> Self::V { 0 } fn f() -> Self::V { 0 }
//~^ ERROR ambiguous associated item //~^ ERROR ambiguous associated item
//~| WARN this was previously accepted //~| WARN this was previously accepted
//~| HELP use fully-qualified syntax
} }
fn main() {} fn main() {}