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>,
) {
self.construct_generic_bound_failure(region_scope_tree, span, origin, bound_kind, sub)
.emit()
.emit();
}
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>(
sess: &'a Session,
pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a>);
impl<'a> LintDiagnosticBuilder<'a> {
/// Return the inner DiagnosticBuilder, first setting the primary message to `msg`.
pub fn build(mut self, msg: &str) -> DiagnosticBuilder<'a> {
self.0.set_primary_message(msg);
self.0
}
/// Create a LintDiagnosticBuilder from some existing DiagnosticBuilder.
pub fn new(err: DiagnosticBuilder<'a>) -> LintDiagnosticBuilder<'a> {
LintDiagnosticBuilder(err)
}
}
pub fn struct_lint_level<'s, 'd>(
sess: &'s Session,
lint: &'static Lint,
level: Level,
src: LintSource,
span: Option<MultiSpan>,
msg: &str,
) -> DiagnosticBuilder<'a> {
let mut err = match (level, span) {
(Level::Allow, _) => return sess.diagnostic().struct_dummy(),
(Level::Warn, Some(span)) => sess.struct_span_warn(span, msg),
(Level::Warn, None) => sess.struct_warn(msg),
(Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => sess.struct_span_err(span, msg),
(Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(msg),
};
// Check for future incompatibility lints and issue a stronger warning.
let lint_id = LintId::of(lint);
let future_incompatible = lint.future_incompatible;
// If this code originates in a foreign macro, aka something that this crate
// did not itself author, then it's likely that there's nothing this crate
// can do about it. We probably want to skip the lint entirely.
if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
// Any suggestions made here are likely to be incorrect, so anything we
// emit shouldn't be automatically fixed by rustfix.
err.allow_suggestions(false);
// If this is a future incompatible lint it'll become a hard error, so
// we have to emit *something*. Also allow lints to whitelist themselves
// on a case-by-case basis for emission in a foreign macro.
if future_incompatible.is_none() && !lint.report_in_external_macro {
err.cancel();
// Don't continue further, since we don't want to have
// `diag_span_note_once` called for a diagnostic that isn't emitted.
return err;
}
}
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
),
);
decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>) + 'd,
) {
// Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to
// the "real" work.
fn struct_lint_level_impl(
sess: &'s Session,
lint: &'static Lint,
level: Level,
src: LintSource,
span: Option<MultiSpan>,
decorate: Box<dyn for<'b> FnOnce(LintDiagnosticBuilder<'b>) + 'd>,
) {
let mut err = match (level, span) {
(Level::Allow, _) => {
return;
}
}
LintSource::Node(lint_attr_name, src, reason) => {
if let Some(rationale) = reason {
err.note(&rationale.as_str());
(Level::Warn, Some(span)) => sess.struct_span_warn(span, ""),
(Level::Warn, None) => sess.struct_warn(""),
(Level::Deny, Some(span)) | (Level::Forbid, Some(span)) => {
sess.struct_span_err(span, "")
}
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)
(Level::Deny, None) | (Level::Forbid, None) => sess.struct_err(""),
};
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.

View File

@ -222,11 +222,13 @@ fn late_report_deprecation(
return;
}
let mut diag = tcx.struct_span_lint_hir(lint, hir_id, span, message);
if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
deprecation_suggestion(&mut diag, suggestion, span);
}
diag.emit();
tcx.struct_span_lint_hir(lint, hir_id, span, |lint| {
let mut diag = lint.build(message);
if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
deprecation_suggestion(&mut diag, suggestion, span);
}
diag.emit()
});
if hir_id == hir::DUMMY_HIR_ID {
span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id);
}
@ -387,8 +389,11 @@ impl<'tcx> TyCtxt<'tcx> {
/// Additionally, this function will also check if the item is deprecated. If so, and `id` is
/// not `None`, a deprecated lint attached to `id` will be emitted.
pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) {
let soft_handler =
|lint, span, msg: &_| self.lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg);
let soft_handler = |lint, span, msg: &_| {
self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| {
lint.build(msg).emit()
})
};
match self.eval_stability(def_id, id, span) {
EvalResult::Allow => {}
EvalResult::Deny { feature, reason, issue, is_soft } => {

View File

@ -83,18 +83,15 @@ impl<'tcx> ConstEvalErr<'tcx> {
&self,
tcx: TyCtxtAt<'tcx>,
message: &str,
) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
self.struct_generic(tcx, message, None)
emit: impl FnOnce(DiagnosticBuilder<'_>),
) -> Result<(), ErrorHandled> {
self.struct_generic(tcx, message, emit, None)
}
pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
let err = self.struct_error(tcx, message);
match err {
Ok(mut err) => {
err.emit();
ErrorHandled::Reported
}
Err(err) => err,
match self.struct_error(tcx, message, |mut e| e.emit()) {
Ok(_) => ErrorHandled::Reported,
Err(x) => x,
}
}
@ -105,9 +102,11 @@ impl<'tcx> ConstEvalErr<'tcx> {
lint_root: hir::HirId,
span: Option<Span>,
) -> ErrorHandled {
let lint = self.struct_generic(tcx, message, Some(lint_root));
match lint {
Ok(mut lint) => {
match self.struct_generic(
tcx,
message,
|mut lint: DiagnosticBuilder<'_>| {
// Apply the span.
if let Some(span) = span {
let primary_spans = lint.span.primary_spans().to_vec();
// point at the actual error as the primary span
@ -121,18 +120,25 @@ impl<'tcx> ConstEvalErr<'tcx> {
}
}
lint.emit();
ErrorHandled::Reported
}
},
Some(lint_root),
) {
Ok(_) => ErrorHandled::Reported,
Err(err) => err,
}
}
/// Sets the message passed in via `message`, then adds the span labels for you, before applying
/// further modifications in `emit`. It's up to you to call emit(), stash(..), etc. within the
/// `emit` method. If you don't need to do any additional processing, just use
/// struct_generic.
fn struct_generic(
&self,
tcx: TyCtxtAt<'tcx>,
message: &str,
emit: impl FnOnce(DiagnosticBuilder<'_>),
lint_root: Option<hir::HirId>,
) -> Result<DiagnosticBuilder<'tcx>, ErrorHandled> {
) -> Result<(), ErrorHandled> {
let must_error = match self.error {
InterpError::MachineStop(_) => bug!("CTFE does not stop"),
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
@ -143,7 +149,22 @@ impl<'tcx> ConstEvalErr<'tcx> {
_ => false,
};
trace!("reporting const eval failure at {:?}", self.span);
let mut err = if let (Some(lint_root), false) = (lint_root, must_error) {
let add_span_labels = |err: &mut DiagnosticBuilder<'_>| {
if !must_error {
err.span_label(self.span, self.error.to_string());
}
// Skip the last, which is just the environment of the constant. The stacktrace
// is sometimes empty because we create "fake" eval contexts in CTFE to do work
// on constant values.
if self.stacktrace.len() > 0 {
for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
err.span_label(frame_info.call_site, frame_info.to_string());
}
}
};
if let (Some(lint_root), false) = (lint_root, must_error) {
let hir_id = self
.stacktrace
.iter()
@ -155,25 +176,22 @@ impl<'tcx> ConstEvalErr<'tcx> {
rustc_session::lint::builtin::CONST_ERR,
hir_id,
tcx.span,
message,
)
} else if must_error {
struct_error(tcx, &self.error.to_string())
|lint| {
let mut err = lint.build(message);
add_span_labels(&mut err);
emit(err);
},
);
} else {
struct_error(tcx, message)
let mut err = if must_error {
struct_error(tcx, &self.error.to_string())
} else {
struct_error(tcx, message)
};
add_span_labels(&mut err);
emit(err);
};
if !must_error {
err.span_label(self.span, self.error.to_string());
}
// Skip the last, which is just the environment of the constant. The stacktrace
// is sometimes empty because we create "fake" eval contexts in CTFE to do work
// on constant values.
if self.stacktrace.len() > 0 {
for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
err.span_label(frame_info.call_site, frame_info.to_string());
}
}
Ok(err)
Ok(())
}
}

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.
// It's also hard to get a use site span, so we use the method definition span.
let mut err = tcx.struct_span_lint_hir(
tcx.struct_span_lint_hir(
WHERE_CLAUSES_OBJECT_SAFETY,
hir::CRATE_HIR_ID,
*span,
&format!(
"the trait `{}` cannot be made into an object",
tcx.def_path_str(trait_def_id)
),
|lint| {
let mut err = lint.build(&format!(
"the trait `{}` cannot be made into an object",
tcx.def_path_str(trait_def_id)
));
let node = tcx.hir().get_if_local(trait_def_id);
let msg = if let Some(hir::Node::Item(item)) = node {
err.span_label(
item.ident.span,
"this trait cannot be made into an object...",
);
format!("...because {}", violation.error_msg())
} else {
format!(
"the trait cannot be made into an object because {}",
violation.error_msg()
)
};
err.span_label(*span, &msg);
match (node, violation.solution()) {
(Some(_), Some((note, None))) => {
err.help(&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
} else {
true

View File

@ -16,6 +16,7 @@ use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine};
use crate::ty::subst::{InternalSubsts, Subst, SubstsRef};
use crate::ty::{self, TyCtxt, TypeFoldable};
use rustc::lint::LintDiagnosticBuilder;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err;
use rustc_hir::def_id::DefId;
@ -317,22 +318,70 @@ pub(super) fn specialization_graph_provider(
};
if let Some(overlap) = overlap {
let msg = format!(
"conflicting implementations of trait `{}`{}:{}",
overlap.trait_desc,
overlap
.self_desc
.clone()
.map_or(String::new(), |ty| { format!(" for type `{}`", ty) }),
match used_to_be_allowed {
Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)",
_ => "",
}
);
let impl_span =
tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
let mut err = match used_to_be_allowed {
None => struct_span_err!(tcx.sess, impl_span, E0119, "{}", msg),
// Work to be done after we've built the DiagnosticBuilder. We have to define it
// now because the struct_lint methods don't return back the DiagnosticBuilder
// that's passed in.
let decorate = |err: LintDiagnosticBuilder<'_>| {
let msg = format!(
"conflicting implementations of trait `{}`{}:{}",
overlap.trait_desc,
overlap
.self_desc
.clone()
.map_or(String::new(), |ty| { format!(" for type `{}`", ty) }),
match used_to_be_allowed {
Some(FutureCompatOverlapErrorKind::Issue33140) => " (E0119)",
_ => "",
}
);
let mut err = err.build(&msg);
match tcx.span_of_impl(overlap.with_impl) {
Ok(span) => {
err.span_label(
tcx.sess.source_map().def_span(span),
"first implementation here".to_string(),
);
err.span_label(
impl_span,
format!(
"conflicting implementation{}",
overlap
.self_desc
.map_or(String::new(), |ty| format!(" for `{}`", ty))
),
);
}
Err(cname) => {
let msg = match to_pretty_impl_header(tcx, overlap.with_impl) {
Some(s) => format!(
"conflicting implementation in crate `{}`:\n- {}",
cname, s
),
None => format!("conflicting implementation in crate `{}`", cname),
};
err.note(&msg);
}
}
for cause in &overlap.intercrate_ambiguity_causes {
cause.add_intercrate_ambiguity_hint(&mut err);
}
if overlap.involves_placeholder {
coherence::add_placeholder_note(&mut err);
}
err.emit()
};
match used_to_be_allowed {
None => {
let err = struct_span_err!(tcx.sess, impl_span, E0119, "");
decorate(LintDiagnosticBuilder::new(err));
}
Some(kind) => {
let lint = match kind {
FutureCompatOverlapErrorKind::Issue33140 => {
@ -344,47 +393,10 @@ pub(super) fn specialization_graph_provider(
lint,
tcx.hir().as_local_hir_id(impl_def_id).unwrap(),
impl_span,
&msg,
decorate,
)
}
};
match tcx.span_of_impl(overlap.with_impl) {
Ok(span) => {
err.span_label(
tcx.sess.source_map().def_span(span),
"first implementation here".to_string(),
);
err.span_label(
impl_span,
format!(
"conflicting implementation{}",
overlap
.self_desc
.map_or(String::new(), |ty| format!(" for `{}`", ty))
),
);
}
Err(cname) => {
let msg = match to_pretty_impl_header(tcx, overlap.with_impl) {
Some(s) => {
format!("conflicting implementation in crate `{}`:\n- {}", cname, s)
}
None => format!("conflicting implementation in crate `{}`", cname),
};
err.note(&msg);
}
}
for cause in &overlap.intercrate_ambiguity_causes {
cause.add_intercrate_ambiguity_hint(&mut err);
}
if overlap.involves_placeholder {
coherence::add_placeholder_note(&mut err);
}
err.emit();
}
} else {
let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,13 +20,14 @@ use crate::levels::LintLevelsBuilder;
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
use rustc::hir::map::definitions::{DefPathData, DisambiguatedDefPathData};
use rustc::lint::add_elided_lifetime_in_path_suggestion;
use rustc::lint::LintDiagnosticBuilder;
use rustc::middle::privacy::AccessLevels;
use rustc::middle::stability;
use rustc::ty::layout::{LayoutError, LayoutOf, TyLayout};
use rustc::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_errors::{struct_span_err, Applicability};
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId};
use rustc_session::lint::BuiltinLintDiagnostics;
@ -473,150 +474,123 @@ pub trait LintContext: Sized {
fn sess(&self) -> &Session;
fn lints(&self) -> &LintStore;
fn lookup_and_emit<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: Option<S>, msg: &str) {
self.lookup(lint, span, msg).emit();
}
fn lookup_and_emit_with_diagnostics<S: Into<MultiSpan>>(
fn lookup_with_diagnostics(
&self,
lint: &'static Lint,
span: Option<S>,
msg: &str,
span: Option<impl Into<MultiSpan>>,
decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
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();
match diagnostic {
BuiltinLintDiagnostics::Normal => (),
BuiltinLintDiagnostics::BareTraitObject(span, is_global) => {
let (sugg, app) = match sess.source_map().span_to_snippet(span) {
Ok(s) if is_global => {
(format!("dyn ({})", s), Applicability::MachineApplicable)
}
Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable),
Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders),
};
db.span_suggestion(span, "use `dyn`", sugg, app);
}
BuiltinLintDiagnostics::AbsPathWithModule(span) => {
let (sugg, app) = match sess.source_map().span_to_snippet(span) {
Ok(ref s) => {
// FIXME(Manishearth) ideally the emitting code
// can tell us whether or not this is global
let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
// Now, set up surrounding context.
let sess = self.sess();
match diagnostic {
BuiltinLintDiagnostics::Normal => (),
BuiltinLintDiagnostics::BareTraitObject(span, is_global) => {
let (sugg, app) = match sess.source_map().span_to_snippet(span) {
Ok(s) if is_global => {
(format!("dyn ({})", s), Applicability::MachineApplicable)
}
Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable),
Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders),
};
db.span_suggestion(span, "use `dyn`", sugg, app);
}
BuiltinLintDiagnostics::AbsPathWithModule(span) => {
let (sugg, app) = match sess.source_map().span_to_snippet(span) {
Ok(ref s) => {
// FIXME(Manishearth) ideally the emitting code
// can tell us whether or not this is global
let opt_colon =
if s.trim_start().starts_with("::") { "" } else { "::" };
(format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
}
Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
};
db.span_suggestion(span, "use `crate`", sugg, app);
}
BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
db.span_label(
span,
"names from parent modules are not accessible without an explicit import",
);
}
BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
db.span_note(span_def, "the macro is defined here");
}
BuiltinLintDiagnostics::ElidedLifetimesInPaths(
n,
path_span,
incl_angl_brckt,
insertion_span,
anon_lts,
) => {
add_elided_lifetime_in_path_suggestion(
sess,
&mut db,
(format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
}
Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
};
db.span_suggestion(span, "use `crate`", sugg, app);
}
BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
db.span_label(
span,
"names from parent modules are not accessible without an explicit import",
);
}
BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(
span_def,
) => {
db.span_note(span_def, "the macro is defined here");
}
BuiltinLintDiagnostics::ElidedLifetimesInPaths(
n,
path_span,
incl_angl_brckt,
insertion_span,
anon_lts,
);
}
BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
db.span_suggestion(span, &note, sugg, Applicability::MaybeIncorrect);
}
BuiltinLintDiagnostics::UnusedImports(message, replaces) => {
if !replaces.is_empty() {
db.tool_only_multipart_suggestion(
&message,
replaces,
Applicability::MachineApplicable,
) => {
add_elided_lifetime_in_path_suggestion(
sess,
&mut db,
n,
path_span,
incl_angl_brckt,
insertion_span,
anon_lts,
);
}
}
BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
for (span, is_imported) in spans {
let introduced = if is_imported { "imported" } else { "defined" };
db.span_label(
span,
format!("the item `{}` is already {} here", ident, introduced),
);
BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
db.span_suggestion(span, &note, sugg, Applicability::MaybeIncorrect);
}
BuiltinLintDiagnostics::UnusedImports(message, replaces) => {
if !replaces.is_empty() {
db.tool_only_multipart_suggestion(
&message,
replaces,
Applicability::MachineApplicable,
);
}
}
BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
for (span, is_imported) in spans {
let introduced = if is_imported { "imported" } else { "defined" };
db.span_label(
span,
format!("the item `{}` is already {} here", ident, introduced),
);
}
}
BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
stability::deprecation_suggestion(&mut db, suggestion, span)
}
}
BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
stability::deprecation_suggestion(&mut db, suggestion, span)
}
}
db.emit();
// Rewrap `db`, and pass control to the user.
decorate(LintDiagnosticBuilder::new(db));
});
}
// 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>>(
&self,
lint: &'static Lint,
span: Option<S>,
msg: &str,
) -> 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);
}
decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
);
fn struct_span_lint<S: Into<MultiSpan>>(
&self,
lint: &'static Lint,
span: S,
msg: &str,
) -> DiagnosticBuilder<'_> {
self.lookup(lint, Some(span), msg)
}
/// Emit a lint and note at the appropriate level, for a particular span.
fn span_lint_note(
&self,
lint: &'static Lint,
span: Span,
msg: &str,
note_span: Span,
note: &str,
decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
) {
let mut err = self.lookup(lint, Some(span), msg);
if note_span == span {
err.note(note);
} else {
err.span_note(note_span, note);
}
err.emit();
self.lookup(lint, Some(span), decorate);
}
/// Emit a lint and help at the appropriate level, for a particular span.
fn span_lint_help(&self, lint: &'static Lint, span: Span, msg: &str, help: &str) {
let mut err = self.lookup(lint, Some(span), msg);
self.span_lint(lint, span, msg);
err.span_help(span, help);
err.emit();
}
/// Emit a lint at the appropriate level, with no associated span.
fn lint(&self, lint: &'static Lint, msg: &str) {
self.lookup_and_emit(lint, None as Option<Span>, msg);
fn lint(&self, lint: &'static Lint, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)) {
self.lookup(lint, None as Option<Span>, decorate);
}
}
@ -654,13 +628,13 @@ impl LintContext for LateContext<'_, '_> {
&self,
lint: &'static Lint,
span: Option<S>,
msg: &str,
) -> DiagnosticBuilder<'_> {
decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
) {
let hir_id = self.last_node_with_lint_attrs;
match span {
Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, msg),
None => self.tcx.struct_lint_node(lint, hir_id, msg),
Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, decorate),
None => self.tcx.struct_lint_node(lint, hir_id, decorate),
}
}
}
@ -681,9 +655,9 @@ impl LintContext for EarlyContext<'_> {
&self,
lint: &'static Lint,
span: Option<S>,
msg: &str,
) -> DiagnosticBuilder<'_> {
self.builder.struct_lint(lint, span.map(|s| s.into()), msg)
decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
) {
self.builder.struct_lint(lint, span.map(|s| s.into()), decorate)
}
}

