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,132 +174,164 @@ 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> { ) {
let mut err = match (level, span) { // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to
(Level::Allow, _) => return sess.diagnostic().struct_dummy(), // the "real" work.
(Level::Warn, Some(span)) => sess.struct_span_warn(span, msg), fn struct_lint_level_impl(
(Level::Warn, None) => sess.struct_warn(msg), sess: &'s Session,
(Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => sess.struct_span_err(span, msg), lint: &'static Lint,
(Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(msg), level: Level,
}; src: LintSource,
span: Option<MultiSpan>,
// Check for future incompatibility lints and issue a stronger warning. decorate: Box<dyn for<'b> FnOnce(LintDiagnosticBuilder<'b>) + 'd>,
let lint_id = LintId::of(lint); ) {
let future_incompatible = lint.future_incompatible; let mut err = match (level, span) {
(Level::Allow, _) => {
// If this code originates in a foreign macro, aka something that this crate return;
// did not itself author, then it's likely that there's nothing this crate
// can do about it. We probably want to skip the lint entirely.
if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
// Any suggestions made here are likely to be incorrect, so anything we
// emit shouldn't be automatically fixed by rustfix.
err.allow_suggestions(false);
// If this is a future incompatible lint it'll become a hard error, so
// we have to emit *something*. Also allow lints to whitelist themselves
// on a case-by-case basis for emission in a foreign macro.
if future_incompatible.is_none() && !lint.report_in_external_macro {
err.cancel();
// Don't continue further, since we don't want to have
// `diag_span_note_once` called for a diagnostic that isn't emitted.
return err;
}
}
let name = lint.name_lower();
match src {
LintSource::Default => {
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!("`#[{}({})]` on by default", level.as_str(), name),
);
}
LintSource::CommandLine(lint_flag_val) => {
let flag = match level {
Level::Warn => "-W",
Level::Deny => "-D",
Level::Forbid => "-F",
Level::Allow => panic!(),
};
let hyphen_case_lint_name = name.replace("_", "-");
if lint_flag_val.as_str() == name {
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"requested on the command line with `{} {}`",
flag, hyphen_case_lint_name
),
);
} else {
let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-");
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"`{} {}` implied by `{} {}`",
flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
),
);
} }
} (Level::Warn, Some(span)) => sess.struct_span_warn(span, ""),
LintSource::Node(lint_attr_name, src, reason) => { (Level::Warn, None) => sess.struct_warn(""),
if let Some(rationale) = reason { (Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => {
err.note(&rationale.as_str()); sess.struct_span_err(span, "")
} }
sess.diag_span_note_once( (Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(""),
&mut err,
DiagnosticMessageId::from(lint),
src,
"the lint level is defined here",
);
if lint_attr_name.as_str() != name {
let level_str = level.as_str();
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"`#[{}({})]` implied by `#[{}({})]`",
level_str, name, level_str, lint_attr_name
),
);
}
}
}
err.code(DiagnosticId::Lint(name));
if let Some(future_incompatible) = future_incompatible {
const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \
it will become a hard error";
let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) {
"once this method is added to the standard library, \
the ambiguity may cause an error or change in behavior!"
.to_owned()
} else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) {
"this borrowing pattern was not meant to be accepted, \
and may become a hard error in the future"
.to_owned()
} else if let Some(edition) = future_incompatible.edition {
format!("{} in the {} edition!", STANDARD_MESSAGE, edition)
} else {
format!("{} in a future release!", STANDARD_MESSAGE)
}; };
let citation = format!("for more information, see {}", future_incompatible.reference);
err.warn(&explanation);
err.note(&citation);
}
return err; // Check for future incompatibility lints and issue a stronger warning.
let lint_id = LintId::of(lint);
let future_incompatible = lint.future_incompatible;
// If this code originates in a foreign macro, aka something that this crate
// did not itself author, then it's likely that there's nothing this crate
// can do about it. We probably want to skip the lint entirely.
if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
// Any suggestions made here are likely to be incorrect, so anything we
// emit shouldn't be automatically fixed by rustfix.
err.allow_suggestions(false);
// If this is a future incompatible lint it'll become a hard error, so
// we have to emit *something*. Also allow lints to whitelist themselves
// on a case-by-case basis for emission in a foreign macro.
if future_incompatible.is_none() && !lint.report_in_external_macro {
err.cancel();
// Don't continue further, since we don't want to have
// `diag_span_note_once` called for a diagnostic that isn't emitted.
return;
}
}
let name = lint.name_lower();
match src {
LintSource::Default => {
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!("`#[{}({})]` on by default", level.as_str(), name),
);
}
LintSource::CommandLine(lint_flag_val) => {
let flag = match level {
Level::Warn => "-W",
Level::Deny => "-D",
Level::Forbid => "-F",
Level::Allow => panic!(),
};
let hyphen_case_lint_name = name.replace("_", "-");
if lint_flag_val.as_str() == name {
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"requested on the command line with `{} {}`",
flag, hyphen_case_lint_name
),
);
} else {
let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-");
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"`{} {}` implied by `{} {}`",
flag, hyphen_case_lint_name, flag, hyphen_case_flag_val
),
);
}
}
LintSource::Node(lint_attr_name, src, reason) => {
if let Some(rationale) = reason {
err.note(&rationale.as_str());
}
sess.diag_span_note_once(
&mut err,
DiagnosticMessageId::from(lint),
src,
"the lint level is defined here",
);
if lint_attr_name.as_str() != name {
let level_str = level.as_str();
sess.diag_note_once(
&mut err,
DiagnosticMessageId::from(lint),
&format!(
"`#[{}({})]` implied by `#[{}({})]`",
level_str, name, level_str, lint_attr_name
),
);
}
}
}
err.code(DiagnosticId::Lint(name));
if let Some(future_incompatible) = future_incompatible {
const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \
it will become a hard error";
let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) {
"once this method is added to the standard library, \
the ambiguity may cause an error or change in behavior!"
.to_owned()
} else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) {
"this borrowing pattern was not meant to be accepted, \
and may become a hard error in the future"
.to_owned()
} else if let Some(edition) = future_incompatible.edition {
format!("{} in the {} edition!", STANDARD_MESSAGE, edition)
} else {
format!("{} in a future release!", STANDARD_MESSAGE)
};
let citation = format!("for more information, see {}", future_incompatible.reference);
err.warn(&explanation);
err.note(&citation);
}
// 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| {
if let hir::Node::Expr(_) = tcx.hir().get(hir_id) { let mut diag = lint.build(message);
deprecation_suggestion(&mut diag, suggestion, span); if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
} 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,7 +149,22 @@ 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 add_span_labels = |err: &mut DiagnosticBuilder<'_>| {
if !must_error {
err.span_label(self.span, self.error.to_string());
}
// Skip the last, which is just the environment of the constant. The stacktrace
// is sometimes empty because we create "fake" eval contexts in CTFE to do work
// on constant values.
if self.stacktrace.len() > 0 {
for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
err.span_label(frame_info.call_site, frame_info.to_string());
}
}
};
if let (Some(lint_root), false) = (lint_root, must_error) {
let hir_id = self let hir_id = self
.stacktrace .stacktrace
.iter() .iter()
@ -155,25 +176,22 @@ impl<'tcx> ConstEvalErr<'tcx> {
rustc_session::lint::builtin::CONST_ERR, rustc_session::lint::builtin::CONST_ERR,
hir_id, hir_id,
tcx.span, tcx.span,
message, |lint| {
) let mut err = lint.build(message);
} else if must_error { add_span_labels(&mut err);
struct_error(tcx, &self.error.to_string()) emit(err);
},
);
} else { } else {
struct_error(tcx, message) let mut err = if must_error {
struct_error(tcx, &self.error.to_string())
} else {
struct_error(tcx, message)
};
add_span_labels(&mut err);
emit(err);
}; };
if !must_error { Ok(())
err.span_label(self.span, self.error.to_string());
}
// Skip the last, which is just the environment of the constant. The stacktrace
// is sometimes empty because we create "fake" eval contexts in CTFE to do work
// on constant values.
if self.stacktrace.len() > 0 {
for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
err.span_label(frame_info.call_site, frame_info.to_string());
}
}
Ok(err)
} }
} }

