1
Fork 0

Refactor rustc lint API

This commit is contained in:
Maybe Waffle 2022-09-16 11:01:02 +04:00
parent 65445a571c
commit a8f7e244b7
64 changed files with 1760 additions and 1555 deletions

View file

@ -33,7 +33,7 @@ use super::FnCtxt;
use crate::hir::def_id::DefId;
use crate::type_error_struct;
use hir::def_id::LOCAL_CRATE;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode};
use rustc_middle::mir::Mutability;
@ -754,19 +754,25 @@ impl<'a, 'tcx> CastCheck<'tcx> {
} else {
("", lint::builtin::TRIVIAL_CASTS)
};
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)
))
.help(&format!(
"cast can be replaced by coercion; this might \
require {type_asc_or}a temporary variable"
))
.emit();
});
fcx.tcx.struct_span_lint_hir(
lint,
self.expr.hir_id,
self.span,
DelayDm(|| {
format!(
"trivial {}cast: `{}` as `{}`",
adjective,
fcx.ty_to_string(t_expr),
fcx.ty_to_string(t_cast)
)
}),
|lint| {
lint.help(format!(
"cast can be replaced by coercion; this might \
require {type_asc_or}a temporary variable"
))
},
);
}
#[instrument(skip(fcx), level = "debug")]
@ -1074,12 +1080,12 @@ impl<'a, 'tcx> CastCheck<'tcx> {
lint::builtin::CENUM_IMPL_DROP_CAST,
self.expr.hir_id,
self.span,
|err| {
err.build(&format!(
"cannot cast enum `{}` into integer `{}` because it implements `Drop`",
self.expr_ty, self.cast_ty
))
.emit();
DelayDm(|| format!(
"cannot cast enum `{}` into integer `{}` because it implements `Drop`",
self.expr_ty, self.cast_ty
)),
|lint| {
lint
},
);
}
@ -1090,12 +1096,11 @@ impl<'a, 'tcx> CastCheck<'tcx> {
lint::builtin::LOSSY_PROVENANCE_CASTS,
self.expr.hir_id,
self.span,
|err| {
let mut err = err.build(&format!(
DelayDm(|| format!(
"under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`",
self.expr_ty, self.cast_ty
));
)),
|lint| {
let msg = "use `.addr()` to obtain the address of a pointer";
let expr_prec = self.expr.precedence().order();
@ -1114,9 +1119,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
(cast_span, format!(").addr(){scalar_cast}")),
];
err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
} else {
err.span_suggestion(
lint.span_suggestion(
cast_span,
msg,
format!(".addr(){scalar_cast}"),
@ -1124,12 +1129,12 @@ impl<'a, 'tcx> CastCheck<'tcx> {
);
}
err.help(
lint.help(
"if you can't comply with strict provenance and need to expose the pointer \
provenance you can use `.expose_addr()` instead"
);
err.emit();
lint
},
);
}
@ -1139,24 +1144,24 @@ impl<'a, 'tcx> CastCheck<'tcx> {
lint::builtin::FUZZY_PROVENANCE_CASTS,
self.expr.hir_id,
self.span,
|err| {
let mut err = err.build(&format!(
"strict provenance disallows casting integer `{}` to pointer `{}`",
self.expr_ty, self.cast_ty
));
DelayDm(|| format!(
"strict provenance disallows casting integer `{}` to pointer `{}`",
self.expr_ty, self.cast_ty
)),
|lint| {
let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address";
let suggestions = vec![
(self.expr_span.shrink_to_lo(), String::from("(...).with_addr(")),
(self.expr_span.shrink_to_hi().to(self.cast_span), String::from(")")),
];
err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
err.help(
lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
lint.help(
"if you can't comply with strict provenance and don't have a pointer with \
the correct provenance you can use `std::ptr::from_exposed_addr()` instead"
);
err.emit();
lint
},
);
}

View file