View File

@ -37,11 +37,18 @@ struct EarlyContextAndPass<'a, T: EarlyLintPass> {
impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
fn check_id(&mut self, id: ast::NodeId) {
for early_lint in self.context.buffered.take(id) {
self.context.lookup_and_emit_with_diagnostics(
early_lint.lint_id.lint,
Some(early_lint.span.clone()),
&early_lint.msg,
early_lint.diagnostic,
let rustc_session::lint::BufferedEarlyLint {
span,
msg,
node_id: _,
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 {
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
if let Some(replace) = self.map.get(&ident.name) {
let msg = format!("Prefer {} over {}, it has better performance", replace, ident);
let mut db = cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, &msg);
db.span_suggestion(
ident.span,
"use",
replace.to_string(),
Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
);
db.note(&format!("a `use rustc_data_structures::fx::{}` may be necessary", replace))
.emit();
cx.struct_span_lint(DEFAULT_HASH_TYPES, ident.span, |lint| {
// FIXME: We can avoid a copy here. Would require us to take String instead of &str.
let msg = format!("Prefer {} over {}, it has better performance", replace, ident);
lint.build(&msg)
.span_suggestion(
ident.span,
"use",
replace.to_string(),
Applicability::MaybeIncorrect, // FxHashMap, ... needs another import
)
.note(&format!(
"a `use rustc_data_structures::fx::{}` may be necessary",
replace
))
.emit();
});
}
}
}
@ -85,14 +91,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
if let Some(last) = segments.last() {
let span = path.span.with_hi(last.ident.span.hi());
if lint_ty_kind_usage(cx, last) {
cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, "usage of `ty::TyKind::<kind>`")
.span_suggestion(
span,
"try using ty::<kind> directly",
"ty".to_string(),
Applicability::MaybeIncorrect, // ty maybe needs an import
)
.emit();
cx.struct_span_lint(USAGE_OF_TY_TYKIND, span, |lint| {
lint.build("usage of `ty::TyKind::<kind>`")
.span_suggestion(
span,
"try using ty::<kind> directly",
"ty".to_string(),
Applicability::MaybeIncorrect, // ty maybe needs an import
)
.emit();
})
}
}
}
@ -103,32 +111,28 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
if let QPath::Resolved(_, path) = qpath {
if let Some(last) = path.segments.iter().last() {
if lint_ty_kind_usage(cx, last) {
cx.struct_span_lint(
USAGE_OF_TY_TYKIND,
path.span,
"usage of `ty::TyKind`",
)
.help("try using `Ty` instead")
.emit();
cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| {
lint.build("usage of `ty::TyKind`")
.help("try using `Ty` instead")
.emit();
})
} else {
if ty.span.from_expansion() {
return;
}
if let Some(t) = is_ty_or_ty_ctxt(cx, ty) {
if path.segments.len() > 1 {
cx.struct_span_lint(
USAGE_OF_QUALIFIED_TY,
path.span,
&format!("usage of qualified `ty::{}`", t),
)
.span_suggestion(
path.span,
"try using it unqualified",
t,
// The import probably needs to be changed
Applicability::MaybeIncorrect,
)
.emit();
cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| {
lint.build(&format!("usage of qualified `ty::{}`", t))
.span_suggestion(
path.span,
"try using it unqualified",
t,
// The import probably needs to be changed
Applicability::MaybeIncorrect,
)
.emit();
})
}
}
}
@ -142,19 +146,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind {
}
}
if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) {
cx.struct_span_lint(
TY_PASS_BY_REFERENCE,
ty.span,
&format!("passing `{}` by reference", t),
)
.span_suggestion(
ty.span,
"try passing by value",
t,
// Changing type of function argument
Applicability::MaybeIncorrect,
)
.emit();
cx.struct_span_lint(TY_PASS_BY_REFERENCE, ty.span, |lint| {
lint.build(&format!("passing `{}` by reference", t))
.span_suggestion(
ty.span,
"try passing by value",
t,
// Changing type of function argument
Applicability::MaybeIncorrect,
)
.emit();
})
}
}
_ => {}
@ -234,10 +236,12 @@ impl EarlyLintPass for LintPassImpl {
cx.struct_span_lint(
LINT_PASS_IMPL_WITHOUT_MACRO,
lint_pass.path.span,
"implementing `LintPass` by hand",
|lint| {
lint.build("implementing `LintPass` by hand")
.help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
.emit();
},
)
.help("try using `declare_lint_pass!` or `impl_lint_pass!` instead")
.emit();
}
}
}