View file

@ -227,37 +227,47 @@ 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| {
"the trait `{}` cannot be made into an object", let mut err = lint.build(&format!(
tcx.def_path_str(trait_def_id) "the trait `{}` cannot be made into an object",
), tcx.def_path_str(trait_def_id)
));
let node = tcx.hir().get_if_local(trait_def_id);
let msg = if let Some(hir::Node::Item(item)) = node {
err.span_label(
item.ident.span,
"this trait cannot be made into an object...",
);
format!("...because {}", violation.error_msg())
} else {
format!(
"the trait cannot be made into an object because {}",
violation.error_msg()
)
};
err.span_label(*span, &msg);
match (node, violation.solution()) {
(Some(_), Some((note, None))) => {
err.help(&note);
}
(Some(_), Some((note, Some((sugg, span))))) => {
err.span_suggestion(
span,
&note,
sugg,
Applicability::MachineApplicable,
);
}
// Only provide the help if its a local trait, otherwise it's not actionable.
_ => {}
}
err.emit();
},
); );
let node = tcx.hir().get_if_local(trait_def_id);
let msg = if let Some(hir::Node::Item(item)) = node {
err.span_label(item.ident.span, "this trait cannot be made into an object...");
format!("...because {}", violation.error_msg())
} else {
format!(
"the trait cannot be made into an object because {}",
violation.error_msg()
)
};
err.span_label(*span, &msg);
match (node, violation.solution()) {
(Some(_), Some((note, None))) => {
err.help(&note);
}
(Some(_), Some((note, Some((sugg, span))))) => {
err.span_suggestion(span, &note, sugg, Applicability::MachineApplicable);
}
// Only provide the help if its a local trait, otherwise it's not actionable.
_ => {}
}
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,22 +318,70 @@ pub(super) fn specialization_graph_provider(
}; };
if let Some(overlap) = overlap { if let Some(overlap) = overlap {
let msg = format!(
"conflicting implementations of trait `{}`{}:{}",
overlap.trait_desc,
overlap
.self_desc
.clone()
.map_or(String::new(), |ty| { format!(" for type `{}`", ty) }),
match used_to_be_allowed {
Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)",
_ => "",
}
);
let impl_span = let impl_span =
tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap()); tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
let mut err = match used_to_be_allowed {
None => struct_span_err!(tcx.sess, impl_span, E0119, "{}", msg), // Work to be done after we've built the DiagnosticBuilder. We have to define it
// now because the struct_lint methods don't return back the DiagnosticBuilder
// that's passed in.
let decorate = |err: LintDiagnosticBuilder<'_>| {
let msg = format!(
"conflicting implementations of trait `{}`{}:{}",
overlap.trait_desc,
overlap
.self_desc
.clone()
.map_or(String::new(), |ty| { format!(" for type `{}`", ty) }),
match used_to_be_allowed {
Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)",
_ => "",
}
);
let mut err = err.build(&msg);
match tcx.span_of_impl(overlap.with_impl) {
Ok(span) => {
err.span_label(
tcx.sess.source_map().def_span(span),
"first implementation here".to_string(),
);
err.span_label(
impl_span,
format!(
"conflicting implementation{}",
overlap
.self_desc
.map_or(String::new(), |ty| format!(" for `{}`", ty))
),
);
}
Err(cname) => {
let msg = match to_pretty_impl_header(tcx, overlap.with_impl) {
Some(s) => format!(
"conflicting implementation in crate `{}`:\n- {}",
cname, s
),
None => format!("conflicting implementation in crate `{}`", cname),
};
err.note(&msg);
}
}
for cause in &overlap.intercrate_ambiguity_causes {
cause.add_intercrate_ambiguity_hint(&mut err);
}
if overlap.involves_placeholder {
coherence::add_placeholder_note(&mut err);
}
err.emit()
};
match used_to_be_allowed {
None => {
let err = struct_span_err!(tcx.sess, impl_span, E0119, "");
decorate(LintDiagnosticBuilder::new(err));
}
Some(kind) => { Some(kind) => {
let lint = match kind { let lint = match kind {
FutureCompatOverlapErrorKind::Issue33140 => { FutureCompatOverlapErrorKind::Issue33140 => {
@ -344,47 +393,10 @@ pub(super) fn specialization_graph_provider(
lint, lint,
tcx.hir().as_local_hir_id(impl_def_id).unwrap(), tcx.hir().as_local_hir_id(impl_def_id).unwrap(),
impl_span, impl_span,
&msg, decorate,
) )
} }
}; };
match tcx.span_of_impl(overlap.with_impl) {
Ok(span) => {
err.span_label(
tcx.sess.source_map().def_span(span),
"first implementation here".to_string(),
);
err.span_label(
impl_span,
format!(
"conflicting implementation{}",
overlap
.self_desc
.map_or(String::new(), |ty| format!(" for `{}`", ty))
),
);
}
Err(cname) => {
let msg = match to_pretty_impl_header(tcx, overlap.with_impl) {
Some(s) => {
format!("conflicting implementation in crate `{}`:\n- {}", cname, s)
}
None => format!("conflicting implementation in crate `{}`", cname),
};
err.note(&msg);
}
}
for cause in &overlap.intercrate_ambiguity_causes {
cause.add_intercrate_ambiguity_hint(&mut err);
}
if overlap.involves_placeholder {
coherence::add_placeholder_note(&mut err);
}
err.emit();
} }
} else { } 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,14 +78,16 @@ 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| {
.span_suggestion_short( lint.build(msg)
condition_span, .span_suggestion_short(
"use `loop`", condition_span,
"loop".to_owned(), "use `loop`",
Applicability::MachineApplicable, "loop".to_owned(),
) 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,29 +178,28 @@ 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 {
); hir::BindingAnnotation::Unannotated => None,
let binding = match binding_annot { hir::BindingAnnotation::Mutable => Some("mut"),
hir::BindingAnnotation::Unannotated => None, hir::BindingAnnotation::Ref => Some("ref"),
hir::BindingAnnotation::Mutable => Some("mut"), hir::BindingAnnotation::RefMut => Some("ref mut"),
hir::BindingAnnotation::Ref => Some("ref"), };
hir::BindingAnnotation::RefMut => Some("ref mut"), let ident = if let Some(binding) = binding {
}; format!("{} {}", binding, ident)
let ident = if let Some(binding) = binding { } else {
format!("{} {}", binding, ident) ident.to_string()
} else { };
ident.to_string() err.span_suggestion(
}; fieldpat.span,
err.span_suggestion( "use shorthand field pattern",
fieldpat.span, ident,
"use shorthand field pattern", Applicability::MachineApplicable,
ident, );
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,28 +645,28 @@ 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 {
let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span); 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, 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, "anonymous parameters are deprecated and will be \
arg.pat.span,
"anonymous parameters are deprecated and will be \
removed in the next edition.", removed in the next edition.",
) )
.span_suggestion( .span_suggestion(
arg.pat.span, arg.pat.span,
"try naming the parameter or explicitly \ "try naming the parameter or explicitly \
ignoring it", ignoring it",
format!("_: {}", ty_snip), format!("_: {}", ty_snip),
appl, appl,
) )
.emit(); .emit();
})
} }
} }
_ => (), _ => (),
@ -687,14 +700,16 @@ 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| {
.span_suggestion_short( lint.build(msg)
attr.span, .span_suggestion_short(
suggestion.unwrap_or("remove this attribute"), attr.span,
String::new(), suggestion.unwrap_or("remove this attribute"),
Applicability::MachineApplicable, String::new(),
) 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 {
err.help(
"to document an item produced by a macro, \
the macro must produce the documentation as part of its expansion",
); );
} if is_macro_expansion {
err.help(
err.emit(); "to document an item produced by a macro, \
the macro must produce the documentation as part of its expansion",
);
}
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", )
); .span_suggestion_short(
err.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, )
); .emit();
err.emit(); });
break; break;
} }
} }
@ -855,26 +869,28 @@ 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
let msg = "const items should never be `#[no_mangle]`"; cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| {
let mut err = cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg); let msg = "const items should never be `#[no_mangle]`";
let mut err = lint.build(msg);
// account for "pub const" (#45562) // account for "pub const" (#45562)
let start = cx let start = cx
.tcx .tcx
.sess .sess
.source_map() .source_map()
.span_to_snippet(it.span) .span_to_snippet(it.span)
.map(|snippet| snippet.find("const").unwrap_or(0)) .map(|snippet| snippet.find("const").unwrap_or(0))
.unwrap_or(0) as u32; .unwrap_or(0) as u32;
// `const` is 5 chars // `const` is 5 chars
let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5)); let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
err.span_suggestion( err.span_suggestion(
const_span, const_span,
"try a static value", "try a static value",
"pub static".to_owned(), "pub static".to_owned(),
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,28 +1004,26 @@ 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, let replacement = if cx.tcx.features().crate_visibility_modifier {
&format!("unreachable `pub` {}", what), "crate"
); } else {
let replacement = if cx.tcx.features().crate_visibility_modifier { "pub(crate)"
"crate" }
} else { .to_owned();
"pub(crate)"
}
.to_owned();
err.span_suggestion( err.span_suggestion(
vis.span, vis.span,
"consider restricting its visibility", "consider restricting its visibility",
replacement, replacement,
applicability, applicability,
); );
if exportable { if exportable {
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,28 +1132,30 @@ 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() {
let spans: Vec<_> = type_alias_generics cx.lint(
.where_clause
.predicates
.iter()
.map(|pred| pred.span())
.collect();
let mut err = cx.struct_span_lint(
TYPE_ALIAS_BOUNDS, TYPE_ALIAS_BOUNDS,
spans, |lint| {
"where clauses are not enforced in type aliases", let mut err = lint.build("where clauses are not enforced in type aliases");
let spans: Vec<_> = type_alias_generics
.where_clause
.predicates
.iter()
.map(|pred| pred.span())
.collect();
err.set_span(spans);
err.span_suggestion(
type_alias_generics.where_clause.span_for_predicates_or_empty_place(),
"the clause will not be checked when the type alias is used, and should be removed",
String::new(),
Applicability::MachineApplicable,
);
if !suggested_changing_assoc_types {
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
suggested_changing_assoc_types = true;
}
err.emit();
},
); );
err.span_suggestion(
type_alias_generics.where_clause.span_for_predicates_or_empty_place(),
"the clause will not be checked when the type alias is used, and should be removed",
String::new(),
Applicability::MachineApplicable,
);
if !suggested_changing_assoc_types {
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
suggested_changing_assoc_types = true;
}
err.emit();
} }
// 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,19 +1168,18 @@ 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, \
); and should be removed";
let msg = "the bound will not be checked when the type alias is used, \ err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable);
and should be removed"; if !suggested_changing_assoc_types {
err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable); TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
if !suggested_changing_assoc_types { suggested_changing_assoc_types = true;
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); }
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);
let end = expr_to_string(&end); cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| {
let replace = match start { let end = expr_to_string(&end);
Some(start) => format!("&({}..={})", expr_to_string(&start), end), let replace = match start {
None => format!("&(..={})", end), Some(start) => format!("&({}..={})", expr_to_string(&start), end),
}; None => format!("&(..={})", end),
let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg); };
err.span_suggestion( lint.build(msg)
pat.span, .span_suggestion(
suggestion, pat.span,
replace, suggestion,
Applicability::MachineApplicable, replace,
); 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)
join, .span_suggestion_short(
suggestion, join,
"..=".to_owned(), suggestion,
Applicability::MachineApplicable, "..=".to_owned(),
); 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), ident.span,
); "you can use a raw identifier to stay compatible",
lint.span_suggestion( format!("r#{}", ident),
ident.span, Applicability::MachineApplicable,
"you can use a raw identifier to stay compatible", )
format!("r#{}", ident), .emit()
Applicability::MachineApplicable, });
);
lint.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<_>>(), },
Applicability::MachineApplicable, lint_spans
); .into_iter()
err.emit(); .map(|span| (span, "".to_owned()))
.collect::<Vec<_>>(),
Applicability::MachineApplicable,
)
.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,30 +2040,28 @@ 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, "help: use `MaybeUninit<T>` instead, \
"help: use `MaybeUninit<T>` instead, \ and only call `assume_init` after initialization is done",
and only call `assume_init` after initialization is done", );
); if let Some(span) = span {
if let Some(span) = span { err.span_note(span, &msg);
err.span_note(span, &msg); } else {
} else { 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,150 +474,123 @@ 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("");
let sess = self.sess(); // Now, set up surrounding context.
match diagnostic { let sess = self.sess();
BuiltinLintDiagnostics::Normal => (), match diagnostic {
BuiltinLintDiagnostics::BareTraitObject(span, is_global) => { BuiltinLintDiagnostics::Normal => (),
let (sugg, app) = match sess.source_map().span_to_snippet(span) { BuiltinLintDiagnostics::BareTraitObject(span, is_global) => {
Ok(s) if is_global => { let (sugg, app) = match sess.source_map().span_to_snippet(span) {
(format!("dyn ({})", s), Applicability::MachineApplicable) Ok(s) if is_global => {
} (format!("dyn ({})", s), Applicability::MachineApplicable)
Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable), }
Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders), Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable),
}; Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders),
db.span_suggestion(span, "use `dyn`", sugg, app); };
} db.span_suggestion(span, "use `dyn`", sugg, app);
BuiltinLintDiagnostics::AbsPathWithModule(span) => { }
let (sugg, app) = match sess.source_map().span_to_snippet(span) { BuiltinLintDiagnostics::AbsPathWithModule(span) => {
Ok(ref s) => { let (sugg, app) = match sess.source_map().span_to_snippet(span) {
// FIXME(Manishearth) ideally the emitting code Ok(ref s) => {
// can tell us whether or not this is global // FIXME(Manishearth) ideally the emitting code
let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; // can tell us whether or not this is global
let opt_colon =
if s.trim_start().starts_with("::") { "" } else { "::" };
(format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable) (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
} }
Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders), Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
}; };
db.span_suggestion(span, "use `crate`", sugg, app); db.span_suggestion(span, "use `crate`", sugg, app);
} }
BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => { BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
db.span_label( db.span_label(
span, span,
"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(
db.span_note(span_def, "the macro is defined here"); span_def,
} ) => {
BuiltinLintDiagnostics::ElidedLifetimesInPaths( db.span_note(span_def, "the macro is defined here");
n, }
path_span, BuiltinLintDiagnostics::ElidedLifetimesInPaths(
incl_angl_brckt,
insertion_span,
anon_lts,
) => {
add_elided_lifetime_in_path_suggestion(
sess,
&mut db,
n, n,
path_span, path_span,
incl_angl_brckt, incl_angl_brckt,
insertion_span, insertion_span,
anon_lts, anon_lts,
); ) => {
} add_elided_lifetime_in_path_suggestion(
BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { sess,
db.span_suggestion(span, &note, sugg, Applicability::MaybeIncorrect); &mut db,
} n,
BuiltinLintDiagnostics::UnusedImports(message, replaces) => { path_span,
if !replaces.is_empty() { incl_angl_brckt,
db.tool_only_multipart_suggestion( insertion_span,
&message, anon_lts,
replaces,
Applicability::MachineApplicable,
); );
} }
} BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
BuiltinLintDiagnostics::RedundantImport(spans, ident) => { db.span_suggestion(span, &note, sugg, Applicability::MaybeIncorrect);
for (span, is_imported) in spans { }
let introduced = if is_imported { "imported" } else { "defined" }; BuiltinLintDiagnostics::UnusedImports(message, replaces) => {
db.span_label( if !replaces.is_empty() {
span, db.tool_only_multipart_suggestion(
format!("the item `{}` is already {} here", ident, introduced), &message,
); replaces,
Applicability::MachineApplicable,
);
}
}
BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
for (span, is_imported) in spans {
let introduced = if is_imported { "imported" } else { "defined" };
db.span_label(
span,
format!("the item `{}` is already {} here", ident, introduced),
);
}
}
BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
stability::deprecation_suggestion(&mut db, suggestion, span)
} }
} }
BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => { // Rewrap `db`, and pass control to the user.
stability::deprecation_suggestion(&mut db, suggestion, span) decorate(LintDiagnosticBuilder::new(db));
} });
}
db.emit();
} }
// 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) {
let msg = format!("Prefer {} over {}, it has better performance", replace, ident); cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, |lint| {
let mut db = cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, &msg); // FIXME: We can avoid a copy here. Would require us to take String instead of &str.
db.span_suggestion( let msg = format!("Prefer {} over {}, it has better performance", replace, ident);
ident.span, lint.build(&msg)
"use", .span_suggestion(
replace.to_string(), ident.span,
Applicability::MaybeIncorrect, // FxHashMap, ... needs another import "use",
); replace.to_string(),
db.note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace)) Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
.emit(); )
.note(&format!(
"a `use rustc_data_structures::fx::{}` may be necessary",
replace
))
.emit();
});
} }
} }
} }
@ -85,14 +91,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
if let Some(last) = segments.last() { 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| {
.span_suggestion( lint.build("usage of `ty::TyKind::<kind>`")
span, .span_suggestion(
"try using ty::<kind> directly", span,
"ty".to_string(), "try using ty::<kind> directly",
Applicability::MaybeIncorrect, // ty maybe needs an import "ty".to_string(),
) Applicability::MaybeIncorrect, // ty maybe needs an import
.emit(); )
.emit();
})
} }
} }
} }
@ -103,32 +111,28 @@ 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, .help("try using `Ty` instead")
"usage of `ty::TyKind`", .emit();
) })
.help("try using `Ty` instead")
.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, .span_suggestion(
&format!("usage of qualified `ty::{}`", t), path.span,
) "try using it unqualified",
.span_suggestion( t,
path.span, // The import probably needs to be changed
"try using it unqualified", Applicability::MaybeIncorrect,
t, )
// The import probably needs to be changed .emit();
Applicability::MaybeIncorrect, })
)
.emit();
} }
} }
} }
@ -142,19 +146,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
} }
} }
if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) { 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, .span_suggestion(
&format!("passing `{}` by reference", t), ty.span,
) "try passing by value",
.span_suggestion( t,
ty.span, // Changing type of function argument
"try passing by value", Applicability::MaybeIncorrect,
t, )
// Changing type of function argument .emit();
Applicability::MaybeIncorrect, })
)
.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")
.emit();
},
) )
.help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
.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,27 +234,29 @@ 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!(
.span_suggestion( "lint name `{}` is deprecated \
li.span(), and may not have an effect in the future. \
"change it to", Also `cfg_attr(cargo-clippy)` won't be necessary anymore",
new_lint_name.to_string(), name
Applicability::MachineApplicable, );
) lint.build(&msg)
.emit(); .span_suggestion(
li.span(),
"change it to",
new_lint_name.to_string(),
Applicability::MachineApplicable,
)
.emit();
},
);
let src = LintSource::Node( let src = LintSource::Node(
Symbol::intern(&new_lint_name), Symbol::intern(&new_lint_name),
@ -279,48 +282,49 @@ 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 {
err.span_suggestion(
li.span(),
"use the new name",
new_name,
Applicability::MachineApplicable,
);
}
err.emit();
},
); );
if let Some(new_name) = renamed {
err.span_suggestion(
li.span(),
"use the new name",
new_name,
Applicability::MachineApplicable,
);
}
err.emit();
} }
CheckLintNameResult::NoLint(suggestion) => { 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 {
db.span_suggestion(
li.span(),
"did you mean",
suggestion.to_string(),
Applicability::MachineApplicable,
);
}
db.emit();
},
); );
if let Some(suggestion) = suggestion {
db.span_suggestion(
li.span(),
"did you mean",
suggestion.to_string(),
Applicability::MachineApplicable,
);
}
db.emit();
} }
} }
} }
@ -390,10 +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) {
let msg = format!("{} `{}` should have an upper camel case name", sort, name); cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, |lint| {
cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, &msg) let msg = format!("{} `{}` should have an upper camel case name", sort, name);
.span_suggestion( lint.build(&msg)
ident.span, .span_suggestion(
"convert the identifier to upper camel case", ident.span,
to_camel_case(name), "convert the identifier to upper camel case",
Applicability::MaybeIncorrect, to_camel_case(name),
) Applicability::MaybeIncorrect,
.emit(); )
.emit()
})
} }
} }
} }
@ -223,25 +225,25 @@ impl NonSnakeCase {
let name = &ident.name.as_str(); let name = &ident.name.as_str();
if !is_snake_case(name) { if !is_snake_case(name) {
let sc = NonSnakeCase::to_snake_case(name); cx.struct_span_lint(NON_SNAKE_CASE, ident.span, |lint| {
let sc = NonSnakeCase::to_snake_case(name);
let msg = format!("{} `{}` should have a snake case name", sort, name);
let mut err = lint.build(&msg);
// We have a valid span in almost all cases, but we don't have one when linting a crate
// name provided via the command line.
if !ident.span.is_dummy() {
err.span_suggestion(
ident.span,
"convert the identifier to snake case",
sc,
Applicability::MaybeIncorrect,
);
} else {
err.help(&format!("convert the identifier to snake case: `{}`", sc));
}
let msg = format!("{} `{}` should have a snake case name", sort, name); err.emit();
let mut err = cx.struct_span_lint(NON_SNAKE_CASE, ident.span, &msg); });
// We have a valid span in almost all cases, but we don't have one when linting a crate
// name provided via the command line.
if !ident.span.is_dummy() {
err.span_suggestion(
ident.span,
"convert the identifier to snake case",
sc,
Applicability::MaybeIncorrect,
);
} else {
err.help(&format!("convert the identifier to snake case: `{}`", sc));
}
err.emit();
} }
} }
} }
@ -386,19 +388,18 @@ 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()) {
let uc = NonSnakeCase::to_snake_case(&name).to_uppercase(); cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, |lint| {
let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
let msg = format!("{} `{}` should have an upper case name", sort, name); lint.build(&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", uc,
uc, Applicability::MaybeIncorrect,
Applicability::MaybeIncorrect, )
) .emit();
.emit(); })
} }
} }
} }