@ -48,9 +48,13 @@ pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ab
.emit();
}
None => {
tcx.struct_span_lint_hir(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| {
lint.build("use of calling convention not supported on this target").emit();
});
tcx.struct_span_lint_hir(
UNSUPPORTED_CALLING_CONVENTIONS,
hir_id,
span,
"use of calling convention not supported on this target",
|lint| lint,
);
}
}
@ -510,10 +514,10 @@ fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
UNINHABITED_STATIC,
tcx.hir().local_def_id_to_hir_id(def_id),
span,
"static of uninhabited type",
|lint| {
lint.build("static of uninhabited type")
lint
.note("uninhabited statics cannot be initialized, and any access would be an immediate error")
.emit();
},
);
}
@ -1434,6 +1438,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
span,
"zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types",
|lint| {
let note = if non_exhaustive {
"is marked with `#[non_exhaustive]`"
@ -1441,10 +1446,9 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
"contains private fields"
};
let field_ty = tcx.def_path_str_with_substs(def_id, substs);
lint.build("zero-sized fields in repr(transparent) cannot contain external non-exhaustive types")
lint
.note(format!("this {descr} contains `{field_ty}`, which {note}, \
and makes it not a breaking change to become non-zero-sized in the future."))
.emit();
},
)
}

View file

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

View file