View File

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

View File

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

View File

@ -107,15 +107,17 @@ impl NonCamelCaseTypes {
let name = &ident.name.as_str();
if !is_camel_case(name) {
let msg = format!("{} `{}` should have an upper camel case name", sort, name);
cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, &msg)
.span_suggestion(
ident.span,
"convert the identifier to upper camel case",
to_camel_case(name),
Applicability::MaybeIncorrect,
)
.emit();
cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, |lint| {
let msg = format!("{} `{}` should have an upper camel case name", sort, name);
lint.build(&msg)
.span_suggestion(
ident.span,
"convert the identifier to upper camel case",
to_camel_case(name),
Applicability::MaybeIncorrect,
)
.emit()
})
}
}
}
@ -223,25 +225,25 @@ impl NonSnakeCase {
let name = &ident.name.as_str();
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);
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();
err.emit();
});
}
}
}
@ -386,19 +388,18 @@ declare_lint_pass!(NonUpperCaseGlobals => [NON_UPPER_CASE_GLOBALS]);
impl NonUpperCaseGlobals {
fn check_upper_case(cx: &LateContext<'_, '_>, sort: &str, ident: &Ident) {
let name = &ident.name.as_str();
if name.chars().any(|c| c.is_lowercase()) {
let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
let msg = format!("{} `{}` should have an upper case name", sort, name);
cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, &msg)
.span_suggestion(
ident.span,
"convert the identifier to upper case",
uc,
Applicability::MaybeIncorrect,
)
.emit();
cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, |lint| {
let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
lint.build(&format!("{} `{}` should have an upper case name", sort, name))
.span_suggestion(
ident.span,
"convert the identifier to upper case",
uc,
Applicability::MaybeIncorrect,
)
.emit();
})
}
}
}