View file

@ -26,19 +26,21 @@ 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 suggest_msg = if multiple { let mut err = lint.build(&msg);
"remove these semicolons" let suggest_msg = if multiple {
} else { "remove these semicolons"
"remove this semicolon" } else {
}; "remove this semicolon"
err.span_suggestion( };
stmt.span, err.span_suggestion(
&suggest_msg, stmt.span,
String::new(), &suggest_msg,
Applicability::MaybeIncorrect, String::new(),
); 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,35 +76,32 @@ 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, if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) {
&format!("range endpoint is out of range for `{}`", ty), use ast::{LitIntType, LitKind};
); // We need to preserve the literal's suffix,
if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) { // as it may determine typing information.
use ast::{LitIntType, LitKind}; let suffix = match lit.node {
// We need to preserve the literal's suffix, LitKind::Int(_, LitIntType::Signed(s)) => format!("{}", s.name_str()),
// as it may determine typing information. LitKind::Int(_, LitIntType::Unsigned(s)) => format!("{}", s.name_str()),
let suffix = match lit.node { LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(),
LitKind::Int(_, LitIntType::Signed(s)) => format!("{}", s.name_str()), _ => bug!(),
LitKind::Int(_, LitIntType::Unsigned(s)) => format!("{}", s.name_str()), };
LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(), let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
_ => bug!(), err.span_suggestion(
}; parent_expr.span,
let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); &"use an inclusive range instead",
err.span_suggestion( suggestion,
parent_expr.span, Applicability::MachineApplicable,
&"use an inclusive range instead", );
suggestion, err.emit();
Applicability::MachineApplicable, overwritten = true;
); }
err.emit(); });
return 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,41 +151,39 @@ 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();
let (t, actually) = match ty { cx.struct_span_lint(OVERFLOWING_LITERALS, expr.span, |lint| {
attr::IntType::SignedInt(t) => { let (t, actually) = match ty {
let actually = sign_extend(val, size) as i128; attr::IntType::SignedInt(t) => {
(t.name_str(), actually.to_string()) let actually = sign_extend(val, size) as i128;
(t.name_str(), actually.to_string())
}
attr::IntType::UnsignedInt(t) => {
let actually = truncate(val, size);
(t.name_str(), actually.to_string())
}
};
let mut err = lint.build(&format!("literal out of range for {}", t));
err.note(&format!(
"the literal `{}` (decimal `{}`) does not fit into \
an `{}` and will become `{}{}`",
repr_str, val, t, actually, t
));
if let Some(sugg_ty) = get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative)
{
if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
let (sans_suffix, _) = repr_str.split_at(pos);
err.span_suggestion(
expr.span,
&format!("consider using `{}` instead", sugg_ty),
format!("{}{}", sans_suffix, sugg_ty),
Applicability::MachineApplicable,
);
} else {
err.help(&format!("consider using `{}` instead", sugg_ty));
}
} }
attr::IntType::UnsignedInt(t) => { err.emit();
let actually = truncate(val, size); });
(t.name_str(), actually.to_string())
}
};
let mut err = cx.struct_span_lint(
OVERFLOWING_LITERALS,
expr.span,
&format!("literal out of range for {}", t),
);
err.note(&format!(
"the literal `{}` (decimal `{}`) does not fit into \
an `{}` and will become `{}{}`",
repr_str, val, t, actually, t
));
if let Some(sugg_ty) = get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative) {
if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
let (sans_suffix, _) = repr_str.split_at(pos);
err.span_suggestion(
expr.span,
&format!("consider using `{}` instead", sugg_ty),
format!("{}{}", sans_suffix, sugg_ty),
Applicability::MachineApplicable,
);
} else {
err.help(&format!("consider using `{}` instead", sugg_ty));
}
}
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`", par_e.span,
); &"use a `char` literal instead",
err.span_suggestion( format!("'\\u{{{:X}}}'", lit_val),
par_e.span, Applicability::MachineApplicable,
&"use a `char` literal instead", )
format!("'\\u{{{:X}}}'", lit_val), .emit();
Applicability::MachineApplicable, });
);
err.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,22 +869,21 @@ 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");
); if let Some(help) = help {
diag.span_label(sp, "not FFI-safe"); diag.help(help);
if let Some(help) = help {
diag.help(help);
}
diag.note(note);
if let ty::Adt(def, _) = ty.kind {
if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) {
diag.span_note(sp, "the type is defined here");
} }
} diag.note(note);
diag.emit(); if let ty::Adt(def, _) = ty.kind {
if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) {
diag.span_note(sp, "the type is defined here");
}
}
diag.emit();
});
} }
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { 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| {
"enum variant is more than three times \ lint.build(&format!(
"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) {
let msg = format!( cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
"unused {}`{}`{} that must be used", let msg = format!(
descr_pre_path, "unused {}`{}`{} that must be used",
cx.tcx.def_path_str(def_id), descr_pre_path,
descr_post_path cx.tcx.def_path_str(def_id),
); descr_post_path
let mut err = cx.struct_span_lint(UNUSED_MUST_USE, span, &msg); );
// check for #[must_use = "..."] let mut err = lint.build(&msg);
if let Some(note) = attr.value_str() { // check for #[must_use = "..."]
err.note(&note.as_str()); if let Some(note) = attr.value_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,17 +292,21 @@ 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) {
let msg = match attr.style { cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| {
ast::AttrStyle::Outer => { let msg = match attr.style {
"crate-level attribute should be an inner attribute: add an exclamation \ ast::AttrStyle::Outer => {
mark: `#![foo]`" "crate-level attribute should be an inner attribute: add an exclamation \
} mark: `#![foo]`"
ast::AttrStyle::Inner => "crate-level attribute should be in the root module", }
}; 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,52 +416,54 @@ impl UnusedParens {
msg: &str, msg: &str,
keep_space: (bool, bool), keep_space: (bool, bool),
) { ) {
let span_msg = format!("unnecessary parentheses around {}", msg); cx.struct_span_lint(UNUSED_PARENS, span, |lint| {
let mut err = cx.struct_span_lint(UNUSED_PARENS, span, &span_msg); let span_msg = format!("unnecessary parentheses around {}", msg);
let mut ate_left_paren = false; let mut err = lint.build(&span_msg);
let mut ate_right_paren = false; let mut ate_left_paren = false;
let parens_removed = pattern.trim_matches(|c| match c { let mut ate_right_paren = false;
'(' => { let parens_removed = pattern.trim_matches(|c| match c {
if ate_left_paren { '(' => {
false if ate_left_paren {
} else { false
ate_left_paren = true; } else {
true ate_left_paren = true;
true
}
} }
} ')' => {
')' => { if ate_right_paren {
if ate_right_paren { false
false } else {
} else { ate_right_paren = true;
ate_right_paren = true; true
true }
} }
} _ => false,
_ => false, });
});
let replace = { let replace = {
let mut replace = if keep_space.0 { let mut replace = if keep_space.0 {
let mut s = String::from(" "); let mut s = String::from(" ");
s.push_str(parens_removed); s.push_str(parens_removed);
s s
} else { } else {
String::from(parens_removed) String::from(parens_removed)
};
if keep_space.1 {
replace.push(' ');
}
replace
}; };
if keep_space.1 { err.span_suggestion_short(
replace.push(' '); span,
} "remove these parentheses",
replace replace,
}; Applicability::MachineApplicable,
);
err.span_suggestion_short( err.emit();
span, });
"remove these parentheses",
replace,
Applicability::MachineApplicable,
);
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,15 +675,17 @@ 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 {
let msg = match m { cx.struct_span_lint(UNUSED_ALLOCATION, e.span, |lint| {
adjustment::AutoBorrowMutability::Not => { let msg = match m {
"unnecessary allocation, use `&` instead" adjustment::AutoBorrowMutability::Not => {
} "unnecessary allocation, use `&` instead"
adjustment::AutoBorrowMutability::Mut { .. } => { }
"unnecessary allocation, use `&mut` instead" adjustment::AutoBorrowMutability::Mut { .. } => {
} "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.span = initial_diag.span.clone();
diag.buffer(&mut mbcx.errors_buffer);
},
); );
diag.message = initial_diag.styled_message().clone();
diag.span = initial_diag.span.clone();
initial_diag.cancel(); initial_diag.cancel();
diag.buffer(&mut mbcx.errors_buffer);
} }
// 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,20 +378,17 @@ fn do_mir_borrowck<'a, 'tcx>(
continue; continue;
} }
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span); tcx.struct_span_lint_hir(UNUSED_MUT, lint_root, span, |lint| {
tcx.struct_span_lint_hir( let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
UNUSED_MUT, lint.build("variable does not need to be mutable")
lint_root, .span_suggestion_short(
span, mut_span,
"variable does not need to be mutable", "remove this `mut`",
) String::new(),
.span_suggestion_short( Applicability::MachineApplicable,
mut_span, )
"remove this `mut`", .emit();
String::new(), })
Applicability::MachineApplicable,
)
.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,18 +516,20 @@ 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));
// FIXME: when we make this a hard error, this should have its tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| {
// own error code. // FIXME: when we make this a hard error, this should have its
let message = if tcx.generics_of(def_id).own_requires_monomorphization() { // own error code.
"`#[derive]` can't be used on a `#[repr(packed)]` struct with \ let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
type or const parameters (error E0133)" "`#[derive]` can't be used on a `#[repr(packed)]` struct with \
.to_string() type or const parameters (error E0133)"
} else { .to_string()
"`#[derive]` can't be used on a `#[repr(packed)]` struct that \ } else {
does not derive Copy (error E0133)" "`#[derive]` can't be used on a `#[repr(packed)]` struct that \
.to_string() does not derive Copy (error E0133)"
}; .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,16 +560,18 @@ 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));
let msg = "unnecessary `unsafe` block"; tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
let mut db = tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, msg); let msg = "unnecessary `unsafe` block";
db.span_label(span, msg); let mut db = lint.build(msg);
if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) { db.span_label(span, msg);
db.span_label( if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
tcx.sess.source_map().def_span(tcx.hir().span(id)), db.span_label(
format!("because it's nested under this `unsafe` {}", kind), tcx.sess.source_map().def_span(tcx.hir().span(id)),
); format!("because it's nested under this `unsafe` {}", kind),
} );
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| {
"{} is unsafe and requires unsafe function or block (error E0133)", lint.build(&format!(
description "{} is unsafe and requires unsafe function or block (error E0133)",
), description
))
.note(&details.as_str())
.emit()
},
) )
.note(&details.as_str())
.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,35 +914,49 @@ 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");
let msg = match msg { self.tcx.struct_span_lint_hir(
PanicInfo::Overflow(_) ::rustc::lint::builtin::CONST_ERR,
| PanicInfo::OverflowNeg hir_id,
| PanicInfo::DivisionByZero span,
| PanicInfo::RemainderByZero => msg.description().to_owned(), |lint| {
PanicInfo::BoundsCheck { ref len, ref index } => { let msg = match msg {
let len = PanicInfo::Overflow(_)
self.eval_operand(len, source_info).expect("len must be const"); | PanicInfo::OverflowNeg
let len = match self.ecx.read_scalar(len) { | PanicInfo::DivisionByZero
Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data, | PanicInfo::RemainderByZero => msg.description().to_owned(),
other => bug!("const len not primitive: {:?}", other), PanicInfo::BoundsCheck { ref len, ref index } => {
let len = self
.eval_operand(len, source_info)
.expect("len must be const");
let len = match self.ecx.read_scalar(len) {
Ok(ScalarMaybeUndef::Scalar(Scalar::Raw {
data,
..
})) => data,
other => bug!("const len not primitive: {:?}", other),
};
let index = self
.eval_operand(index, source_info)
.expect("index must be const");
let index = match self.ecx.read_scalar(index) {
Ok(ScalarMaybeUndef::Scalar(Scalar::Raw {
data,
..
})) => data,
other => bug!("const index not primitive: {:?}", other),
};
format!(
"index out of bounds: \
the len is {} but the index is {}",
len, index,
)
}
// Need proper const propagator for these
_ => return,
}; };
let index = self lint.build(&msg).emit()
.eval_operand(index, source_info) },
.expect("index must be const"); );
let index = match self.ecx.read_scalar(index) {
Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data,
other => bug!("const index not primitive: {:?}", other),
};
format!(
"index out of bounds: \
the len is {} but the index is {}",
len, index,
)
}
// Need proper const propagator for these
_ => return,
};
self.tcx.lint_hir(::rustc::lint::builtin::CONST_ERR, hir_id, span, &msg);
} 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,24 +2235,26 @@ 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");
for int_range in overlaps {
// Use the real type for user display of the ranges:
err.span_label(
int_range.span,
&format!(
"this range overlaps on `{}`",
IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx),
),
);
}
err.emit();
},
); );
err.span_label(ctor_range.span, "overlapping patterns");
for int_range in overlaps {
// Use the real type for user display of the ranges:
err.span_label(
int_range.span,
&format!(
"this range overlaps on `{}`",
IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx),
),
);
}
err.emit();
} }
} }