@ -6,7 +6,7 @@
use self::drop_ranges::DropRanges;
use super::FnCtxt;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_errors::pluralize;
use rustc_errors::{pluralize, DelayDm};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
@ -610,33 +610,33 @@ fn check_must_not_suspend_def(
rustc_session::lint::builtin::MUST_NOT_SUSPEND,
hir_id,
data.source_span,
|lint| {
let msg = format!(
DelayDm(|| {
format!(
"{}`{}`{} held across a suspend point, but should not be",
data.descr_pre,
tcx.def_path_str(def_id),
data.descr_post,
);
let mut err = lint.build(&msg);
)
}),
|lint| {
// add span pointing to the offending yield/await
err.span_label(data.yield_span, "the value is held across this suspend point");
lint.span_label(data.yield_span, "the value is held across this suspend point");
// Add optional reason note
if let Some(note) = attr.value_str() {
// FIXME(guswynn): consider formatting this better
err.span_note(data.source_span, note.as_str());
lint.span_note(data.source_span, note.as_str());
}
// Add some quick suggestions on what to do
// FIXME: can `drop` work as a suggestion here as well?
err.span_help(
lint.span_help(
data.source_span,
"consider using a block (`{ ... }`) \
to shrink the value's scope, ending before the suspend point",
);
err.emit();
lint
},
);

View file

@ -328,17 +328,16 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
lint::builtin::ASM_SUB_REGISTER,
expr.hir_id,
spans,
"formatting may not be suitable for sub-register argument",
|lint| {
let msg = "formatting may not be suitable for sub-register argument";
let mut err = lint.build(msg);
err.span_label(expr.span, "for this argument");
err.help(&format!(
lint.span_label(expr.span, "for this argument");
lint.help(&format!(
"use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`",
));
err.help(&format!(
lint.help(&format!(
"or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`",
));
err.emit();
lint
},
);
}

View file

@ -82,14 +82,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
prelude_or_array_lint,
self_expr.hir_id,
self_expr.span,
format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
|lint| {
let sp = self_expr.span;
let mut lint = lint.build(&format!(
"trait method `{}` will become ambiguous in Rust 2021",
segment.ident.name
));
let derefs = "*".repeat(pick.autoderefs);
let autoref = match pick.autoref_or_ptr_adjustment {
@ -133,7 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
lint.emit();
lint
},
);
} else {
@ -143,6 +139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
prelude_or_array_lint,
call_expr.hir_id,
call_expr.span,
format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
|lint| {
let sp = call_expr.span;
let trait_name = self.trait_path_or_bare_name(
@ -151,11 +148,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pick.item.container_id(self.tcx),
);
let mut lint = lint.build(&format!(
"trait method `{}` will become ambiguous in Rust 2021",
segment.ident.name
));
let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
if precise {
let args = args
@ -202,7 +194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
lint.emit();
lint
},
);
}
@ -257,15 +249,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}
self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| {
// "type" refers to either a type or, more likely, a trait from which
// the associated function or method is from.
let container_id = pick.item.container_id(self.tcx);
let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id);
let trait_generics = self.tcx.generics_of(container_id);
self.tcx.struct_span_lint_hir(
RUST_2021_PRELUDE_COLLISIONS,
expr_id,
span,
format!(
"trait-associated function `{}` will become ambiguous in Rust 2021",
method_name.name
),
|lint| {
// "type" refers to either a type or, more likely, a trait from which
// the associated function or method is from.
let container_id = pick.item.container_id(self.tcx);
let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id);
let trait_generics = self.tcx.generics_of(container_id);
let trait_name =
if trait_generics.params.len() <= trait_generics.has_self as usize {
let trait_name = if trait_generics.params.len() <= trait_generics.has_self as usize
{
trait_path
} else {
let counts = trait_generics.own_counts();
@ -282,44 +282,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
};
let mut lint = lint.build(&format!(
"trait-associated function `{}` will become ambiguous in Rust 2021",
method_name.name
));
let mut self_ty_name = self_ty_span
.find_ancestor_inside(span)
.and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
.unwrap_or_else(|| self_ty.to_string());
let mut self_ty_name = self_ty_span
.find_ancestor_inside(span)
.and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
.unwrap_or_else(|| self_ty.to_string());
// Get the number of generics the self type has (if an Adt) unless we can determine that
// the user has written the self type with generics already which we (naively) do by looking
// for a "<" in `self_ty_name`.
if !self_ty_name.contains('<') {
if let Adt(def, _) = self_ty.kind() {
let generics = self.tcx.generics_of(def.did());
if !generics.params.is_empty() {
let counts = generics.own_counts();
self_ty_name += &format!(
"<{}>",
std::iter::repeat("'_")
.take(counts.lifetimes)
.chain(std::iter::repeat("_").take(counts.types + counts.consts))
.collect::<Vec<_>>()
.join(", ")
);
// Get the number of generics the self type has (if an Adt) unless we can determine that
// the user has written the self type with generics already which we (naively) do by looking
// for a "<" in `self_ty_name`.
if !self_ty_name.contains('<') {
if let Adt(def, _) = self_ty.kind() {
let generics = self.tcx.generics_of(def.did());
if !generics.params.is_empty() {
let counts = generics.own_counts();
self_ty_name += &format!(
"<{}>",
std::iter::repeat("'_")
.take(counts.lifetimes)
.chain(
std::iter::repeat("_").take(counts.types + counts.consts)
)
.collect::<Vec<_>>()
.join(", ")
);
}
}
}
}
lint.span_suggestion(
span,
"disambiguate the associated function",
format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
Applicability::MachineApplicable,
);
lint.span_suggestion(
span,
"disambiguate the associated function",
format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
Applicability::MachineApplicable,
);
lint.emit();
});
lint
},
);
}
fn trait_path_or_bare_name(

View file

@ -409,9 +409,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lint::builtin::TYVAR_BEHIND_RAW_POINTER,
scope_expr_id,
span,
|lint| {
lint.build("type annotations needed").emit();
},
"type annotations needed",
|lint| lint,
);
}
} else {
@ -1358,24 +1357,24 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
stable_pick: &Pick<'_>,
unstable_candidates: &[(Candidate<'tcx>, Symbol)],
) {
let def_kind = stable_pick.item.kind.as_def_kind();
self.tcx.struct_span_lint_hir(
lint::builtin::UNSTABLE_NAME_COLLISIONS,
self.scope_expr_id,
self.span,
format!(
"{} {} with this name may be added to the standard library in the future",
def_kind.article(),
def_kind.descr(stable_pick.item.def_id),
),
|lint| {
let def_kind = stable_pick.item.kind.as_def_kind();
let mut diag = lint.build(&format!(
"{} {} with this name may be added to the standard library in the future",
def_kind.article(),
def_kind.descr(stable_pick.item.def_id),
));
match (stable_pick.item.kind, stable_pick.item.container) {
(ty::AssocKind::Fn, _) => {
// 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!(
lint.help(&format!(
"call with fully qualified syntax `{}(...)` to keep using the current \
method",
self.tcx.def_path_str(stable_pick.item.def_id),
@ -1383,7 +1382,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
(ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => {
let def_id = stable_pick.item.container_id(self.tcx);
diag.span_suggestion(
lint.span_suggestion(
self.span,
"use the fully qualified path to the associated const",
format!(
@ -1399,7 +1398,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
if self.tcx.sess.is_nightly_build() {
for (candidate, feature) in unstable_candidates {
diag.help(&format!(
lint.help(&format!(
"add `#![feature({})]` to the crate attributes to enable `{}`",
feature,
self.tcx.def_path_str(candidate.item.def_id),
@ -1407,7 +1406,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
diag.emit();
lint
},
);
}

View file

@ -1790,10 +1790,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(),
);
self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |build| {
let mut lint = build.build("some fields are not explicitly listed");
self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| {
lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns));
lint.help(
"ensure that all fields are mentioned explicitly by adding the suggested fields",
);
@ -1801,7 +1799,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found",
ty,
));
lint.emit();
lint
});
}

View file

@ -749,10 +749,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
closure_hir_id,
closure_head_span,
reasons.migration_message(),
|lint| {
let mut diagnostics_builder = lint.build(
&reasons.migration_message(),
);
for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations {
// Labels all the usage of the captured variable and why they are responsible
// for migration being needed
@ -760,13 +758,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match &lint_note.captures_info {
UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => {
let cause_span = self.tcx.hir().span(*capture_expr_id);
diagnostics_builder.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`",
lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`",
self.tcx.hir().name(*var_hir_id),
captured_name,
));
}
UpvarMigrationInfo::CapturingNothing { use_span } => {
diagnostics_builder.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect",
lint.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect",
self.tcx.hir().name(*var_hir_id),
));
}
@ -781,13 +779,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match &lint_note.captures_info {
UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => {
diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure",
lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure",
self.tcx.hir().name(*var_hir_id),
captured_name,
));
}
UpvarMigrationInfo::CapturingNothing { use_span: _ } => {
diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure",
lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure",
v = self.tcx.hir().name(*var_hir_id),
));
}
@ -800,7 +798,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match &lint_note.captures_info {
UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => {
let var_name = self.tcx.hir().name(*var_hir_id);
diagnostics_builder.span_label(closure_head_span, format!("\
lint.span_label(closure_head_span, format!("\
in Rust 2018, this closure implements {missing_trait} \
as `{var_name}` implements {missing_trait}, but in Rust 2021, \
this closure will no longer implement {missing_trait} \
@ -814,7 +812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
diagnostics_builder.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>");
lint.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>");
let diagnostic_msg = format!(
"add a dummy let to cause {} to be fully captured",
@ -857,7 +855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We take the indentation from the next non-empty line.
let line2 = lines.find(|line| !line.is_empty()).unwrap_or_default();
let indent = line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0;
diagnostics_builder.span_suggestion(
lint.span_suggestion(
closure_body_span.with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())).shrink_to_lo(),
&diagnostic_msg,
format!("\n{indent}{migration_string};"),
@ -868,7 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// braces, but with more than just the opening
// brace on the first line. We put the `let`
// directly after the `{`.
diagnostics_builder.span_suggestion(
lint.span_suggestion(
closure_body_span.with_lo(closure_body_span.lo() + BytePos(1)).shrink_to_lo(),
&diagnostic_msg,
format!(" {migration_string};"),
@ -877,7 +875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
// This is a closure without braces around the body.
// We add braces to add the `let` before the body.
diagnostics_builder.multipart_suggestion(
lint.multipart_suggestion(
&diagnostic_msg,
vec![
(closure_body_span.shrink_to_lo(), format!("{{ {migration_string}; ")),
@ -887,7 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
} else {
diagnostics_builder.span_suggestion(
lint.span_suggestion(
closure_span,
&diagnostic_msg,
migration_string,
@ -895,7 +893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
diagnostics_builder.emit();
lint
},
);
}