View File

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

View File

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

View File

@ -104,16 +104,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
};
if let Some(must_use_op) = must_use_op {
cx.span_lint(
UNUSED_MUST_USE,
expr.span,
&format!("unused {} that must be used", must_use_op),
);
cx.struct_span_lint(UNUSED_MUST_USE, expr.span, |lint| {
lint.build(&format!("unused {} that must be used", must_use_op)).emit()
});
op_warned = true;
}
if !(type_permits_lack_of_use || fn_warned || op_warned) {
cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
cx.struct_span_lint(UNUSED_RESULTS, s.span, |lint| lint.build("unused result").emit());
}
// Returns whether an error has been emitted (and thus another does not need to be later).
@ -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).
// 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(
cx: &LateContext<'_, '_>,
def_id: DefId,
@ -213,18 +215,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
) -> bool {
for attr in cx.tcx.get_attrs(def_id).iter() {
if attr.check_name(sym::must_use) {
let msg = format!(
"unused {}`{}`{} that must be used",
descr_pre_path,
cx.tcx.def_path_str(def_id),
descr_post_path
);
let mut err = cx.struct_span_lint(UNUSED_MUST_USE, span, &msg);
// check for #[must_use = "..."]
if let Some(note) = attr.value_str() {
err.note(&note.as_str());
}
err.emit();
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
let msg = format!(
"unused {}`{}`{} that must be used",
descr_pre_path,
cx.tcx.def_path_str(def_id),
descr_post_path
);
let mut err = lint.build(&msg);
// check for #[must_use = "..."]
if let Some(note) = attr.value_str() {
err.note(&note.as_str());
}
err.emit();
});
return true;
}
}
@ -245,7 +249,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathStatements {
fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) {
if let hir::StmtKind::Semi(ref expr) = s.kind {
if let hir::ExprKind::Path(_) = expr.kind {
cx.span_lint(PATH_STATEMENTS, s.span, "path statement with no effect");
cx.struct_span_lint(PATH_STATEMENTS, s.span, |lint| {
lint.build("path statement with no effect").emit()
});
}
}
}
@ -286,17 +292,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
if !attr::is_used(attr) {
debug!("emitting warning for: {:?}", attr);
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| {
lint.build("unused attribute").emit()
});
// Is it a builtin attribute that must be used at the crate level?
if attr_info.map_or(false, |(_, ty, ..)| ty == &AttributeType::CrateLevel) {
let msg = match attr.style {
ast::AttrStyle::Outer => {
"crate-level attribute should be an inner attribute: add an exclamation \
mark: `#![foo]`"
}
ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
};
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| {
let msg = match attr.style {
ast::AttrStyle::Outer => {
"crate-level attribute should be an inner attribute: add an exclamation \
mark: `#![foo]`"
}
ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
};
lint.build(msg).emit()
});
}
} else {
debug!("Attr was used: {:?}", attr);
@ -406,52 +416,54 @@ impl UnusedParens {
msg: &str,
keep_space: (bool, bool),
) {
let span_msg = format!("unnecessary parentheses around {}", msg);
let mut err = cx.struct_span_lint(UNUSED_PARENS, span, &span_msg);
let mut ate_left_paren = false;
let mut ate_right_paren = false;
let parens_removed = pattern.trim_matches(|c| match c {
'(' => {
if ate_left_paren {
false
} else {
ate_left_paren = true;
true
cx.struct_span_lint(UNUSED_PARENS, span, |lint| {
let span_msg = format!("unnecessary parentheses around {}", msg);
let mut err = lint.build(&span_msg);
let mut ate_left_paren = false;
let mut ate_right_paren = false;
let parens_removed = pattern.trim_matches(|c| match c {
'(' => {
if ate_left_paren {
false
} else {
ate_left_paren = true;
true
}
}
}
')' => {
if ate_right_paren {
false
} else {
ate_right_paren = true;
true
')' => {
if ate_right_paren {
false
} else {
ate_right_paren = true;
true
}
}
}
_ => false,
});
_ => false,
});
let replace = {
let mut replace = if keep_space.0 {
let mut s = String::from(" ");
s.push_str(parens_removed);
s
} else {
String::from(parens_removed)
let replace = {
let mut replace = if keep_space.0 {
let mut s = String::from(" ");
s.push_str(parens_removed);
s
} else {
String::from(parens_removed)
};
if keep_space.1 {
replace.push(' ');
}
replace
};
if keep_space.1 {
replace.push(' ');
}
replace
};
err.span_suggestion_short(
span,
"remove these parentheses",
replace,
Applicability::MachineApplicable,
);
err.emit();
err.span_suggestion_short(
span,
"remove these parentheses",
replace,
Applicability::MachineApplicable,
);
err.emit();
});
}
}
@ -631,8 +643,9 @@ impl UnusedImportBraces {
ast::UseTreeKind::Nested(_) => return,
};
let msg = format!("braces around {} is unnecessary", node_name);
cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg);
cx.struct_span_lint(UNUSED_IMPORT_BRACES, item.span, |lint| {
lint.build(&format!("braces around {} is unnecessary", node_name)).emit()
});
}
}
}
@ -662,15 +675,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
for adj in cx.tables.expr_adjustments(e) {
if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
let msg = match m {
adjustment::AutoBorrowMutability::Not => {
"unnecessary allocation, use `&` instead"
}
adjustment::AutoBorrowMutability::Mut { .. } => {
"unnecessary allocation, use `&mut` instead"
}
};
cx.span_lint(UNUSED_ALLOCATION, e.span, msg);
cx.struct_span_lint(UNUSED_ALLOCATION, e.span, |lint| {
let msg = match m {
adjustment::AutoBorrowMutability::Not => {
"unnecessary allocation, use `&` instead"
}
adjustment::AutoBorrowMutability::Mut { .. } => {
"unnecessary allocation, use `&mut` instead"
}
};
lint.build(msg).emit()
});
}
}
}