View file

@ -286,26 +286,27 @@ 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 BINDINGS_WITH_VARIANT_NAME,
.struct_span_lint_hir( p.hir_id,
BINDINGS_WITH_VARIANT_NAME, p.span,
p.hir_id, |lint| {
p.span, let ty_path = cx.tcx.def_path_str(edef.did);
&format!( 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, "to match on the variant, qualify the path",
"to match on the variant, qualify the path", format!("{}::{}", ty_path, ident),
format!("{}::{}", ty_path, ident), 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| {
if let Some(catchall) = catchall { let mut err = lint.build("unreachable pattern");
// We had a catchall pattern, hint at that. if let Some(catchall) = catchall {
err.span_label(span, "unreachable pattern"); // We had a catchall pattern, hint at that.
err.span_label(catchall, "matches any value"); err.span_label(span, "unreachable pattern");
} 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) {
let msg = match source { tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| {
hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern", let msg = match source {
hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern", hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
_ => bug!(), hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
}; _ => 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!(
"to use a constant of type `{}` in a pattern, \ let make_msg = || -> String {
`{}` must be annotated with `#[derive(PartialEq, Eq)]`", format!(
path, path, "to use a constant of type `{}` in a pattern, \
); `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path,
)
};
// double-check there even *is* a semantic `PartialEq` to dispatch to. // 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,18 +124,15 @@ 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, db.span_label(sp, "cannot return without recursing");
sp, // offer some help to the programmer.
"function cannot return without recursing", for location in &self_call_locations {
); db.span_label(location.span, "recursive call site");
db.span_label(sp, "cannot return without recursing"); }
// offer some help to the programmer. db.help("a `loop` may express intention better if this is on purpose");
for location in &self_call_locations { db.emit();
db.span_label(location.span, "recursive call site"); });
}
db.help("a `loop` may express intention better if this is on purpose");
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,23 +102,19 @@ 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, .warn(
hir_id, "this was previously accepted by the compiler but is \
attr.span, being phased out; it will become a hard error in \
"`#[inline]` is ignored on constants", a future release!",
) )
.warn( .note(
"this was previously accepted by the compiler but is \ "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
being phased out; it will become a hard error in \ for more information",
a future release!", )
) .emit();
.note( });
"see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
for more information",
)
.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>>(), |lint| {
"conflicting representation hints", 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,45 +1521,47 @@ 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 lint::builtin::UNUSED_VARIABLES,
.struct_span_lint_hir( hir_id,
lint::builtin::UNUSED_VARIABLES, spans,
hir_id, |lint| {
spans, lint.build(&format!("variable `{}` is assigned to, but never used", name))
&format!("variable `{}` is assigned to, but never used", name), .note(&format!("consider using `_{}` instead", name))
) .emit();
.note(&format!("consider using `_{}` instead", name)) },
.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 let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
// Handle `ref` and `ref mut`.
let spans = spans
.iter()
.map(|_span| (pat.span, format!("{}: _", name)))
.collect();
err.multipart_suggestion(
"try ignoring the field",
spans,
Applicability::MachineApplicable,
);
}
} else {
err.multipart_suggestion(
"consider prefixing with an underscore",
spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
Applicability::MachineApplicable,
);
}
err.emit()
},
); );
if self.ir.variable_is_shorthand(var) {
if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
// Handle `ref` and `ref mut`.
let spans =
spans.iter().map(|_span| (pat.span, format!("{}: _", name))).collect();
err.multipart_suggestion(
"try ignoring the field",
spans,
Applicability::MachineApplicable,
);
}
} else {
err.multipart_suggestion(
"consider prefixing with an underscore",
spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
Applicability::MachineApplicable,
);
}
err.emit()
} }
} }
} }
@ -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 lint::builtin::UNUSED_ASSIGNMENTS,
.struct_span_lint_hir( hir_id,
lint::builtin::UNUSED_ASSIGNMENTS, spans,
hir_id, |lint| {
spans, lint.build(&format!("value passed to `{}` is never read", name))
&format!("value passed to `{}` is never read", name), .help("maybe it is overwritten before being read?")
) .emit();
.help("maybe it is overwritten before being read?") },
.emit(); )
} else { } else {
self.ir self.ir.tcx.struct_span_lint_hir(
.tcx lint::builtin::UNUSED_ASSIGNMENTS,
.struct_span_lint_hir( hir_id,
lint::builtin::UNUSED_ASSIGNMENTS, spans,
hir_id, |lint| {
spans, lint.build(&format!("value assigned to `{}` is never read", name))
&format!("value assigned to `{}` is never read", name), .help("maybe it is overwritten before being read?")
) .emit();
.help("maybe it is overwritten before being read?") },
.emit(); )
} }
} }
} }

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| {
"{} `{}` from private dependency '{}' in public \ lint.build(&format!(
interface", "{} `{}` from private dependency '{}' in public \
kind, interface",
descr, kind,
self.tcx.crate_name(def_id.krate) descr,
), 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,22 +1575,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
} }
} }
let mut err = self.tcx.struct_span_lint_hir( self.tcx.struct_span_lint_hir(
lint::builtin::SINGLE_USE_LIFETIMES, 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 {
// spans are the same for in-band lifetime declarations
err.span_label(span, "this lifetime is only used here");
} else {
err.span_label(span, "this lifetime...");
err.span_label(lifetime.span, "...is used only here");
}
self.suggest_eliding_single_use_lifetime(
&mut err, def_id, lifetime,
);
err.emit();
},
); );
if span == lifetime.span {
// spans are the same for in-band lifetime declarations
err.span_label(span, "this lifetime is only used here");
} else {
err.span_label(span, "this lifetime...");
err.span_label(lifetime.span, "...is used only here");
}
self.suggest_eliding_single_use_lifetime(&mut err, def_id, lifetime);
err.emit();
} }
} }
Some(LifetimeUseSet::Many) => { Some(LifetimeUseSet::Many) => {
@ -1610,26 +1616,32 @@ 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
if let Some(parent_def_id) = self.tcx.parent(def_id) { .build(&format!("lifetime parameter `{}` never used", name));
if let Some(generics) = self.tcx.hir().get_generics(parent_def_id) { if let Some(parent_def_id) = self.tcx.parent(def_id) {
let unused_lt_span = self.lifetime_deletion_span(name, generics); if let Some(generics) =
if let Some(span) = unused_lt_span { self.tcx.hir().get_generics(parent_def_id)
err.span_suggestion( {
span, let unused_lt_span =
"elide the unused lifetime", self.lifetime_deletion_span(name, generics);
String::new(), if let Some(span) = unused_lt_span {
Applicability::MachineApplicable, err.span_suggestion(
); span,
"elide the unused lifetime",
String::new(),
Applicability::MachineApplicable,
);
}
}
} }
} 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,34 +2216,31 @@ 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, let mut could_refer_to = |kind: DefKind, def_id, also| {
span, let note_msg = format!(
"ambiguous associated item", "`{}` could{} refer to the {} defined here",
); assoc_ident,
also,
kind.descr(def_id)
);
err.span_note(tcx.def_span(def_id), &note_msg);
};
let mut could_refer_to = |kind: DefKind, def_id, also| { could_refer_to(DefKind::Variant, variant_def_id, "");
let note_msg = format!( could_refer_to(kind, item.def_id, " also");
"`{}` could{} refer to the {} defined here",
assoc_ident, err.span_suggestion(
also, span,
kind.descr(def_id) "use fully-qualified syntax",
format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident),
Applicability::MachineApplicable,
); );
err.span_note(tcx.def_span(def_id), &note_msg);
};
could_refer_to(DefKind::Variant, variant_def_id, "");
could_refer_to(kind, item.def_id, " also");
err.span_suggestion( err.emit();
span, });
"use fully-qualified syntax",
format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident),
Applicability::MachineApplicable,
)
.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 ))
)); .emit();
err.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,33 +1280,36 @@ 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,
"a method with this name may be added to the standard library in the future", |lint| {
); let mut diag = lint.build(
"a method with this name may be added to the standard library in the future",
// FIXME: This should be a `span_suggestion` instead of `help` );
// However `self.span` only // FIXME: This should be a `span_suggestion` instead of `help`
// highlights the method name, so we can't use it. Also consider reusing the code from // However `self.span` only
// `report_method_error()`. // highlights the method name, so we can't use it. Also consider reusing the code from
diag.help(&format!( // `report_method_error()`.
"call with fully qualified syntax `{}(...)` to keep using the current method",
self.tcx.def_path_str(stable_pick.item.def_id),
));
if nightly_options::is_nightly_build() {
for (candidate, feature) in unstable_candidates {
diag.help(&format!( diag.help(&format!(
"add `#![feature({})]` to the crate attributes to enable `{}`", "call with fully qualified syntax `{}(...)` to keep using the current method",
feature, self.tcx.def_path_str(stable_pick.item.def_id),
self.tcx.def_path_str(candidate.item.def_id),
)); ));
}
}
diag.emit(); if nightly_options::is_nightly_build() {
for (candidate, feature) in unstable_candidates {
diag.help(&format!(
"add `#![feature({})]` to the crate attributes to enable `{}`",
feature,
self.tcx.def_path_str(candidate.item.def_id),
));
}
}
diag.emit();
},
);
} }
fn select_trait_candidate( 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);
let msg = format!("unreachable {}", kind); self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
self.tcx() let msg = format!("unreachable {}", kind);
.struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, &msg) lint.build(&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;
} }
let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { self.tcx.struct_span_lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, |lint| {
format!("unused import: `{}`", snippet) let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
} else { format!("unused import: `{}`", snippet)
"unused import".to_owned() } else {
}; "unused import".to_owned()
self.tcx.lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, &msg); };
lint.build(&msg).emit();
});
} }
} }
@ -121,23 +123,23 @@ 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)
let span_with_attrs = tcx
.get_attrs(extern_crate.def_id)
.iter()
.map(|attr| attr.span)
.fold(span, |acc, attr_span| acc.to(attr_span));
// Removal suggestion span needs to include attributes (Issue #54400) lint.build("unused extern crate")
let span_with_attrs = tcx .span_suggestion_short(
.get_attrs(extern_crate.def_id) span_with_attrs,
.iter() "remove it",
.map(|attr| attr.span) String::new(),
.fold(span, |acc, attr_span| acc.to(attr_span)); Applicability::MachineApplicable,
)
tcx.struct_span_lint_hir(lint, id, span, msg) .emit();
.span_suggestion_short( });
span_with_attrs,
"remove it",
String::new(),
Applicability::MachineApplicable,
)
.emit();
continue; continue;
} }
} }
@ -168,23 +170,26 @@ 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.
let base_replacement = match extern_crate.orig_name {
Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name),
None => format!("use {};", item.ident.name),
};
// Otherwise, we can convert it into a `use` of some kind. let replacement = visibility_qualified(&item.vis, base_replacement);
let msg = "`extern crate` is not idiomatic in the new edition"; let msg = "`extern crate` is not idiomatic in the new edition";
let help = format!("convert it to a `{}`", visibility_qualified(&item.vis, "use")); let help = format!("convert it to a `{}`", visibility_qualified(&item.vis, "use"));
let base_replacement = match extern_crate.orig_name {
Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), lint.build(msg)
None => format!("use {};", item.ident.name), .span_suggestion_short(
}; extern_crate.span,
let replacement = visibility_qualified(&item.vis, base_replacement); &help,
tcx.struct_span_lint_hir(lint, id, extern_crate.span, msg) replacement,
.span_suggestion_short( Applicability::MachineApplicable,
extern_crate.span, )
&help, .emit();
replacement, })
Applicability::MachineApplicable,
)
.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| {
"defaults for type parameters are only allowed in \ lint.build(&format!(
"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")
.emit();
},
) )
.span_note(inline_span, "inlining requested here")
.emit();
} }
} }
} }

View file

@ -669,39 +669,43 @@ 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.span_label(sp, short_err_msg); diag.set_span(sp);
} else { diag.span_label(sp, short_err_msg);
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah } else {
// ^ ~~~~ // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
// | link_range // ^ ~~~~
// last_new_line_offset // | link_range
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); // last_new_line_offset
let line = dox[last_new_line_offset..].lines().next().unwrap_or(""); let last_new_line_offset =
dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
// Print the line containing the `link_range` and manually mark it with '^'s. // Print the line containing the `link_range` and manually mark it with '^'s.
diag.note(&format!( diag.note(&format!(
"the link appears in this line:\n\n{line}\n\ "the link appears in this line:\n\n{line}\n\
{indicator: <before$}{indicator:^<found$}", {indicator: <before$}{indicator:^<found$}",
line = line, line = line,
indicator = "", indicator = "",
before = link_range.start - last_new_line_offset, before = link_range.start - last_new_line_offset,
found = link_range.len(), found = link_range.len(),
)); ));
} }
}; };
if let Some(help_msg) = help_msg { if let Some(help_msg) = help_msg {
diag.help(help_msg); diag.help(help_msg);
} }
diag.emit(); diag.emit();
},
);
} }
/// Reports a resolution failure diagnostic. /// Reports a resolution failure diagnostic.
@ -766,105 +770,108 @@ 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());
let mut msg = format!("`{}` is ", path_str); cx.tcx.struct_span_lint_hir(
let candidates = [TypeNS, ValueNS, MacroNS]
.iter()
.filter_map(|&ns| candidates[ns].map(|res| (res, ns)))
.collect::<Vec<_>>();
match candidates.as_slice() {
[(first_def, _), (second_def, _)] => {
msg += &format!(
"both {} {} and {} {}",
first_def.article(),
first_def.descr(),
second_def.article(),
second_def.descr(),
);
}
_ => {
let mut candidates = candidates.iter().peekable();
while let Some((res, _)) = candidates.next() {
if candidates.peek().is_some() {
msg += &format!("{} {}, ", res.article(), res.descr());
} else {
msg += &format!("and {} {}", res.article(), res.descr());
}
}
}
}
let mut diag = cx.tcx.struct_span_lint_hir(
lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE, lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
hir_id, hir_id,
sp, sp,
&msg, |lint| {
); let mut msg = format!("`{}` is ", path_str);
if let Some(link_range) = link_range { let candidates = [TypeNS, ValueNS, MacroNS]
if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) { .iter()
diag.set_span(sp); .filter_map(|&ns| candidates[ns].map(|res| (res, ns)))
diag.span_label(sp, "ambiguous link"); .collect::<Vec<_>>();
match candidates.as_slice() {
[(first_def, _), (second_def, _)] => {
msg += &format!(
"both {} {} and {} {}",
first_def.article(),
first_def.descr(),
second_def.article(),
second_def.descr(),
);
}
_ => {
let mut candidates = candidates.iter().peekable();
while let Some((res, _)) = candidates.next() {
if candidates.peek().is_some() {
msg += &format!("{} {}, ", res.article(), res.descr());
} else {
msg += &format!("and {} {}", res.article(), res.descr());
}
}
}
}
for (res, ns) in candidates { let mut diag = lint.build(&msg);
let (action, mut suggestion) = match res {
Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => { if let Some(link_range) = link_range {
("add parentheses", format!("{}()", path_str)) if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs)
} {
Res::Def(DefKind::Macro(..), _) => { diag.set_span(sp);
("add an exclamation mark", format!("{}!", path_str)) diag.span_label(sp, "ambiguous link");
}
_ => { for (res, ns) in candidates {
let type_ = match (res, ns) { let (action, mut suggestion) = match res {
(Res::Def(DefKind::Const, _), _) => "const", Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => {
(Res::Def(DefKind::Static, _), _) => "static", ("add parentheses", format!("{}()", path_str))
(Res::Def(DefKind::Struct, _), _) => "struct", }
(Res::Def(DefKind::Enum, _), _) => "enum", Res::Def(DefKind::Macro(..), _) => {
(Res::Def(DefKind::Union, _), _) => "union", ("add an exclamation mark", format!("{}!", path_str))
(Res::Def(DefKind::Trait, _), _) => "trait", }
(Res::Def(DefKind::Mod, _), _) => "module", _ => {
(_, TypeNS) => "type", let type_ = match (res, ns) {
(_, ValueNS) => "value", (Res::Def(DefKind::Const, _), _) => "const",
(_, MacroNS) => "macro", (Res::Def(DefKind::Static, _), _) => "static",
(Res::Def(DefKind::Struct, _), _) => "struct",
(Res::Def(DefKind::Enum, _), _) => "enum",
(Res::Def(DefKind::Union, _), _) => "union",
(Res::Def(DefKind::Trait, _), _) => "trait",
(Res::Def(DefKind::Mod, _), _) => "module",
(_, TypeNS) => "type",
(_, ValueNS) => "value",
(_, MacroNS) => "macro",
};
// FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
("prefix with the item type", format!("{}@{}", type_, path_str))
}
}; };
// FIXME: if this is an implied shortcut link, it's bad style to suggest `@` if dox.bytes().nth(link_range.start) == Some(b'`') {
("prefix with the item type", format!("{}@{}", type_, path_str)) suggestion = format!("`{}`", suggestion);
}
diag.span_suggestion(
sp,
&format!("to link to the {}, {}", res.descr(), action),
suggestion,
Applicability::MaybeIncorrect,
);
} }
}; } else {
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
// ^ ~~~~
// | link_range
// last_new_line_offset
let last_new_line_offset =
dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
if dox.bytes().nth(link_range.start) == Some(b'`') { // Print the line containing the `link_range` and manually mark it with '^'s.
suggestion = format!("`{}`", suggestion); diag.note(&format!(
"the link appears in this line:\n\n{line}\n\
{indicator: <before$}{indicator:^<found$}",
line = line,
indicator = "",
before = link_range.start - last_new_line_offset,
found = link_range.len(),
));
} }
diag.span_suggestion(
sp,
&format!("to link to the {}, {}", res.descr(), action),
suggestion,
Applicability::MaybeIncorrect,
);
} }
} else { diag.emit();
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah },
// ^ ~~~~ );
// | link_range
// last_new_line_offset
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
// Print the line containing the `link_range` and manually mark it with '^'s.
diag.note(&format!(
"the link appears in this line:\n\n{line}\n\
{indicator: <before$}{indicator:^<found$}",
line = line,
indicator = "",
before = link_range.start - last_new_line_offset,
found = link_range.len(),
));
}
}
diag.emit();
} }
/// 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(
cx: &LateContext<'a, 'tcx>, &mut self,
_: intravisit::FnKind<'tcx>, cx: &LateContext<'a, 'tcx>,
_: &'tcx hir::FnDecl, _: intravisit::FnKind<'tcx>,
_: &'tcx hir::Body, _: &'tcx hir::FnDecl,
span: source_map::Span, _: &'tcx hir::Body,
id: hir::HirId) { span: source_map::Span,
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() {}