View File

@ -319,18 +319,20 @@ fn do_mir_borrowck<'a, 'tcx>(
};
// Span and message don't matter; we overwrite them below anyway
let mut diag = mbcx.infcx.tcx.struct_span_lint_hir(
mbcx.infcx.tcx.struct_span_lint_hir(
MUTABLE_BORROW_RESERVATION_CONFLICT,
lint_root,
DUMMY_SP,
"",
|lint| {
let mut diag = lint.build("");
diag.message = initial_diag.styled_message().clone();
diag.span = initial_diag.span.clone();
diag.buffer(&mut mbcx.errors_buffer);
},
);
diag.message = initial_diag.styled_message().clone();
diag.span = initial_diag.span.clone();
initial_diag.cancel();
diag.buffer(&mut mbcx.errors_buffer);
}
// 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;
}
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
tcx.struct_span_lint_hir(
UNUSED_MUT,
lint_root,
span,
"variable does not need to be mutable",
)
.span_suggestion_short(
mut_span,
"remove this `mut`",
String::new(),
Applicability::MachineApplicable,
)
.emit();
tcx.struct_span_lint_hir(UNUSED_MUT, lint_root, span, |lint| {
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
lint.build("variable does not need to be mutable")
.span_suggestion_short(
mut_span,
"remove this `mut`",
String::new(),
Applicability::MachineApplicable,
)
.emit();
})
}
// Buffer any move errors that we collected and de-duplicated.

View File

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

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.
if let err_unsup!(ValidationFailure(_)) = error.kind {
let err = crate::const_eval::error_to_const_error(&ecx, error);
match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
Ok(mut diag) => {
match err.struct_error(
ecx.tcx,
"it is undefined behavior to use this value",
|mut diag| {
diag.note(crate::const_eval::note_on_undefined_behavior_error());
diag.emit();
}
Err(ErrorHandled::TooGeneric) | Err(ErrorHandled::Reported) => {}
},
) {
Ok(()) | Err(ErrorHandled::TooGeneric) | Err(ErrorHandled::Reported) => {}
}
}
}

View File

@ -516,18 +516,20 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: DefId) {
.as_local_hir_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
// own error code.
let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
"`#[derive]` can't be used on a `#[repr(packed)]` struct with \
type or const parameters (error E0133)"
.to_string()
} else {
"`#[derive]` can't be used on a `#[repr(packed)]` struct that \
does not derive Copy (error E0133)"
.to_string()
};
tcx.lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), &message);
tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| {
// FIXME: when we make this a hard error, this should have its
// own error code.
let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
"`#[derive]` can't be used on a `#[repr(packed)]` struct with \
type or const parameters (error E0133)"
.to_string()
} else {
"`#[derive]` can't be used on a `#[repr(packed)]` struct that \
does not derive Copy (error E0133)"
.to_string()
};
lint.build(&message).emit()
});
}
/// 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) {
let span = tcx.sess.source_map().def_span(tcx.hir().span(id));
let msg = "unnecessary `unsafe` block";
let mut db = tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, msg);
db.span_label(span, msg);
if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
db.span_label(
tcx.sess.source_map().def_span(tcx.hir().span(id)),
format!("because it's nested under this `unsafe` {}", kind),
);
}
db.emit();
tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| {
let msg = "unnecessary `unsafe` block";
let mut db = lint.build(msg);
db.span_label(span, msg);
if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) {
db.span_label(
tcx.sess.source_map().def_span(tcx.hir().span(id)),
format!("because it's nested under this `unsafe` {}", kind),
);
}
db.emit();
});
}
fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
@ -619,13 +623,15 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) {
SAFE_PACKED_BORROWS,
lint_hir_id,
source_info.span,
&format!(
"{} is unsafe and requires unsafe function or block (error E0133)",
description
),
|lint| {
lint.build(&format!(
"{} is unsafe and requires unsafe function or block (error E0133)",
description
))
.note(&details.as_str())
.emit()
},
)
.note(&details.as_str())
.emit();
}
}
}

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

View File

@ -2235,24 +2235,26 @@ fn lint_overlapping_patterns<'tcx>(
overlaps: Vec<IntRange<'tcx>>,
) {
if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) {
let mut err = tcx.struct_span_lint_hir(
tcx.struct_span_lint_hir(
lint::builtin::OVERLAPPING_PATTERNS,
hir_id,
ctor_range.span,
"multiple patterns covering the same range",
|lint| {
let mut err = lint.build("multiple patterns covering the same range");
err.span_label(ctor_range.span, "overlapping patterns");
for int_range in overlaps {
// Use the real type for user display of the ranges:
err.span_label(
int_range.span,
&format!(
"this range overlaps on `{}`",
IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx),
),
);
}
err.emit();
},
);
err.span_label(ctor_range.span, "overlapping patterns");
for int_range in overlaps {
// Use the real type for user display of the ranges:
err.span_label(
int_range.span,
&format!(
"this range overlaps on `{}`",
IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx),
),
);
}
err.emit();
}
}

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
})
{
let ty_path = cx.tcx.def_path_str(edef.did);
cx.tcx
.struct_span_lint_hir(
BINDINGS_WITH_VARIANT_NAME,
p.hir_id,
p.span,
&format!(
cx.tcx.struct_span_lint_hir(
BINDINGS_WITH_VARIANT_NAME,
p.hir_id,
p.span,
|lint| {
let ty_path = cx.tcx.def_path_str(edef.did);
lint.build(&format!(
"pattern binding `{}` is named the same as one \
of the variants of the type `{}`",
of the variants of the type `{}`",
ident, ty_path
),
)
.code(error_code!(E0170))
.span_suggestion(
p.span,
"to match on the variant, qualify the path",
format!("{}::{}", ty_path, ident),
Applicability::MachineApplicable,
)
.emit();
))
.code(error_code!(E0170))
.span_suggestion(
p.span,
"to match on the variant, qualify the path",
format!("{}::{}", ty_path, ident),
Applicability::MachineApplicable,
)
.emit();
},
)
}
}
}
@ -325,22 +326,26 @@ fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
}
fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
let mut err = tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern");
if let Some(catchall) = catchall {
// We had a catchall pattern, hint at that.
err.span_label(span, "unreachable pattern");
err.span_label(catchall, "matches any value");
}
err.emit();
tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| {
let mut err = lint.build("unreachable pattern");
if let Some(catchall) = catchall {
// We had a catchall pattern, hint at that.
err.span_label(span, "unreachable pattern");
err.span_label(catchall, "matches any value");
}
err.emit();
});
}
fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) {
let msg = match source {
hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
_ => bug!(),
};
tcx.lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, msg);
tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| {
let msg = match source {
hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
_ => bug!(),
};
lint.build(msg).emit()
});
}
/// 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 msg = format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path,
);
let make_msg = || -> String {
format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path,
)
};
// double-check there even *is* a semantic `PartialEq` to dispatch to.
//
@ -143,13 +146,13 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
if !ty_is_partial_eq {
// span_fatal avoids ICE from resolution of non-existent method (rare case).
self.tcx().sess.span_fatal(self.span, &msg);
self.tcx().sess.span_fatal(self.span, &make_msg());
} else {
self.tcx().lint_hir(
self.tcx().struct_span_lint_hir(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
self.span,
&msg,
|lint| lint.build(&make_msg()).emit(),
);
}
}
@ -177,11 +180,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
let kind = match cv.ty.kind {
ty::Float(_) => {
tcx.lint_hir(
tcx.struct_span_lint_hir(
::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id,
span,
"floating-point types cannot be used in patterns",
|lint| lint.build("floating-point types cannot be used in patterns").emit(),
);
PatKind::Constant { value: cv }
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1014,7 +1014,7 @@ impl<'a> Parser<'a> {
String::new(),
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() |=
unclosed_delims.iter().any(|unmatched_delim| unmatched_delim.found_delim.is_none());
for unmatched in unclosed_delims.drain(..) {
make_unclosed_delims_error(unmatched, sess).map(|mut e| e.emit());
make_unclosed_delims_error(unmatched, sess).map(|mut e| {
e.emit();
});
}
}

View File

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

View File

@ -27,7 +27,11 @@ pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
_ => {
if let MacArgs::Eq(..) = attr.get_normal_item().args {
// All key-value attributes are restricted to meta-item syntax.
parse_meta(sess, attr).map_err(|mut err| err.emit()).ok();
parse_meta(sess, attr)
.map_err(|mut err| {
err.emit();
})
.ok();
}
}
}
@ -152,6 +156,8 @@ pub fn check_builtin_attribute(
}
}
}
Err(mut err) => err.emit(),
Err(mut err) => {
err.emit();
}
}
}

View File

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

View File

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

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 is_assigned {
self.ir
.tcx
.struct_span_lint_hir(
lint::builtin::UNUSED_VARIABLES,
hir_id,
spans,
&format!("variable `{}` is assigned to, but never used", name),
)
.note(&format!("consider using `_{}` instead", name))
.emit();
self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_VARIABLES,
hir_id,
spans,
|lint| {
lint.build(&format!("variable `{}` is assigned to, but never used", name))
.note(&format!("consider using `_{}` instead", name))
.emit();
},
)
} else {
let mut err = self.ir.tcx.struct_span_lint_hir(
self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_VARIABLES,
hir_id,
spans.clone(),
&format!("unused variable: `{}`", name),
|lint| {
let mut err = lint.build(&format!("unused variable: `{}`", name));
if self.ir.variable_is_shorthand(var) {
if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
// Handle `ref` and `ref mut`.
let spans = spans
.iter()
.map(|_span| (pat.span, format!("{}: _", name)))
.collect();
err.multipart_suggestion(
"try ignoring the field",
spans,
Applicability::MachineApplicable,
);
}
} else {
err.multipart_suggestion(
"consider prefixing with an underscore",
spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
Applicability::MachineApplicable,
);
}
err.emit()
},
);
if self.ir.variable_is_shorthand(var) {
if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
// Handle `ref` and `ref mut`.
let spans =
spans.iter().map(|_span| (pat.span, format!("{}: _", name))).collect();
err.multipart_suggestion(
"try ignoring the field",
spans,
Applicability::MachineApplicable,
);
}
} else {
err.multipart_suggestion(
"consider prefixing with an underscore",
spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
Applicability::MachineApplicable,
);
}
err.emit()
}
}
}
@ -1573,27 +1575,27 @@ impl<'tcx> Liveness<'_, 'tcx> {
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 is_param {
self.ir
.tcx
.struct_span_lint_hir(
lint::builtin::UNUSED_ASSIGNMENTS,
hir_id,
spans,
&format!("value passed to `{}` is never read", name),
)
.help("maybe it is overwritten before being read?")
.emit();
self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_ASSIGNMENTS,
hir_id,
spans,
|lint| {
lint.build(&format!("value passed to `{}` is never read", name))
.help("maybe it is overwritten before being read?")
.emit();
},
)
} else {
self.ir
.tcx
.struct_span_lint_hir(
lint::builtin::UNUSED_ASSIGNMENTS,
hir_id,
spans,
&format!("value assigned to `{}` is never read", name),
)
.help("maybe it is overwritten before being read?")
.emit();
self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_ASSIGNMENTS,
hir_id,
spans,
|lint| {
lint.build(&format!("value assigned to `{}` is never read", name))
.help("maybe it is overwritten before being read?")
.emit();
},
)
}
}
}

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) {
tcx.lint_hir(
lint::builtin::STABLE_FEATURES,
hir::CRATE_HIR_ID,
span,
&format!(
tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
lint.build(&format!(
"the feature `{}` has been stable since {} and no longer requires \
an attribute to enable",
an attribute to enable",
feature, since
),
);
))
.emit();
});
}
fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) {

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 {
if self.leaks_private_dep(def_id) {
self.tcx.lint_hir(
self.tcx.struct_span_lint_hir(
lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
self.item_id,
self.span,
&format!(
"{} `{}` from private dependency '{}' in public \
interface",
kind,
descr,
self.tcx.crate_name(def_id.krate)
),
|lint| {
lint.build(&format!(
"{} `{}` from private dependency '{}' in public \
interface",
kind,
descr,
self.tcx.crate_name(def_id.krate)
))
.emit()
},
);
}
@ -1802,23 +1805,23 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> {
let (vis, vis_span, vis_descr) = def_id_visibility(self.tcx, def_id);
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 {
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 {
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(vis_span, format!("`{}` declared as {}", descr, vis_descr));
err.emit();
} else {
let err_code = if kind == "trait" { "E0445" } else { "E0446" };
self.tcx.lint_hir(
self.tcx.struct_span_lint_hir(
lint::builtin::PRIVATE_IN_PUBLIC,
hir_id,
self.span,
&format!("{} (error {})", msg, err_code),
|lint| lint.build(&format!("{} (error {})", 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,
id,
span,
&format!("lifetime parameter `{}` only used once", name),
|lint| {
let mut err = lint.build(&format!(
"lifetime parameter `{}` only used once",
name
));
if span == lifetime.span {
// spans are the same for in-band lifetime declarations
err.span_label(span, "this lifetime is only used here");
} else {
err.span_label(span, "this lifetime...");
err.span_label(lifetime.span, "...is used only here");
}
self.suggest_eliding_single_use_lifetime(
&mut err, def_id, lifetime,
);
err.emit();
},
);
if span == lifetime.span {
// spans are the same for in-band lifetime declarations
err.span_label(span, "this lifetime is only used here");
} else {
err.span_label(span, "this lifetime...");
err.span_label(lifetime.span, "...is used only here");
}
self.suggest_eliding_single_use_lifetime(&mut err, def_id, lifetime);
err.emit();
}
}
Some(LifetimeUseSet::Many) => {
@ -1610,26 +1616,32 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
_ => None,
} {
debug!("id ={:?} span = {:?} name = {:?}", id, span, name);
let mut err = self.tcx.struct_span_lint_hir(
self.tcx.struct_span_lint_hir(
lint::builtin::UNUSED_LIFETIMES,
id,
span,
&format!("lifetime parameter `{}` never used", name),
);
if let Some(parent_def_id) = self.tcx.parent(def_id) {
if let Some(generics) = self.tcx.hir().get_generics(parent_def_id) {
let unused_lt_span = self.lifetime_deletion_span(name, generics);
if let Some(span) = unused_lt_span {
err.span_suggestion(
span,
"elide the unused lifetime",
String::new(),
Applicability::MachineApplicable,
);
|lint| {
let mut err = lint
.build(&format!("lifetime parameter `{}` never used", name));
if let Some(parent_def_id) = self.tcx.parent(def_id) {
if let Some(generics) =
self.tcx.hir().get_generics(parent_def_id)
{
let unused_lt_span =
self.lifetime_deletion_span(name, generics);
if let Some(span) = unused_lt_span {
err.span_suggestion(
span,
"elide the unused lifetime",
String::new(),
Applicability::MachineApplicable,
);
}
}
}
}
}
err.emit();
err.emit();
},
);
}
}
}

View File

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

View File

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

View File

@ -382,11 +382,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
.emit();
} else {
self.tcx.lint_hir(
self.tcx.struct_span_lint_hir(
lint::builtin::TYVAR_BEHIND_RAW_POINTER,
scope_expr_id,
span,
"type annotations needed",
|lint| lint.build("type annotations needed").emit(),
);
}
} else {
@ -1280,33 +1280,36 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
stable_pick: &Pick<'_>,
unstable_candidates: &[(&Candidate<'tcx>, Symbol)],
) {
let mut diag = self.tcx.struct_span_lint_hir(
self.tcx.struct_span_lint_hir(
lint::builtin::UNSTABLE_NAME_COLLISIONS,
self.fcx.body_id,
self.span,
"a method with this name may be added to the standard library in the future",
);
// FIXME: This should be a `span_suggestion` instead of `help`
// However `self.span` only
// highlights the method name, so we can't use it. Also consider reusing the code from
// `report_method_error()`.
diag.help(&format!(
"call with fully qualified syntax `{}(...)` to keep using the current method",
self.tcx.def_path_str(stable_pick.item.def_id),
));
if nightly_options::is_nightly_build() {
for (candidate, feature) in unstable_candidates {
|lint| {
let mut diag = lint.build(
"a method with this name may be added to the standard library in the future",
);
// FIXME: This should be a `span_suggestion` instead of `help`
// However `self.span` only
// highlights the method name, so we can't use it. Also consider reusing the code from
// `report_method_error()`.
diag.help(&format!(
"add `#![feature({})]` to the crate attributes to enable `{}`",
feature,
self.tcx.def_path_str(candidate.item.def_id),
"call with fully qualified syntax `{}(...)` to keep using the current method",
self.tcx.def_path_str(stable_pick.item.def_id),
));
}
}
diag.emit();
if nightly_options::is_nightly_build() {
for (candidate, feature) in unstable_candidates {
diag.help(&format!(
"add `#![feature({})]` to the crate attributes to enable `{}`",
feature,
self.tcx.def_path_str(candidate.item.def_id),
));
}
}
diag.emit();
},
);
}
fn select_trait_candidate(

View File

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

View File

@ -55,12 +55,14 @@ impl CheckVisitor<'tcx> {
return;
}
let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
format!("unused import: `{}`", snippet)
} else {
"unused import".to_owned()
};
self.tcx.lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, &msg);
self.tcx.struct_span_lint_hir(lint::builtin::UNUSED_IMPORTS, id, span, |lint| {
let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
format!("unused import: `{}`", snippet)
} else {
"unused import".to_owned()
};
lint.build(&msg).emit();
});
}
}
@ -121,23 +123,23 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
// We do this in any edition.
if extern_crate.warn_if_unused {
if let Some(&span) = unused_extern_crates.get(&extern_crate.def_id) {
let msg = "unused extern crate";
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)
let span_with_attrs = tcx
.get_attrs(extern_crate.def_id)
.iter()
.map(|attr| attr.span)
.fold(span, |acc, attr_span| acc.to(attr_span));
tcx.struct_span_lint_hir(lint, id, span, msg)
.span_suggestion_short(
span_with_attrs,
"remove it",
String::new(),
Applicability::MachineApplicable,
)
.emit();
lint.build("unused extern crate")
.span_suggestion_short(
span_with_attrs,
"remove it",
String::new(),
Applicability::MachineApplicable,
)
.emit();
});
continue;
}
}
@ -168,23 +170,26 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
if !tcx.get_attrs(extern_crate.def_id).is_empty() {
continue;
}
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 msg = "`extern crate` is not idiomatic in the new edition";
let help = format!("convert it to a `{}`", visibility_qualified(&item.vis, "use"));
let base_replacement = match extern_crate.orig_name {
Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name),
None => format!("use {};", item.ident.name),
};
let replacement = visibility_qualified(&item.vis, base_replacement);
tcx.struct_span_lint_hir(lint, id, extern_crate.span, msg)
.span_suggestion_short(
extern_crate.span,
&help,
replacement,
Applicability::MachineApplicable,
)
.emit();
let replacement = visibility_qualified(&item.vis, base_replacement);
let msg = "`extern crate` is not idiomatic in the new edition";
let help = format!("convert it to a `{}`", visibility_qualified(&item.vis, "use"));
lint.build(msg)
.span_suggestion_short(
extern_crate.span,
&help,
replacement,
Applicability::MachineApplicable,
)
.emit();
})
}
}

View File

@ -1151,14 +1151,17 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics {
GenericParamKind::Type { ref default, synthetic, .. } => {
if !allow_defaults && default.is_some() {
if !tcx.features().default_type_parameter_fallback {
tcx.lint_hir(
tcx.struct_span_lint_hir(
lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
param.hir_id,
param.span,
&format!(
"defaults for type parameters are only allowed in \
|lint| {
lint.build(&format!(
"defaults for type parameters are only allowed in \
`struct`, `enum`, `type`, or `trait` definitions."
),
))
.emit();
},
);
}
}
@ -2956,10 +2959,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
lint::builtin::INLINE_NO_SANITIZE,
hir_id,
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 sp = span_of_attrs(attrs).unwrap_or(item.source.span());
let mut diag = cx.tcx.struct_span_lint_hir(
cx.tcx.struct_span_lint_hir(
lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
hir_id,
sp,
&format!("`[{}]` {}", path_str, err_msg),
);
if let Some(link_range) = link_range {
if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
diag.set_span(sp);
diag.span_label(sp, short_err_msg);
} else {
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
// ^ ~~~~
// | link_range
// last_new_line_offset
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
|lint| {
let mut diag = lint.build(&format!("`[{}]` {}", path_str, err_msg));
if let Some(link_range) = link_range {
if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs)
{
diag.set_span(sp);
diag.span_label(sp, short_err_msg);
} else {
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
// ^ ~~~~
// | link_range
// last_new_line_offset
let last_new_line_offset =
dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
// Print the line containing the `link_range` and manually mark it with '^'s.
diag.note(&format!(
"the link appears in this line:\n\n{line}\n\
{indicator: <before$}{indicator:^<found$}",
line = line,
indicator = "",
before = link_range.start - last_new_line_offset,
found = link_range.len(),
));
}
};
if let Some(help_msg) = help_msg {
diag.help(help_msg);
}
diag.emit();
// Print the line containing the `link_range` and manually mark it with '^'s.
diag.note(&format!(
"the link appears in this line:\n\n{line}\n\
{indicator: <before$}{indicator:^<found$}",
line = line,
indicator = "",
before = link_range.start - last_new_line_offset,
found = link_range.len(),
));
}
};
if let Some(help_msg) = help_msg {
diag.help(help_msg);
}
diag.emit();
},
);
}
/// Reports a resolution failure diagnostic.
@ -766,105 +770,108 @@ fn ambiguity_error(
let attrs = &item.attrs;
let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
let mut msg = format!("`{}` is ", path_str);
let candidates = [TypeNS, ValueNS, MacroNS]
.iter()
.filter_map(|&ns| candidates[ns].map(|res| (res, ns)))
.collect::<Vec<_>>();
match candidates.as_slice() {
[(first_def, _), (second_def, _)] => {
msg += &format!(
"both {} {} and {} {}",
first_def.article(),
first_def.descr(),
second_def.article(),
second_def.descr(),
);
}
_ => {
let mut candidates = candidates.iter().peekable();
while let Some((res, _)) = candidates.next() {
if candidates.peek().is_some() {
msg += &format!("{} {}, ", res.article(), res.descr());
} else {
msg += &format!("and {} {}", res.article(), res.descr());
}
}
}
}
let mut diag = cx.tcx.struct_span_lint_hir(
cx.tcx.struct_span_lint_hir(
lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
hir_id,
sp,
&msg,
);
|lint| {
let mut msg = format!("`{}` is ", path_str);
if let Some(link_range) = link_range {
if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
diag.set_span(sp);
diag.span_label(sp, "ambiguous link");
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());
}
}
}
}
for (res, ns) in candidates {
let (action, mut suggestion) = match res {
Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => {
("add parentheses", format!("{}()", path_str))
}
Res::Def(DefKind::Macro(..), _) => {
("add an exclamation mark", format!("{}!", path_str))
}
_ => {
let type_ = match (res, ns) {
(Res::Def(DefKind::Const, _), _) => "const",
(Res::Def(DefKind::Static, _), _) => "static",
(Res::Def(DefKind::Struct, _), _) => "struct",
(Res::Def(DefKind::Enum, _), _) => "enum",
(Res::Def(DefKind::Union, _), _) => "union",
(Res::Def(DefKind::Trait, _), _) => "trait",
(Res::Def(DefKind::Mod, _), _) => "module",
(_, TypeNS) => "type",
(_, ValueNS) => "value",
(_, MacroNS) => "macro",
let mut diag = lint.build(&msg);
if let Some(link_range) = link_range {
if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs)
{
diag.set_span(sp);
diag.span_label(sp, "ambiguous link");
for (res, ns) in candidates {
let (action, mut suggestion) = match res {
Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => {
("add parentheses", format!("{}()", path_str))
}
Res::Def(DefKind::Macro(..), _) => {
("add an exclamation mark", format!("{}!", path_str))
}
_ => {
let type_ = match (res, ns) {
(Res::Def(DefKind::Const, _), _) => "const",
(Res::Def(DefKind::Static, _), _) => "static",
(Res::Def(DefKind::Struct, _), _) => "struct",
(Res::Def(DefKind::Enum, _), _) => "enum",
(Res::Def(DefKind::Union, _), _) => "union",
(Res::Def(DefKind::Trait, _), _) => "trait",
(Res::Def(DefKind::Mod, _), _) => "module",
(_, TypeNS) => "type",
(_, ValueNS) => "value",
(_, MacroNS) => "macro",
};
// FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
("prefix with the item type", format!("{}@{}", type_, path_str))
}
};
// FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
("prefix with the item type", format!("{}@{}", type_, path_str))
if dox.bytes().nth(link_range.start) == Some(b'`') {
suggestion = format!("`{}`", suggestion);
}
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'`') {
suggestion = format!("`{}`", suggestion);
// Print the line containing the `link_range` and manually mark it with '^'s.
diag.note(&format!(
"the link appears in this line:\n\n{line}\n\
{indicator: <before$}{indicator:^<found$}",
line = line,
indicator = "",
before = link_range.start - last_new_line_offset,
found = link_range.len(),
));
}
diag.span_suggestion(
sp,
&format!("to link to the {}, {}", res.descr(), action),
suggestion,
Applicability::MaybeIncorrect,
);
}
} else {
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
// ^ ~~~~
// | link_range
// last_new_line_offset
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
// Print the line containing the `link_range` and manually mark it with '^'s.
diag.note(&format!(
"the link appears in this line:\n\n{line}\n\
{indicator: <before$}{indicator:^<found$}",
line = line,
indicator = "",
before = link_range.start - last_new_line_offset,
found = link_range.len(),
));
}
}
diag.emit();
diag.emit();
},
);
}
/// Given an enum variant's res, return the res of its enum and the associated fragment.

View File

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

View File

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

View File

@ -5,12 +5,14 @@
extern crate rustc_driver;
extern crate rustc_hir;
extern crate rustc_span;
#[macro_use] extern crate rustc_lint;
#[macro_use] extern crate rustc_session;
#[macro_use]
extern crate rustc_lint;
#[macro_use]
extern crate rustc_session;
extern crate syntax;
use rustc_lint::{LateContext, LintContext, LintPass, LateLintPass};
use rustc_driver::plugin::Registry;
use rustc_lint::{LateContext, LateLintPass, LintContext, LintPass};
use rustc_span::symbol::Symbol;
use syntax::attr;
@ -28,8 +30,10 @@ macro_rules! fake_lint_pass {
fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) {
$(
if !attr::contains_name(&krate.attrs, $attr) {
cx.span_lint(CRATE_NOT_OKAY, krate.span,
&format!("crate is not marked with #![{}]", $attr));
cx.lint(CRATE_NOT_OKAY, |lint| {
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_hir;
#[macro_use] extern crate rustc_lint;
#[macro_use] extern crate rustc_session;
#[macro_use]
extern crate rustc_lint;
#[macro_use]
extern crate rustc_session;
extern crate rustc_span;
extern crate syntax;
use rustc_lint::{LateContext, LintContext, LintPass, LateLintPass, LintArray};
use rustc_driver::plugin::Registry;
use rustc_lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc_span::symbol::Symbol;
use syntax::attr;
@ -26,8 +28,11 @@ declare_lint_pass!(Pass => [CRATE_NOT_OKAY]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) {
if !attr::contains_name(&krate.attrs, Symbol::intern("crate_okay")) {
cx.span_lint(CRATE_NOT_OKAY, krate.span,
"crate is not marked with #![crate_okay]");
cx.lint(CRATE_NOT_OKAY, |lint| {
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.
extern crate rustc_driver;
extern crate rustc_hir;
#[macro_use] extern crate rustc_lint;
#[macro_use] extern crate rustc_session;
#[macro_use]
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_lint::{LateContext, LateLintPass, LintArray, LintContext, LintId, LintPass};
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 {
fn check_item(&mut self, cx: &LateContext, it: &rustc_hir::Item) {
match &*it.ident.as_str() {
"lintme" => cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'"),
"pleaselintme" => cx.span_lint(PLEASE_LINT, it.span, "item is named 'pleaselintme'"),
"lintme" => cx.lint(TEST_LINT, |lint| {
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) {
reg.lint_store.register_lints(&[&TEST_LINT, &PLEASE_LINT]);
reg.lint_store.register_late_pass(|| box Pass);
reg.lint_store.register_group(true, "lint_me", None,
vec![LintId::of(&TEST_LINT), LintId::of(&PLEASE_LINT)]);
reg.lint_store.register_group(
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
extern crate rustc_driver;
#[macro_use] extern crate rustc_lint;
#[macro_use] extern crate rustc_session;
#[macro_use]
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_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
use syntax::ast;
declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'");
@ -20,7 +22,9 @@ declare_lint_pass!(Pass => [TEST_LINT]);
impl EarlyLintPass for Pass {
fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
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
extern crate rustc_driver;
#[macro_use] extern crate rustc_lint;
#[macro_use] extern crate rustc_session;
#[macro_use]
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_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintId, LintPass};
use syntax::ast;
declare_tool_lint!(pub clippy::TEST_LINT, Warn, "Warn about stuff");
declare_tool_lint!(
@ -30,10 +32,14 @@ declare_lint_pass!(Pass => [TEST_LINT, TEST_GROUP, TEST_RUSTC_TOOL_LINT]);
impl EarlyLintPass for Pass {
fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
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" {
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) {
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_group(true, "clippy::group", Some("clippy_group"),
vec![LintId::of(&TEST_LINT), LintId::of(&TEST_GROUP)]);
reg.lint_store.register_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 }
//~^ ERROR ambiguous associated item
//~| WARN this was previously accepted
//~| HELP use fully-qualified syntax
}
fn main() {}