Auto merge of #101138 - Rejyr:diagnostic-migration-rustc-lint-pt2, r=davidtwco

Migrate `rustc_lint` lint diagnostics

Part 2 of [Migrate `rustc_lint` errors to `SessionDiagnostic`](https://github.com/rust-lang/rust/pull/100776)

r? `@davidtwco`

# TODO
- [x] Refactor some lints manually implementing `DecorateLint` to use `Option<Subdiagnostic>`.
- [x] Add `#[rustc_lint_diagnostics]` to lint functions in `context.rs`.
- [x] Migrate `hidden_unicode_codepoints.rs`.
- [x] Migrate `UnsafeCode` in `builtin.rs`.
- [x] Migrate the rest of `builtin.rs`.
This commit is contained in:
bors 2023-01-13 02:13:00 +00:00
commit bfffe406fb
26 changed files with 2295 additions and 1374 deletions

View File

@ -15,6 +15,43 @@ lint_enum_intrinsics_mem_variant =
lint_expectation = this lint expectation is unfulfilled lint_expectation = this lint expectation is unfulfilled
.note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
.rationale = {$rationale}
lint_for_loops_over_fallibles =
for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
.suggestion = consider using `if let` to clear intent
.remove_next = to iterate over `{$recv_snip}` remove the call to `next`
.use_while_let = to check pattern in a loop use `while let`
.use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents
lint_non_binding_let_on_sync_lock =
non-binding let on a synchronization lock
lint_non_binding_let_on_drop_type =
non-binding let on a type that implements `Drop`
lint_non_binding_let_suggestion =
consider binding to an unused variable to avoid immediately dropping the value
lint_non_binding_let_multi_suggestion =
consider immediately dropping the value
lint_deprecated_lint_name =
lint name `{$name}` is deprecated and may not have an effect in the future.
.suggestion = change it to
lint_renamed_or_removed_lint = {$msg}
.suggestion = use the new name
lint_unknown_lint =
unknown lint: `{$name}`
.suggestion = did you mean
lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level
lint_unknown_gated_lint =
unknown lint: `{$name}`
.note = the `{$name}` lint is unstable
lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label} lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label}
.label = this {$label} contains {$count -> .label = this {$label} contains {$count ->
@ -55,6 +92,8 @@ lint_diag_out_of_impl =
lint_untranslatable_diag = diagnostics should be created using translatable messages lint_untranslatable_diag = diagnostics should be created using translatable messages
lint_bad_opt_access = {$msg}
lint_cstring_ptr = getting the inner pointer of a temporary `CString` lint_cstring_ptr = getting the inner pointer of a temporary `CString`
.as_ptr_label = this pointer will be invalid .as_ptr_label = this pointer will be invalid
.unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime .unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
@ -331,6 +370,8 @@ lint_builtin_anonymous_params = anonymous parameters are deprecated and will be
.suggestion = try naming the parameter or explicitly ignoring it .suggestion = try naming the parameter or explicitly ignoring it
lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link} lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link}
.msg_suggestion = {$msg}
.default_suggestion = remove this attribute
lint_builtin_deprecated_attr_used = use of deprecated attribute `{$name}`: no longer used. lint_builtin_deprecated_attr_used = use of deprecated attribute `{$name}`: no longer used.
lint_builtin_deprecated_attr_default_suggestion = remove this attribute lint_builtin_deprecated_attr_default_suggestion = remove this attribute
@ -391,10 +432,16 @@ lint_builtin_incomplete_features = the feature `{$name}` is incomplete and may n
.note = see issue #{$n} <https://github.com/rust-lang/rust/issues/{$n}> for more information .note = see issue #{$n} <https://github.com/rust-lang/rust/issues/{$n}> for more information
.help = consider using `min_{$name}` instead, which is more stable and complete .help = consider using `min_{$name}` instead, which is more stable and complete
lint_builtin_clashing_extern_same_name = `{$this_fi}` redeclared with a different signature lint_builtin_unpermitted_type_init_zeroed = the type `{$ty}` does not permit zero-initialization
lint_builtin_unpermitted_type_init_unint = the type `{$ty}` does not permit being left uninitialized
lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed
lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
lint_builtin_clashing_extern_same_name = `{$this}` redeclared with a different signature
.previous_decl_label = `{$orig}` previously declared here .previous_decl_label = `{$orig}` previously declared here
.mismatch_label = this signature doesn't match the previous declaration .mismatch_label = this signature doesn't match the previous declaration
lint_builtin_clashing_extern_diff_name = `{$this_fi}` redeclares `{$orig}` with a different signature lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature
.previous_decl_label = `{$orig}` previously declared here .previous_decl_label = `{$orig}` previously declared here
.mismatch_label = this signature doesn't match the previous declaration .mismatch_label = this signature doesn't match the previous declaration
@ -403,6 +450,16 @@ lint_builtin_deref_nullptr = dereferencing a null pointer
lint_builtin_asm_labels = avoid using named labels in inline assembly lint_builtin_asm_labels = avoid using named labels in inline assembly
lint_builtin_special_module_name_used_lib = found module declaration for lib.rs
.note = lib.rs is the root of this crate's library target
.help = to refer to it from other targets, use the library's name as the path
lint_builtin_special_module_name_used_main = found module declaration for main.rs
.note = a binary crate cannot be used as library
lint_supertrait_as_deref_target = `{$t}` implements `Deref` with supertrait `{$target_principal}` as target
.label = target type is set here
lint_overruled_attribute = {$lint_level}({$lint_source}) incompatible with previous forbid lint_overruled_attribute = {$lint_level}({$lint_source}) incompatible with previous forbid
.label = overruled by previous forbid .label = overruled by previous forbid

View File

@ -1,5 +1,5 @@
use crate::lints::{ArrayIntoIterDiag, ArrayIntoIterDiagSub};
use crate::{LateContext, LateLintPass, LintContext}; use crate::{LateContext, LateLintPass, LintContext};
use rustc_errors::{fluent, Applicability};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::adjustment::{Adjust, Adjustment};
@ -118,41 +118,23 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
// to an array or to a slice. // to an array or to a slice.
_ => bug!("array type coerced to something other than array or slice"), _ => bug!("array type coerced to something other than array or slice"),
}; };
cx.struct_span_lint( let sub = if self.for_expr_span == expr.span {
Some(ArrayIntoIterDiagSub::RemoveIntoIter {
span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
})
} else if receiver_ty.is_array() {
Some(ArrayIntoIterDiagSub::UseExplicitIntoIter {
start_span: expr.span.shrink_to_lo(),
end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
})
} else {
None
};
cx.emit_spanned_lint(
ARRAY_INTO_ITER, ARRAY_INTO_ITER,
call.ident.span, call.ident.span,
fluent::lint_array_into_iter, ArrayIntoIterDiag { target, suggestion: call.ident.span, sub },
|diag| { );
diag.set_arg("target", target);
diag.span_suggestion(
call.ident.span,
fluent::use_iter_suggestion,
"iter",
Applicability::MachineApplicable,
);
if self.for_expr_span == expr.span {
diag.span_suggestion(
receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
fluent::remove_into_iter_suggestion,
"",
Applicability::MaybeIncorrect,
);
} else if receiver_ty.is_array() {
diag.multipart_suggestion(
fluent::use_explicit_into_iter_suggestion,
vec![
(expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()),
(
receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
")".into(),
),
],
Applicability::MaybeIncorrect,
);
}
diag
},
)
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -965,6 +965,7 @@ pub trait LintContext: Sized {
/// Note that this function should only be called for [`LintExpectationId`]s /// Note that this function should only be called for [`LintExpectationId`]s
/// retrieved from the current lint pass. Buffered or manually created ids can /// retrieved from the current lint pass. Buffered or manually created ids can
/// cause ICEs. /// cause ICEs.
#[rustc_lint_diagnostics]
fn fulfill_expectation(&self, expectation: LintExpectationId) { fn fulfill_expectation(&self, expectation: LintExpectationId) {
// We need to make sure that submitted expectation ids are correctly fulfilled suppressed // We need to make sure that submitted expectation ids are correctly fulfilled suppressed
// and stored between compilation sessions. To not manually do these steps, we simply create // and stored between compilation sessions. To not manually do these steps, we simply create
@ -1011,6 +1012,7 @@ impl<'tcx> LintContext for LateContext<'tcx> {
&*self.lint_store &*self.lint_store
} }
#[rustc_lint_diagnostics]
fn lookup<S: Into<MultiSpan>>( fn lookup<S: Into<MultiSpan>>(
&self, &self,
lint: &'static Lint, lint: &'static Lint,
@ -1045,6 +1047,7 @@ impl LintContext for EarlyContext<'_> {
self.builder.lint_store() self.builder.lint_store()
} }
#[rustc_lint_diagnostics]
fn lookup<S: Into<MultiSpan>>( fn lookup<S: Into<MultiSpan>>(
&self, &self,
lint: &'static Lint, lint: &'static Lint,

View File

@ -1,6 +1,8 @@
use crate::{LateContext, LateLintPass, LintContext}; use crate::{
lints::{SupertraitAsDerefTarget, SupertraitAsDerefTargetLabel},
LateContext, LateLintPass, LintContext,
};
use rustc_errors::DelayDm;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::{traits::util::supertraits, ty}; use rustc_middle::{traits::util::supertraits, ty};
use rustc_span::sym; use rustc_span::sym;
@ -71,22 +73,14 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait {
&& supertraits(cx.tcx, t_principal.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self)) && supertraits(cx.tcx, t_principal.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self))
.any(|sup| sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(cx.tcx, x)) == target_principal) .any(|sup| sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(cx.tcx, x)) == target_principal)
{ {
cx.struct_span_lint( let label = impl_.items.iter().find_map(|i| (i.ident.name == sym::Target).then_some(i.span)).map(|label| SupertraitAsDerefTargetLabel {
DEREF_INTO_DYN_SUPERTRAIT, label,
cx.tcx.def_span(item.owner_id.def_id), });
DelayDm(|| { cx.emit_spanned_lint(DEREF_INTO_DYN_SUPERTRAIT, cx.tcx.def_span(item.owner_id.def_id), SupertraitAsDerefTarget {
format!( t,
"`{t}` implements `Deref` with supertrait `{target_principal}` as target" target_principal: target_principal.to_string(),
) label,
}), });
|lint| {
if let Some(target_span) = impl_.items.iter().find_map(|i| (i.ident.name == sym::Target).then_some(i.span)) {
lint.span_label(target_span, "target type is set here");
}
lint
},
)
} }
} }
} }

View File

@ -40,6 +40,7 @@ pub struct EarlyContextAndPass<'a, T: EarlyLintPass> {
impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
// This always-inlined function is for the hot call site. // This always-inlined function is for the hot call site.
#[inline(always)] #[inline(always)]
#[allow(rustc::diagnostic_outside_of_impl)]
fn inlined_check_id(&mut self, id: ast::NodeId) { fn inlined_check_id(&mut self, id: ast::NodeId) {
for early_lint in self.context.buffered.take(id) { for early_lint in self.context.buffered.take(id) {
let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint; let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint;

View File

@ -1,5 +1,8 @@
use crate::{context::LintContext, LateContext, LateLintPass}; use crate::{
use rustc_errors::fluent; context::LintContext,
lints::{EnumIntrinsicsMemDiscriminate, EnumIntrinsicsMemVariant},
LateContext, LateLintPass,
};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::ty::{visit::TypeVisitable, Ty}; use rustc_middle::ty::{visit::TypeVisitable, Ty};
use rustc_span::{symbol::sym, Span}; use rustc_span::{symbol::sym, Span};
@ -50,11 +53,10 @@ fn enforce_mem_discriminant(
) { ) {
let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0); let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
if is_non_enum(ty_param) { if is_non_enum(ty_param) {
cx.struct_span_lint( cx.emit_spanned_lint(
ENUM_INTRINSICS_NON_ENUMS, ENUM_INTRINSICS_NON_ENUMS,
expr_span, expr_span,
fluent::lint_enum_intrinsics_mem_discriminant, EnumIntrinsicsMemDiscriminate { ty_param, note: args_span },
|lint| lint.set_arg("ty_param", ty_param).span_note(args_span, fluent::note),
); );
} }
} }
@ -62,11 +64,10 @@ fn enforce_mem_discriminant(
fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) { fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) {
let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0); let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
if is_non_enum(ty_param) { if is_non_enum(ty_param) {
cx.struct_span_lint( cx.emit_spanned_lint(
ENUM_INTRINSICS_NON_ENUMS, ENUM_INTRINSICS_NON_ENUMS,
span, span,
fluent::lint_enum_intrinsics_mem_variant, EnumIntrinsicsMemVariant { ty_param },
|lint| lint.set_arg("ty_param", ty_param).note(fluent::note),
); );
} }
} }

View File

@ -8,12 +8,12 @@ use rustc_span::{Span, Symbol};
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(lint_overruled_attribute, code = "E0453")] #[diag(lint_overruled_attribute, code = "E0453")]
pub struct OverruledAttribute { pub struct OverruledAttribute<'a> {
#[primary_span] #[primary_span]
pub span: Span, pub span: Span,
#[label] #[label]
pub overruled: Span, pub overruled: Span,
pub lint_level: String, pub lint_level: &'a str,
pub lint_source: Symbol, pub lint_source: Symbol,
#[subdiagnostic] #[subdiagnostic]
pub sub: OverruledAttributeSub, pub sub: OverruledAttributeSub,
@ -38,6 +38,7 @@ impl AddToDiagnostic for OverruledAttributeSub {
OverruledAttributeSub::NodeSource { span, reason } => { OverruledAttributeSub::NodeSource { span, reason } => {
diag.span_label(span, fluent::lint_node_source); diag.span_label(span, fluent::lint_node_source);
if let Some(rationale) = reason { if let Some(rationale) = reason {
#[allow(rustc::untranslatable_diagnostic)]
diag.note(rationale.as_str()); diag.note(rationale.as_str());
} }
} }

View File

@ -1,8 +1,7 @@
use crate::builtin; use crate::lints::{Expectation, ExpectationNote};
use rustc_errors::fluent;
use rustc_hir::HirId;
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
use rustc_middle::{lint::LintExpectation, ty::TyCtxt}; use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS;
use rustc_session::lint::LintExpectationId; use rustc_session::lint::LintExpectationId;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::Symbol; use rustc_span::Symbol;
@ -28,34 +27,17 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
if !fulfilled_expectations.contains(&id) if !fulfilled_expectations.contains(&id)
&& tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter)) && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))
{ {
emit_unfulfilled_expectation_lint(tcx, *hir_id, expectation); let rationale = expectation.reason.map(|rationale| ExpectationNote { rationale });
let note = expectation.is_unfulfilled_lint_expectations.then_some(());
tcx.emit_spanned_lint(
UNFULFILLED_LINT_EXPECTATIONS,
*hir_id,
expectation.emission_span,
Expectation { rationale, note },
);
} }
} else { } else {
unreachable!("at this stage all `LintExpectationId`s are stable"); unreachable!("at this stage all `LintExpectationId`s are stable");
} }
} }
} }
fn emit_unfulfilled_expectation_lint(
tcx: TyCtxt<'_>,
hir_id: HirId,
expectation: &LintExpectation,
) {
tcx.struct_span_lint_hir(
builtin::UNFULFILLED_LINT_EXPECTATIONS,
hir_id,
expectation.emission_span,
fluent::lint_expectation,
|lint| {
if let Some(rationale) = expectation.reason {
lint.note(rationale.as_str());
}
if expectation.is_unfulfilled_lint_expectations {
lint.note(fluent::note);
}
lint
},
);
}

View File

@ -1,7 +1,12 @@
use crate::{LateContext, LateLintPass, LintContext}; use crate::{
lints::{
ForLoopsOverFalliblesDiag, ForLoopsOverFalliblesLoopSub, ForLoopsOverFalliblesQuestionMark,
ForLoopsOverFalliblesSuggestion,
},
LateContext, LateLintPass, LintContext,
};
use hir::{Expr, Pat}; use hir::{Expr, Pat};
use rustc_errors::{Applicability, DelayDm};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause}; use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause};
use rustc_middle::ty::{self, List}; use rustc_middle::ty::{self, List};
@ -53,53 +58,29 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
_ => return, _ => return,
}; };
let msg = DelayDm(|| { let sub = if let Some(recv) = extract_iterator_next_call(cx, arg)
format!(
"for loop over {article} `{ty}`. This is more readably written as an `if let` statement",
)
});
cx.struct_span_lint(FOR_LOOPS_OVER_FALLIBLES, arg.span, msg, |lint| {
if let Some(recv) = extract_iterator_next_call(cx, arg)
&& let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span) && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
{ {
lint.span_suggestion( ForLoopsOverFalliblesLoopSub::RemoveNext { suggestion: recv.span.between(arg.span.shrink_to_hi()), recv_snip }
recv.span.between(arg.span.shrink_to_hi()),
format!("to iterate over `{recv_snip}` remove the call to `next`"),
".by_ref()",
Applicability::MaybeIncorrect
);
} else { } else {
lint.multipart_suggestion_verbose( ForLoopsOverFalliblesLoopSub::UseWhileLet { start_span: expr.span.with_hi(pat.span.lo()), end_span: pat.span.between(arg.span), var }
"to check pattern in a loop use `while let`", } ;
vec![ let question_mark = if suggest_question_mark(cx, adt, substs, expr.span) {
// NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts Some(ForLoopsOverFalliblesQuestionMark { suggestion: arg.span.shrink_to_hi() })
(expr.span.with_hi(pat.span.lo()), format!("while let {var}(")), } else {
(pat.span.between(arg.span), ") = ".to_string()), None
], };
Applicability::MaybeIncorrect let suggestion = ForLoopsOverFalliblesSuggestion {
); var,
} start_span: expr.span.with_hi(pat.span.lo()),
end_span: pat.span.between(arg.span),
};
if suggest_question_mark(cx, adt, substs, expr.span) { cx.emit_spanned_lint(
lint.span_suggestion( FOR_LOOPS_OVER_FALLIBLES,
arg.span.shrink_to_hi(), arg.span,
"consider unwrapping the `Result` with `?` to iterate over its contents", ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion },
"?", );
Applicability::MaybeIncorrect,
);
}
lint.multipart_suggestion_verbose(
"consider using `if let` to clear intent",
vec![
// NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
(expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
(pat.span.between(arg.span), ") = ".to_string()),
],
Applicability::MaybeIncorrect,
)
})
} }
} }

View File

@ -1,7 +1,12 @@
use crate::{EarlyContext, EarlyLintPass, LintContext}; use crate::{
lints::{
HiddenUnicodeCodepointsDiag, HiddenUnicodeCodepointsDiagLabels,
HiddenUnicodeCodepointsDiagSub,
},
EarlyContext, EarlyLintPass, LintContext,
};
use ast::util::unicode::{contains_text_flow_control_chars, TEXT_FLOW_CONTROL_CHARS}; use ast::util::unicode::{contains_text_flow_control_chars, TEXT_FLOW_CONTROL_CHARS};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_errors::{fluent, Applicability, SuggestionStyle};
use rustc_span::{BytePos, Span, Symbol}; use rustc_span::{BytePos, Span, Symbol};
declare_lint! { declare_lint! {
@ -60,55 +65,19 @@ impl HiddenUnicodeCodepoints {
}) })
.collect(); .collect();
cx.struct_span_lint( let count = spans.len();
let labels = point_at_inner_spans
.then_some(HiddenUnicodeCodepointsDiagLabels { spans: spans.clone() });
let sub = if point_at_inner_spans && !spans.is_empty() {
HiddenUnicodeCodepointsDiagSub::Escape { spans }
} else {
HiddenUnicodeCodepointsDiagSub::NoEscape { spans }
};
cx.emit_spanned_lint(
TEXT_DIRECTION_CODEPOINT_IN_LITERAL, TEXT_DIRECTION_CODEPOINT_IN_LITERAL,
span, span,
fluent::lint_hidden_unicode_codepoints, HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub },
|lint| {
lint.set_arg("label", label);
lint.set_arg("count", spans.len());
lint.span_label(span, fluent::label);
lint.note(fluent::note);
if point_at_inner_spans {
for (c, span) in &spans {
lint.span_label(*span, format!("{:?}", c));
}
}
if point_at_inner_spans && !spans.is_empty() {
lint.multipart_suggestion_with_style(
fluent::suggestion_remove,
spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
Applicability::MachineApplicable,
SuggestionStyle::HideCodeAlways,
);
lint.multipart_suggestion(
fluent::suggestion_escape,
spans
.into_iter()
.map(|(c, span)| {
let c = format!("{:?}", c);
(span, c[1..c.len() - 1].to_string())
})
.collect(),
Applicability::MachineApplicable,
);
} else {
// FIXME: in other suggestions we've reversed the inner spans of doc comments. We
// should do the same here to provide the same good suggestions as we do for
// literals above.
lint.set_arg(
"escaped",
spans
.into_iter()
.map(|(c, _)| format!("{:?}", c))
.collect::<Vec<String>>()
.join(", "),
);
lint.note(fluent::suggestion_remove);
lint.note(fluent::no_suggestion_note_escape);
}
lint
},
); );
} }
} }

View File

@ -1,9 +1,12 @@
//! Some lints that are only useful in the compiler or crates that use compiler internals, such as //! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy. //! Clippy.
use crate::lints::{
BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistantDocKeyword,
QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag,
};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_errors::{fluent, Applicability};
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath}; use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath};
use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind}; use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind};
@ -29,20 +32,15 @@ impl LateLintPass<'_> for DefaultHashTypes {
// don't lint imports, only actual usages // don't lint imports, only actual usages
return; return;
} }
let replace = match cx.tcx.get_diagnostic_name(def_id) { let preferred = match cx.tcx.get_diagnostic_name(def_id) {
Some(sym::HashMap) => "FxHashMap", Some(sym::HashMap) => "FxHashMap",
Some(sym::HashSet) => "FxHashSet", Some(sym::HashSet) => "FxHashSet",
_ => return, _ => return,
}; };
cx.struct_span_lint( cx.emit_spanned_lint(
DEFAULT_HASH_TYPES, DEFAULT_HASH_TYPES,
path.span, path.span,
fluent::lint_default_hash_types, DefaultHashTypesDiag { preferred, used: cx.tcx.item_name(def_id) },
|lint| {
lint.set_arg("preferred", replace)
.set_arg("used", cx.tcx.item_name(def_id))
.note(fluent::note)
},
); );
} }
} }
@ -83,12 +81,11 @@ impl LateLintPass<'_> for QueryStability {
if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) { if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) {
let def_id = instance.def_id(); let def_id = instance.def_id();
if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) { if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
cx.struct_span_lint( cx.emit_spanned_lint(
POTENTIAL_QUERY_INSTABILITY, POTENTIAL_QUERY_INSTABILITY,
span, span,
fluent::lint_query_instability, QueryInstability { query: cx.tcx.item_name(def_id) },
|lint| lint.set_arg("query", cx.tcx.item_name(def_id)).note(fluent::note), );
)
} }
} }
} }
@ -126,14 +123,8 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
let span = path.span.with_hi( let span = path.span.with_hi(
segment.args.map_or(segment.ident.span, |a| a.span_ext).hi() segment.args.map_or(segment.ident.span, |a| a.span_ext).hi()
); );
cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, fluent::lint_tykind_kind, |lint| { cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind {
lint suggestion: span,
.span_suggestion(
span,
fluent::suggestion,
"ty",
Applicability::MaybeIncorrect, // ty maybe needs an import
)
}); });
} }
} }
@ -190,39 +181,17 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
match span { match span {
Some(span) => { Some(span) => {
cx.struct_span_lint( cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind {
USAGE_OF_TY_TYKIND, suggestion: span,
path.span, });
fluent::lint_tykind_kind,
|lint| lint.span_suggestion(
span,
fluent::suggestion,
"ty",
Applicability::MaybeIncorrect, // ty maybe needs an import
)
)
}, },
None => cx.struct_span_lint( None => cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindDiag),
USAGE_OF_TY_TYKIND,
path.span,
fluent::lint_tykind,
|lint| lint.help(fluent::help)
)
}
} else if !ty.span.from_expansion() && let Some(t) = is_ty_or_ty_ctxt(cx, &path) {
if path.segments.len() > 1 {
cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, fluent::lint_ty_qualified, |lint| {
lint
.set_arg("ty", t.clone())
.span_suggestion(
path.span,
fluent::suggestion,
t,
// The import probably needs to be changed
Applicability::MaybeIncorrect,
)
})
} }
} else if !ty.span.from_expansion() && path.segments.len() > 1 && let Some(t) = is_ty_or_ty_ctxt(cx, &path) {
cx.emit_spanned_lint(USAGE_OF_QUALIFIED_TY, path.span, TyQualified {
ty: t.clone(),
suggestion: path.span,
});
} }
} }
_ => {} _ => {}
@ -303,12 +272,11 @@ impl EarlyLintPass for LintPassImpl {
&& call_site.ctxt().outer_expn_data().kind && call_site.ctxt().outer_expn_data().kind
!= ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass) != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
{ {
cx.struct_span_lint( cx.emit_spanned_lint(
LINT_PASS_IMPL_WITHOUT_MACRO, LINT_PASS_IMPL_WITHOUT_MACRO,
lint_pass.path.span, lint_pass.path.span,
fluent::lint_lintpass_by_hand, LintPassByHand,
|lint| lint.help(fluent::help), );
)
} }
} }
} }
@ -338,17 +306,16 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword {
if let Some(list) = attr.meta_item_list() { if let Some(list) = attr.meta_item_list() {
for nested in list { for nested in list {
if nested.has_name(sym::keyword) { if nested.has_name(sym::keyword) {
let v = nested let keyword = nested
.value_str() .value_str()
.expect("#[doc(keyword = \"...\")] expected a value!"); .expect("#[doc(keyword = \"...\")] expected a value!");
if is_doc_keyword(v) { if is_doc_keyword(keyword) {
return; return;
} }
cx.struct_span_lint( cx.emit_spanned_lint(
EXISTING_DOC_KEYWORD, EXISTING_DOC_KEYWORD,
attr.span, attr.span,
fluent::lint_non_existant_doc_keyword, NonExistantDocKeyword { keyword },
|lint| lint.set_arg("keyword", v).help(fluent::help),
); );
} }
} }
@ -407,12 +374,7 @@ impl LateLintPass<'_> for Diagnostics {
} }
debug!(?found_impl); debug!(?found_impl);
if !found_parent_with_attr && !found_impl { if !found_parent_with_attr && !found_impl {
cx.struct_span_lint( cx.emit_spanned_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
DIAGNOSTIC_OUTSIDE_OF_IMPL,
span,
fluent::lint_diag_out_of_impl,
|lint| lint,
)
} }
let mut found_diagnostic_message = false; let mut found_diagnostic_message = false;
@ -428,12 +390,7 @@ impl LateLintPass<'_> for Diagnostics {
} }
debug!(?found_diagnostic_message); debug!(?found_diagnostic_message);
if !found_parent_with_attr && !found_diagnostic_message { if !found_parent_with_attr && !found_diagnostic_message {
cx.struct_span_lint( cx.emit_spanned_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag);
UNTRANSLATABLE_DIAGNOSTIC,
span,
fluent::lint_untranslatable_diag,
|lint| lint,
)
} }
} }
} }
@ -465,9 +422,9 @@ impl LateLintPass<'_> for BadOptAccess {
let Some(lit) = item.lit() && let Some(lit) = item.lit() &&
let ast::LitKind::Str(val, _) = lit.kind let ast::LitKind::Str(val, _) = lit.kind
{ {
cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, val.as_str(), |lint| cx.emit_spanned_lint(BAD_OPT_ACCESS, expr.span, BadOptAccessDiag {
lint msg: val.as_str(),
); });
} }
} }
} }

View File

@ -1,5 +1,8 @@
use crate::{LateContext, LateLintPass, LintContext}; use crate::{
use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan}; lints::{NonBindingLet, NonBindingLetSub},
LateContext, LateLintPass, LintContext,
};
use rustc_errors::MultiSpan;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_span::Symbol; use rustc_span::Symbol;
@ -119,6 +122,11 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
_ => false, _ => false,
}; };
let sub = NonBindingLetSub {
suggestion: local.pat.span,
multi_suggestion_start: local.span.until(init.span),
multi_suggestion_end: init.span.shrink_to_hi(),
};
if is_sync_lock { if is_sync_lock {
let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]); let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
span.push_span_label( span.push_span_label(
@ -129,41 +137,14 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
init.span, init.span,
"this binding will immediately drop the value assigned to it".to_string(), "this binding will immediately drop the value assigned to it".to_string(),
); );
cx.struct_span_lint( cx.emit_spanned_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub });
LET_UNDERSCORE_LOCK,
span,
"non-binding let on a synchronization lock",
|lint| build_lint(lint, local, init.span),
)
} else { } else {
cx.struct_span_lint( cx.emit_spanned_lint(
LET_UNDERSCORE_DROP, LET_UNDERSCORE_DROP,
local.span, local.span,
"non-binding let on a type that implements `Drop`", NonBindingLet::DropType { sub },
|lint| build_lint(lint, local, init.span), );
)
} }
} }
} }
} }
fn build_lint<'a, 'b>(
lint: &'a mut DiagnosticBuilder<'b, ()>,
local: &hir::Local<'_>,
init_span: rustc_span::Span,
) -> &'a mut DiagnosticBuilder<'b, ()> {
lint.span_suggestion_verbose(
local.pat.span,
"consider binding to an unused variable to avoid immediately dropping the value",
"_unused",
Applicability::MachineApplicable,
)
.multipart_suggestion(
"consider immediately dropping the value",
vec![
(local.span.until(init_span), "drop(".to_string()),
(init_span.shrink_to_hi(), ")".to_string()),
],
Applicability::MachineApplicable,
)
}

View File

@ -1,9 +1,13 @@
use crate::context::{CheckLintNameResult, LintStore}; use crate::context::{CheckLintNameResult, LintStore};
use crate::late::unerased_lint_store; use crate::late::unerased_lint_store;
use crate::lints::{
DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAtributeLint, RenamedOrRemovedLint,
RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan}; use rustc_errors::{fluent, DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::HirId; use rustc_hir::HirId;
@ -15,6 +19,7 @@ use rustc_middle::lint::{
}; };
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_middle::ty::{RegisteredTools, TyCtxt};
use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES};
use rustc_session::lint::{ use rustc_session::lint::{
builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS}, builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
Level, Lint, LintExpectationId, LintId, Level, Lint, LintExpectationId, LintId,
@ -583,57 +588,32 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
old_src, old_src,
id_name id_name
); );
let sub = match old_src {
let decorate_diag = |diag: &mut Diagnostic| { LintLevelSource::Default => {
diag.span_label(src.span(), "overruled by previous forbid"); OverruledAttributeSub::DefaultSource { id: id.to_string() }
match old_src {
LintLevelSource::Default => {
diag.note(&format!(
"`forbid` lint level is the default for {}",
id.to_string()
));
}
LintLevelSource::Node { span, reason, .. } => {
diag.span_label(span, "`forbid` level set here");
if let Some(rationale) = reason {
diag.note(rationale.as_str());
}
}
LintLevelSource::CommandLine(_, _) => {
diag.note("`forbid` lint level was set on command line");
}
} }
LintLevelSource::Node { span, reason, .. } => {
OverruledAttributeSub::NodeSource { span, reason }
}
LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource,
}; };
if !fcw_warning { if !fcw_warning {
self.sess.emit_err(OverruledAttribute { self.sess.emit_err(OverruledAttribute {
span: src.span(), span: src.span(),
overruled: src.span(), overruled: src.span(),
lint_level: level.as_str().to_string(), lint_level: level.as_str(),
lint_source: src.name(), lint_source: src.name(),
sub: match old_src { sub,
LintLevelSource::Default => {
OverruledAttributeSub::DefaultSource { id: id.to_string() }
}
LintLevelSource::Node { span, reason, .. } => {
OverruledAttributeSub::NodeSource { span, reason }
}
LintLevelSource::CommandLine(_, _) => {
OverruledAttributeSub::CommandLineSource
}
},
}); });
} else { } else {
self.struct_lint( self.emit_spanned_lint(
FORBIDDEN_LINT_GROUPS, FORBIDDEN_LINT_GROUPS,
Some(src.span().into()), src.span().into(),
format!( OverruledAtributeLint {
"{}({}) incompatible with previous forbid", overruled: src.span(),
level.as_str(), lint_level: level.as_str(),
src.name(), lint_source: src.name(),
), sub,
|lint| {
decorate_diag(lint);
lint
}, },
); );
} }
@ -858,25 +838,13 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
} }
Err((Some(ids), ref new_lint_name)) => { Err((Some(ids), ref new_lint_name)) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS; let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (lvl, src) = self.provider.get_lint_level(lint, &sess); self.emit_spanned_lint(
struct_lint_level(
self.sess,
lint, lint,
lvl, sp.into(),
src, DeprecatedLintName {
Some(sp.into()), name,
format!( suggestion: sp,
"lint name `{}` is deprecated \ replace: &new_lint_name,
and may not have an effect in the future.",
name
),
|lint| {
lint.span_suggestion(
sp,
"change it to",
new_lint_name,
Applicability::MachineApplicable,
)
}, },
); );
@ -917,54 +885,29 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
_ if !self.warn_about_weird_lints => {} _ if !self.warn_about_weird_lints => {}
CheckLintNameResult::Warning(msg, renamed) => { CheckLintNameResult::Warning(msg, renamed) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS; let suggestion =
let (renamed_lint_level, src) = self.provider.get_lint_level(lint, &sess); renamed.as_ref().map(|replace| RenamedOrRemovedLintSuggestion {
struct_lint_level( suggestion: sp,
self.sess, replace: replace.as_str(),
lint, });
renamed_lint_level, self.emit_spanned_lint(
src, RENAMED_AND_REMOVED_LINTS,
Some(sp.into()), sp.into(),
msg, RenamedOrRemovedLint { msg, suggestion },
|lint| {
if let Some(new_name) = &renamed {
lint.span_suggestion(
sp,
"use the new name",
new_name,
Applicability::MachineApplicable,
);
}
lint
},
); );
} }
CheckLintNameResult::NoLint(suggestion) => { CheckLintNameResult::NoLint(suggestion) => {
let lint = builtin::UNKNOWN_LINTS;
let (level, src) = self.provider.get_lint_level(lint, self.sess);
let name = if let Some(tool_ident) = tool_ident { let name = if let Some(tool_ident) = tool_ident {
format!("{}::{}", tool_ident.name, name) format!("{}::{}", tool_ident.name, name)
} else { } else {
name.to_string() name.to_string()
}; };
struct_lint_level( let suggestion = suggestion
self.sess, .map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
lint, self.emit_spanned_lint(
level, UNKNOWN_LINTS,
src, sp.into(),
Some(sp.into()), UnknownLint { name, suggestion },
format!("unknown lint: `{}`", name),
|lint| {
if let Some(suggestion) = suggestion {
lint.span_suggestion(
sp,
"did you mean",
suggestion,
Applicability::MaybeIncorrect,
);
}
lint
},
); );
} }
} }
@ -1010,20 +953,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
continue continue
}; };
let lint = builtin::UNUSED_ATTRIBUTES; self.emit_spanned_lint(
let (lint_level, lint_src) = self.provider.get_lint_level(lint, &self.sess); UNUSED_ATTRIBUTES,
struct_lint_level( lint_attr_span.into(),
self.sess, IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
lint,
lint_level,
lint_src,
Some(lint_attr_span.into()),
format!(
"{}({}) is ignored unless specified at crate level",
level.as_str(),
lint_attr_name
),
|lint| lint,
); );
// don't set a separate error for every lint in the group // don't set a separate error for every lint in the group
break; break;
@ -1047,11 +980,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
level, level,
src, src,
Some(span.into()), Some(span.into()),
format!("unknown lint: `{}`", lint_id.lint.name_lower()), fluent::lint_unknown_gated_lint,
|lint| { |lint| {
lint.note( lint.set_arg("name", lint_id.lint.name_lower());
&format!("the `{}` lint is unstable", lint_id.lint.name_lower(),), lint.note(fluent::note);
);
add_feature_diagnostics(lint, &self.sess.parse_sess, feature); add_feature_diagnostics(lint, &self.sess.parse_sess, feature);
lint lint
}, },
@ -1086,6 +1018,25 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
let (level, src) = self.lint_level(lint); let (level, src) = self.lint_level(lint);
struct_lint_level(self.sess, lint, level, src, span, msg, decorate) struct_lint_level(self.sess, lint, level, src, span, msg, decorate)
} }
pub fn emit_spanned_lint(
&self,
lint: &'static Lint,
span: MultiSpan,
decorate: impl for<'a> DecorateLint<'a, ()>,
) {
let (level, src) = self.lint_level(lint);
struct_lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| {
decorate.decorate_lint(lint)
});
}
pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>) {
let (level, src) = self.lint_level(lint);
struct_lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| {
decorate.decorate_lint(lint)
});
}
} }
pub(crate) fn provide(providers: &mut Providers) { pub(crate) fn provide(providers: &mut Providers) {

View File

@ -38,6 +38,8 @@
#![feature(never_type)] #![feature(never_type)]
#![feature(rustc_attrs)] #![feature(rustc_attrs)]
#![recursion_limit = "256"] #![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#[macro_use] #[macro_use]
extern crate rustc_middle; extern crate rustc_middle;
@ -60,6 +62,7 @@ mod internal;
mod late; mod late;
mod let_underscore; mod let_underscore;
mod levels; mod levels;
mod lints;
mod methods; mod methods;
mod non_ascii_idents; mod non_ascii_idents;
mod non_fmt_panic; mod non_fmt_panic;

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
use crate::lints::CStringPtr;
use crate::LateContext; use crate::LateContext;
use crate::LateLintPass; use crate::LateLintPass;
use crate::LintContext; use crate::LintContext;
use rustc_errors::fluent;
use rustc_hir::{Expr, ExprKind, PathSegment}; use rustc_hir::{Expr, ExprKind, PathSegment};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_span::{symbol::sym, ExpnKind, Span}; use rustc_span::{symbol::sym, ExpnKind, Span};
@ -90,16 +90,10 @@ fn lint_cstring_as_ptr(
if cx.tcx.is_diagnostic_item(sym::Result, def.did()) { if cx.tcx.is_diagnostic_item(sym::Result, def.did()) {
if let ty::Adt(adt, _) = substs.type_at(0).kind() { if let ty::Adt(adt, _) = substs.type_at(0).kind() {
if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) { if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) {
cx.struct_span_lint( cx.emit_spanned_lint(
TEMPORARY_CSTRING_AS_PTR, TEMPORARY_CSTRING_AS_PTR,
as_ptr_span, as_ptr_span,
fluent::lint_cstring_ptr, CStringPtr { as_ptr: as_ptr_span, unwrap: unwrap.span },
|diag| {
diag.span_label(as_ptr_span, fluent::as_ptr_label)
.span_label(unwrap.span, fluent::unwrap_label)
.note(fluent::note)
.help(fluent::help)
},
); );
} }
} }

View File

@ -1,7 +1,10 @@
use crate::lints::{
ConfusableIdentifierPair, IdentifierNonAsciiChar, IdentifierUncommonCodepoints,
MixedScriptConfusables,
};
use crate::{EarlyContext, EarlyLintPass, LintContext}; use crate::{EarlyContext, EarlyLintPass, LintContext};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::fluent;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
declare_lint! { declare_lint! {
@ -180,21 +183,11 @@ impl EarlyLintPass for NonAsciiIdents {
continue; continue;
} }
has_non_ascii_idents = true; has_non_ascii_idents = true;
cx.struct_span_lint( cx.emit_spanned_lint(NON_ASCII_IDENTS, sp, IdentifierNonAsciiChar);
NON_ASCII_IDENTS,
sp,
fluent::lint_identifier_non_ascii_char,
|lint| lint,
);
if check_uncommon_codepoints if check_uncommon_codepoints
&& !symbol_str.chars().all(GeneralSecurityProfile::identifier_allowed) && !symbol_str.chars().all(GeneralSecurityProfile::identifier_allowed)
{ {
cx.struct_span_lint( cx.emit_spanned_lint(UNCOMMON_CODEPOINTS, sp, IdentifierUncommonCodepoints);
UNCOMMON_CODEPOINTS,
sp,
fluent::lint_identifier_uncommon_codepoints,
|lint| lint,
)
} }
} }
@ -222,14 +215,13 @@ impl EarlyLintPass for NonAsciiIdents {
.entry(skeleton_sym) .entry(skeleton_sym)
.and_modify(|(existing_symbol, existing_span, existing_is_ascii)| { .and_modify(|(existing_symbol, existing_span, existing_is_ascii)| {
if !*existing_is_ascii || !is_ascii { if !*existing_is_ascii || !is_ascii {
cx.struct_span_lint( cx.emit_spanned_lint(
CONFUSABLE_IDENTS, CONFUSABLE_IDENTS,
sp, sp,
fluent::lint_confusable_identifier_pair, ConfusableIdentifierPair {
|lint| { existing_sym: *existing_symbol,
lint.set_arg("existing_sym", *existing_symbol) sym: symbol,
.set_arg("sym", symbol) label: *existing_span,
.span_label(*existing_span, fluent::label)
}, },
); );
} }
@ -331,24 +323,18 @@ impl EarlyLintPass for NonAsciiIdents {
} }
for ((sp, ch_list), script_set) in lint_reports { for ((sp, ch_list), script_set) in lint_reports {
cx.struct_span_lint( let mut includes = String::new();
for (idx, ch) in ch_list.into_iter().enumerate() {
if idx != 0 {
includes += ", ";
}
let char_info = format!("'{}' (U+{:04X})", ch, ch as u32);
includes += &char_info;
}
cx.emit_spanned_lint(
MIXED_SCRIPT_CONFUSABLES, MIXED_SCRIPT_CONFUSABLES,
sp, sp,
fluent::lint_mixed_script_confusables, MixedScriptConfusables { set: script_set.to_string(), includes },
|lint| {
let mut includes = String::new();
for (idx, ch) in ch_list.into_iter().enumerate() {
if idx != 0 {
includes += ", ";
}
let char_info = format!("'{}' (U+{:04X})", ch, ch as u32);
includes += &char_info;
}
lint.set_arg("set", script_set.to_string())
.set_arg("includes", includes)
.note(fluent::includes_note)
.note(fluent::note)
},
); );
} }
} }

View File

@ -1,3 +1,4 @@
use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused};
use crate::{LateContext, LateLintPass, LintContext}; use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_errors::{fluent, Applicability}; use rustc_errors::{fluent, Applicability};
@ -118,6 +119,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
arg_span = expn.call_site; arg_span = expn.call_site;
} }
#[allow(rustc::diagnostic_outside_of_impl)]
cx.struct_span_lint(NON_FMT_PANICS, arg_span, fluent::lint_non_fmt_panic, |lint| { cx.struct_span_lint(NON_FMT_PANICS, arg_span, fluent::lint_non_fmt_panic, |lint| {
lint.set_arg("name", symbol); lint.set_arg("name", symbol);
lint.note(fluent::note); lint.note(fluent::note);
@ -253,25 +255,14 @@ fn check_panic_str<'tcx>(
.map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end))) .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end)))
.collect(), .collect(),
}; };
cx.struct_span_lint(NON_FMT_PANICS, arg_spans, fluent::lint_non_fmt_panic_unused, |lint| { cx.emit_spanned_lint(
lint.set_arg("count", n_arguments); NON_FMT_PANICS,
lint.note(fluent::note); arg_spans,
if is_arg_inside_call(arg.span, span) { NonFmtPanicUnused {
lint.span_suggestion( count: n_arguments,
arg.span.shrink_to_hi(), suggestion: is_arg_inside_call(arg.span, span).then_some(arg.span),
fluent::add_args_suggestion, },
", ...", );
Applicability::HasPlaceholders,
);
lint.span_suggestion(
arg.span.shrink_to_lo(),
fluent::add_fmt_suggestion,
"\"{}\", ",
Applicability::MachineApplicable,
);
}
lint
});
} else { } else {
let brace_spans: Option<Vec<_>> = let brace_spans: Option<Vec<_>> =
snippet.filter(|s| s.starts_with('"') || s.starts_with("r#")).map(|s| { snippet.filter(|s| s.starts_with('"') || s.starts_with("r#")).map(|s| {
@ -281,22 +272,12 @@ fn check_panic_str<'tcx>(
.collect() .collect()
}); });
let count = brace_spans.as_ref().map(|v| v.len()).unwrap_or(/* any number >1 */ 2); let count = brace_spans.as_ref().map(|v| v.len()).unwrap_or(/* any number >1 */ 2);
cx.struct_span_lint( cx.emit_spanned_lint(
NON_FMT_PANICS, NON_FMT_PANICS,
brace_spans.unwrap_or_else(|| vec![span]), brace_spans.unwrap_or_else(|| vec![span]),
fluent::lint_non_fmt_panic_braces, NonFmtPanicBraces {
|lint| { count,
lint.set_arg("count", count); suggestion: is_arg_inside_call(arg.span, span).then_some(arg.span.shrink_to_lo()),
lint.note(fluent::note);
if is_arg_inside_call(arg.span, span) {
lint.span_suggestion(
arg.span.shrink_to_lo(),
fluent::suggestion,
"\"{}\", ",
Applicability::MachineApplicable,
);
}
lint
}, },
); );
} }

View File

@ -1,7 +1,10 @@
use crate::lints::{
NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub,
NonUpperCaseGlobal, NonUpperCaseGlobalSub,
};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_attr as attr; use rustc_attr as attr;
use rustc_errors::{fluent, Applicability};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
@ -136,30 +139,17 @@ impl NonCamelCaseTypes {
let name = ident.name.as_str(); let name = ident.name.as_str();
if !is_camel_case(name) { if !is_camel_case(name) {
cx.struct_span_lint( let cc = to_camel_case(name);
let sub = if *name != cc {
NonCamelCaseTypeSub::Suggestion { span: ident.span, replace: cc }
} else {
NonCamelCaseTypeSub::Label { span: ident.span }
};
cx.emit_spanned_lint(
NON_CAMEL_CASE_TYPES, NON_CAMEL_CASE_TYPES,
ident.span, ident.span,
fluent::lint_non_camel_case_type, NonCamelCaseType { sort, name, sub },
|lint| { );
let cc = to_camel_case(name);
// We cannot provide meaningful suggestions
// if the characters are in the category of "Lowercase Letter".
if *name != cc {
lint.span_suggestion(
ident.span,
fluent::suggestion,
to_camel_case(name),
Applicability::MaybeIncorrect,
);
} else {
lint.span_label(ident.span, fluent::label);
}
lint.set_arg("sort", sort);
lint.set_arg("name", name);
lint
},
)
} }
} }
} }
@ -294,47 +284,37 @@ impl NonSnakeCase {
let name = ident.name.as_str(); let name = ident.name.as_str();
if !is_snake_case(name) { if !is_snake_case(name) {
cx.struct_span_lint(NON_SNAKE_CASE, ident.span, fluent::lint_non_snake_case, |lint| { let span = ident.span;
let sc = NonSnakeCase::to_snake_case(name); let sc = NonSnakeCase::to_snake_case(name);
// We cannot provide meaningful suggestions // We cannot provide meaningful suggestions
// if the characters are in the category of "Uppercase Letter". // if the characters are in the category of "Uppercase Letter".
if name != sc { let sub = if name != sc {
// We have a valid span in almost all cases, but we don't have one when linting a crate // We have a valid span in almost all cases, but we don't have one when linting a crate
// name provided via the command line. // name provided via the command line.
if !ident.span.is_dummy() { if !span.is_dummy() {
let sc_ident = Ident::from_str_and_span(&sc, ident.span); let sc_ident = Ident::from_str_and_span(&sc, span);
let (message, suggestion) = if sc_ident.is_reserved() { if sc_ident.is_reserved() {
// We shouldn't suggest a reserved identifier to fix non-snake-case identifiers. // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers.
// Instead, recommend renaming the identifier entirely or, if permitted, // Instead, recommend renaming the identifier entirely or, if permitted,
// escaping it to create a raw identifier. // escaping it to create a raw identifier.
if sc_ident.name.can_be_raw() { if sc_ident.name.can_be_raw() {
(fluent::rename_or_convert_suggestion, sc_ident.to_string()) NonSnakeCaseDiagSub::RenameOrConvertSuggestion {
} else { span,
lint.note(fluent::cannot_convert_note); suggestion: sc_ident,
(fluent::rename_suggestion, String::new())
} }
} else { } else {
(fluent::convert_suggestion, sc.clone()) NonSnakeCaseDiagSub::SuggestionAndNote { span }
}; }
lint.span_suggestion(
ident.span,
message,
suggestion,
Applicability::MaybeIncorrect,
);
} else { } else {
lint.help(fluent::help); NonSnakeCaseDiagSub::ConvertSuggestion { span, suggestion: sc.clone() }
} }
} else { } else {
lint.span_label(ident.span, fluent::label); NonSnakeCaseDiagSub::Help
} }
} else {
lint.set_arg("sort", sort); NonSnakeCaseDiagSub::Label { span }
lint.set_arg("name", name); };
lint.set_arg("sc", sc); cx.emit_spanned_lint(NON_SNAKE_CASE, span, NonSnakeCaseDiag { sort, name, sc, sub });
lint
});
} }
} }
} }
@ -490,30 +470,19 @@ impl NonUpperCaseGlobals {
fn check_upper_case(cx: &LateContext<'_>, sort: &str, ident: &Ident) { fn check_upper_case(cx: &LateContext<'_>, sort: &str, ident: &Ident) {
let name = ident.name.as_str(); let name = ident.name.as_str();
if name.chars().any(|c| c.is_lowercase()) { if name.chars().any(|c| c.is_lowercase()) {
cx.struct_span_lint( let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
// We cannot provide meaningful suggestions
// if the characters are in the category of "Lowercase Letter".
let sub = if *name != uc {
NonUpperCaseGlobalSub::Suggestion { span: ident.span, replace: uc }
} else {
NonUpperCaseGlobalSub::Label { span: ident.span }
};
cx.emit_spanned_lint(
NON_UPPER_CASE_GLOBALS, NON_UPPER_CASE_GLOBALS,
ident.span, ident.span,
fluent::lint_non_upper_case_global, NonUpperCaseGlobal { sort, name, sub },
|lint| { );
let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
// We cannot provide meaningful suggestions
// if the characters are in the category of "Lowercase Letter".
if *name != uc {
lint.span_suggestion(
ident.span,
fluent::suggestion,
uc,
Applicability::MaybeIncorrect,
);
} else {
lint.span_label(ident.span, fluent::label);
}
lint.set_arg("sort", sort);
lint.set_arg("name", name);
lint
},
)
} }
} }
} }

View File

@ -1,7 +1,7 @@
use crate::context::LintContext; use crate::context::LintContext;
use crate::lints::NoopMethodCallDiag;
use crate::LateContext; use crate::LateContext;
use crate::LateLintPass; use crate::LateLintPass;
use rustc_errors::fluent;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty; use rustc_middle::ty;
@ -85,11 +85,10 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
} }
let expr_span = expr.span; let expr_span = expr.span;
let span = expr_span.with_lo(receiver.span.hi()); let span = expr_span.with_lo(receiver.span.hi());
cx.struct_span_lint(NOOP_METHOD_CALL, span, fluent::lint_noop_method_call, |lint| { cx.emit_spanned_lint(
lint.set_arg("method", call.ident.name) NOOP_METHOD_CALL,
.set_arg("receiver_ty", receiver_ty) span,
.span_label(span, fluent::label) NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span },
.note(fluent::note) );
});
} }
} }

View File

@ -1,5 +1,5 @@
use crate::lints::PassByValueDiag;
use crate::{LateContext, LateLintPass, LintContext}; use crate::{LateContext, LateLintPass, LintContext};
use rustc_errors::{fluent, Applicability};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::{GenericArg, PathSegment, QPath, TyKind}; use rustc_hir::{GenericArg, PathSegment, QPath, TyKind};
@ -29,20 +29,11 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue {
} }
} }
if let Some(t) = path_for_pass_by_value(cx, &inner_ty) { if let Some(t) = path_for_pass_by_value(cx, &inner_ty) {
cx.struct_span_lint( cx.emit_spanned_lint(
PASS_BY_VALUE, PASS_BY_VALUE,
ty.span, ty.span,
fluent::lint_pass_by_value, PassByValueDiag { ty: t.clone(), suggestion: ty.span },
|lint| { );
lint.set_arg("ty", t.clone()).span_suggestion(
ty.span,
fluent::suggestion,
t,
// Changing type of function argument
Applicability::MaybeIncorrect,
)
},
)
} }
} }
_ => {} _ => {}

View File

@ -1,6 +1,5 @@
use crate::{EarlyContext, EarlyLintPass, LintContext}; use crate::{lints::RedundantSemicolonsDiag, EarlyContext, EarlyLintPass, LintContext};
use rustc_ast::{Block, StmtKind}; use rustc_ast::{Block, StmtKind};
use rustc_errors::{fluent, Applicability};
use rustc_span::Span; use rustc_span::Span;
declare_lint! { declare_lint! {
@ -48,18 +47,10 @@ fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, boo
return; return;
} }
cx.struct_span_lint( cx.emit_spanned_lint(
REDUNDANT_SEMICOLONS, REDUNDANT_SEMICOLONS,
span, span,
fluent::lint_redundant_semicolons, RedundantSemicolonsDiag { multiple, suggestion: span },
|lint| {
lint.set_arg("multiple", multiple).span_suggestion(
span,
fluent::suggestion,
"",
Applicability::MaybeIncorrect,
)
},
); );
} }
} }

View File

@ -1,7 +1,7 @@
use crate::lints::{DropGlue, DropTraitConstraintsDiag};
use crate::LateContext; use crate::LateContext;
use crate::LateLintPass; use crate::LateLintPass;
use crate::LintContext; use crate::LintContext;
use rustc_errors::fluent;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -101,17 +101,13 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
if trait_predicate.trait_ref.self_ty().is_impl_trait() { if trait_predicate.trait_ref.self_ty().is_impl_trait() {
continue; continue;
} }
let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
continue; return
}; };
cx.struct_span_lint( cx.emit_spanned_lint(
DROP_BOUNDS, DROP_BOUNDS,
span, span,
fluent::lint_drop_trait_constraints, DropTraitConstraintsDiag { predicate, tcx: cx.tcx, def_id },
|lint| {
lint.set_arg("predicate", predicate)
.set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
},
); );
} }
} }
@ -123,12 +119,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
}; };
for bound in &bounds[..] { for bound in &bounds[..] {
let def_id = bound.trait_ref.trait_def_id(); let def_id = bound.trait_ref.trait_def_id();
if cx.tcx.lang_items().drop_trait() == def_id if cx.tcx.lang_items().drop_trait() == def_id {
&& let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
{ return
cx.struct_span_lint(DYN_DROP, bound.span, fluent::lint_drop_glue, |lint| { };
lint.set_arg("needs_drop", cx.tcx.def_path_str(needs_drop)) cx.emit_spanned_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
});
} }
} }
} }

View File

@ -1,11 +1,16 @@
use crate::lints::{
AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign,
OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
RangeEndpointOutOfRange, UnusedComparisons, VariantSizeDifferencesDiag,
};
use crate::{LateContext, LateLintPass, LintContext}; use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_attr as attr; use rustc_attr as attr;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{fluent, Applicability, DiagnosticMessage}; use rustc_errors::{fluent, DiagnosticMessage};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
use rustc_macros::LintDiagnostic;
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
@ -146,32 +151,22 @@ fn lint_overflowing_range_endpoint<'tcx>(
}; };
let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false }; let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false };
cx.struct_span_lint( use rustc_ast::{LitIntType, LitKind};
let suffix = match lit.node {
LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
LitKind::Int(_, LitIntType::Unsuffixed) => "",
_ => bug!(),
};
cx.emit_spanned_lint(
OVERFLOWING_LITERALS, OVERFLOWING_LITERALS,
struct_expr.span, struct_expr.span,
fluent::lint_range_endpoint_out_of_range, RangeEndpointOutOfRange {
|lint| { ty,
use ast::{LitIntType, LitKind}; suggestion: struct_expr.span,
start,
lint.set_arg("ty", ty); literal: lit_val - 1,
suffix,
// We need to preserve the literal's suffix,
// as it may determine typing information.
let suffix = match lit.node {
LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
LitKind::Int(_, LitIntType::Unsuffixed) => "",
_ => bug!(),
};
let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
lint.span_suggestion(
struct_expr.span,
fluent::suggestion,
suggestion,
Applicability::MachineApplicable,
);
lint
}, },
); );
@ -228,58 +223,37 @@ fn report_bin_hex_error(
val: u128, val: u128,
negative: bool, negative: bool,
) { ) {
cx.struct_span_lint( let (t, actually) = match ty {
OVERFLOWING_LITERALS, attr::IntType::SignedInt(t) => {
expr.span, let actually = if negative {
fluent::lint_overflowing_bin_hex, -(size.sign_extend(val) as i128)
|lint| {
let (t, actually) = match ty {
attr::IntType::SignedInt(t) => {
let actually = if negative {
-(size.sign_extend(val) as i128)
} else {
size.sign_extend(val) as i128
};
(t.name_str(), actually.to_string())
}
attr::IntType::UnsignedInt(t) => {
let actually = size.truncate(val);
(t.name_str(), actually.to_string())
}
};
if negative {
// If the value is negative,
// emits a note about the value itself, apart from the literal.
lint.note(fluent::negative_note);
lint.note(fluent::negative_becomes_note);
} else { } else {
lint.note(fluent::positive_note); size.sign_extend(val) as i128
};
(t.name_str(), actually.to_string())
}
attr::IntType::UnsignedInt(t) => {
let actually = size.truncate(val);
(t.name_str(), actually.to_string())
}
};
let sign =
if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map(
|suggestion_ty| {
if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
let (sans_suffix, _) = repr_str.split_at(pos);
OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix }
} else {
OverflowingBinHexSub::Help { suggestion_ty }
} }
if let Some(sugg_ty) =
get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative)
{
lint.set_arg("suggestion_ty", sugg_ty);
if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
let (sans_suffix, _) = repr_str.split_at(pos);
lint.span_suggestion(
expr.span,
fluent::suggestion,
format!("{}{}", sans_suffix, sugg_ty),
Applicability::MachineApplicable,
);
} else {
lint.help(fluent::help);
}
}
lint.set_arg("ty", t)
.set_arg("lit", repr_str)
.set_arg("dec", val)
.set_arg("actually", actually);
lint
}, },
); );
cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
expr.span,
OverflowingBinHex { ty: t, lit: repr_str.clone(), dec: val, actually, sign, sub },
)
} }
// This function finds the next fitting type and generates a suggestion string. // This function finds the next fitting type and generates a suggestion string.
@ -363,28 +337,19 @@ fn lint_int_literal<'tcx>(
return; return;
} }
cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_int, |lint| { let lit = cx
lint.set_arg("ty", t.name_str()) .sess()
.set_arg( .source_map()
"lit", .span_to_snippet(lit.span)
cx.sess() .expect("must get snippet from literal");
.source_map() let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
.span_to_snippet(lit.span) .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
.expect("must get snippet from literal"),
)
.set_arg("min", min)
.set_arg("max", max)
.note(fluent::note);
if let Some(sugg_ty) = cx.emit_spanned_lint(
get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) OVERFLOWING_LITERALS,
{ e.span,
lint.set_arg("suggestion_ty", sugg_ty); OverflowingInt { ty: t.name_str(), lit, min, max, help },
lint.help(fluent::help); );
}
lint
});
} }
} }
@ -408,18 +373,10 @@ fn lint_uint_literal<'tcx>(
match par_e.kind { match par_e.kind {
hir::ExprKind::Cast(..) => { hir::ExprKind::Cast(..) => {
if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
cx.struct_span_lint( cx.emit_spanned_lint(
OVERFLOWING_LITERALS, OVERFLOWING_LITERALS,
par_e.span, par_e.span,
fluent::lint_only_cast_u8_to_char, OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
|lint| {
lint.span_suggestion(
par_e.span,
fluent::suggestion,
format!("'\\u{{{:X}}}'", lit_val),
Applicability::MachineApplicable,
)
},
); );
return; return;
} }
@ -443,19 +400,20 @@ fn lint_uint_literal<'tcx>(
); );
return; return;
} }
cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_uint, |lint| { cx.emit_spanned_lint(
lint.set_arg("ty", t.name_str()) OVERFLOWING_LITERALS,
.set_arg( e.span,
"lit", OverflowingUInt {
cx.sess() ty: t.name_str(),
.source_map() lit: cx
.span_to_snippet(lit.span) .sess()
.expect("must get snippet from literal"), .source_map()
) .span_to_snippet(lit.span)
.set_arg("min", min) .expect("must get snippet from literal"),
.set_arg("max", max) min,
.note(fluent::note) max,
}); },
);
} }
} }
@ -484,20 +442,16 @@ fn lint_literal<'tcx>(
_ => bug!(), _ => bug!(),
}; };
if is_infinite == Ok(true) { if is_infinite == Ok(true) {
cx.struct_span_lint( cx.emit_spanned_lint(
OVERFLOWING_LITERALS, OVERFLOWING_LITERALS,
e.span, e.span,
fluent::lint_overflowing_literal, OverflowingLiteral {
|lint| { ty: t.name_str(),
lint.set_arg("ty", t.name_str()) lit: cx
.set_arg( .sess()
"lit", .source_map()
cx.sess() .span_to_snippet(lit.span)
.source_map() .expect("must get snippet from literal"),
.span_to_snippet(lit.span)
.expect("must get snippet from literal"),
)
.note(fluent::note)
}, },
); );
} }
@ -517,12 +471,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
} }
hir::ExprKind::Binary(binop, ref l, ref r) => { hir::ExprKind::Binary(binop, ref l, ref r) => {
if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
cx.struct_span_lint( cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
UNUSED_COMPARISONS,
e.span,
fluent::lint_unused_comparisons,
|lint| lint,
);
} }
} }
hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
@ -1174,26 +1123,21 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
CItemKind::Declaration => IMPROPER_CTYPES, CItemKind::Declaration => IMPROPER_CTYPES,
CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS, CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
}; };
let desc = match self.mode {
self.cx.struct_span_lint(lint, sp, fluent::lint_improper_ctypes, |lint| { CItemKind::Declaration => "block",
let item_description = match self.mode { CItemKind::Definition => "fn",
CItemKind::Declaration => "block", };
CItemKind::Definition => "fn", let span_note = if let ty::Adt(def, _) = ty.kind()
&& let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
Some(sp)
} else {
None
}; };
lint.set_arg("ty", ty); self.cx.emit_spanned_lint(
lint.set_arg("desc", item_description); lint,
lint.span_label(sp, fluent::label); sp,
if let Some(help) = help { ImproperCTypes { ty, desc, label: sp, help, note, span_note },
lint.help(help); );
}
lint.note(note);
if let ty::Adt(def, _) = ty.kind() {
if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
lint.span_note(sp, fluent::note);
}
}
lint
});
} }
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
@ -1397,11 +1341,10 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
// We only warn if the largest variant is at least thrice as large as // We only warn if the largest variant is at least thrice as large as
// the second-largest. // the second-largest.
if largest > slargest * 3 && slargest > 0 { if largest > slargest * 3 && slargest > 0 {
cx.struct_span_lint( cx.emit_spanned_lint(
VARIANT_SIZE_DIFFERENCES, VARIANT_SIZE_DIFFERENCES,
enum_definition.variants[largest_index].span, enum_definition.variants[largest_index].span,
fluent::lint_variant_size_differences, VariantSizeDifferencesDiag { largest },
|lint| lint.set_arg("largest", largest),
); );
} }
} }
@ -1509,17 +1452,19 @@ impl InvalidAtomicOrdering {
fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store]) if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
&& let Some((ordering_arg, invalid_ordering, msg)) = match method { && let Some((ordering_arg, invalid_ordering)) = match method {
sym::load => Some((&args[0], sym::Release, fluent::lint_atomic_ordering_load)), sym::load => Some((&args[0], sym::Release)),
sym::store => Some((&args[1], sym::Acquire, fluent::lint_atomic_ordering_store)), sym::store => Some((&args[1], sym::Acquire)),
_ => None, _ => None,
} }
&& let Some(ordering) = Self::match_ordering(cx, ordering_arg) && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
&& (ordering == invalid_ordering || ordering == sym::AcqRel) && (ordering == invalid_ordering || ordering == sym::AcqRel)
{ {
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, msg, |lint| { if method == sym::load {
lint.help(fluent::help) cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
}); } else {
cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
};
} }
} }
@ -1530,10 +1475,7 @@ impl InvalidAtomicOrdering {
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence)) && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
&& Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed) && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
{ {
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, fluent::lint_atomic_ordering_fence, |lint| { cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
lint
.help(fluent::help)
});
} }
} }
@ -1550,15 +1492,6 @@ impl InvalidAtomicOrdering {
let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return }; let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
if matches!(fail_ordering, sym::Release | sym::AcqRel) { if matches!(fail_ordering, sym::Release | sym::AcqRel) {
#[derive(LintDiagnostic)]
#[diag(lint_atomic_ordering_invalid)]
#[help]
struct InvalidAtomicOrderingDiag {
method: Symbol,
#[label]
fail_order_arg_span: Span,
}
cx.emit_spanned_lint( cx.emit_spanned_lint(
INVALID_ATOMIC_ORDERING, INVALID_ATOMIC_ORDERING,
fail_order_arg.span, fail_order_arg.span,

View File

@ -1,9 +1,14 @@
use crate::lints::{
PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag,
UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDelim, UnusedDelimSuggestion,
UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedResult,
};
use crate::Lint; use crate::Lint;
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::util::{classify, parser}; use rustc_ast::util::{classify, parser};
use rustc_ast::{ExprKind, StmtKind}; use rustc_ast::{ExprKind, StmtKind};
use rustc_errors::{fluent, pluralize, Applicability, MultiSpan}; use rustc_errors::{pluralize, MultiSpan};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -163,23 +168,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
let mut op_warned = false; let mut op_warned = false;
if let Some(must_use_op) = must_use_op { if let Some(must_use_op) = must_use_op {
cx.struct_span_lint(UNUSED_MUST_USE, expr.span, fluent::lint_unused_op, |lint| { cx.emit_spanned_lint(
lint.set_arg("op", must_use_op) UNUSED_MUST_USE,
.span_label(expr.span, fluent::label) expr.span,
.span_suggestion_verbose( UnusedOp {
expr.span.shrink_to_lo(), op: must_use_op,
fluent::suggestion, label: expr.span,
"let _ = ", suggestion: expr.span.shrink_to_lo(),
Applicability::MachineApplicable, },
) );
});
op_warned = true; op_warned = true;
} }
if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) { if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) {
cx.struct_span_lint(UNUSED_RESULTS, s.span, fluent::lint_unused_result, |lint| { cx.emit_spanned_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
lint.set_arg("ty", ty)
});
} }
fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
@ -402,47 +404,31 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
); );
} }
MustUsePath::Closure(span) => { MustUsePath::Closure(span) => {
cx.struct_span_lint( cx.emit_spanned_lint(
UNUSED_MUST_USE, UNUSED_MUST_USE,
*span, *span,
fluent::lint_unused_closure, UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
|lint| {
// FIXME(davidtwco): this isn't properly translatable because of the
// pre/post strings
lint.set_arg("count", plural_len)
.set_arg("pre", descr_pre)
.set_arg("post", descr_post)
.note(fluent::note)
},
); );
} }
MustUsePath::Generator(span) => { MustUsePath::Generator(span) => {
cx.struct_span_lint( cx.emit_spanned_lint(
UNUSED_MUST_USE, UNUSED_MUST_USE,
*span, *span,
fluent::lint_unused_generator, UnusedGenerator { count: plural_len, pre: descr_pre, post: descr_post },
|lint| {
// FIXME(davidtwco): this isn't properly translatable because of the
// pre/post strings
lint.set_arg("count", plural_len)
.set_arg("pre", descr_pre)
.set_arg("post", descr_post)
.note(fluent::note)
},
); );
} }
MustUsePath::Def(span, def_id, reason) => { MustUsePath::Def(span, def_id, reason) => {
cx.struct_span_lint(UNUSED_MUST_USE, *span, fluent::lint_unused_def, |lint| { cx.emit_spanned_lint(
// FIXME(davidtwco): this isn't properly translatable because of the pre/post UNUSED_MUST_USE,
// strings *span,
lint.set_arg("pre", descr_pre); UnusedDef {
lint.set_arg("post", descr_post); pre: descr_pre,
lint.set_arg("def", cx.tcx.def_path_str(*def_id)); post: descr_post,
if let Some(note) = reason { cx,
lint.note(note.as_str()); def_id: *def_id,
} note: *reason,
lint },
}); );
} }
} }
} }
@ -478,31 +464,15 @@ impl<'tcx> LateLintPass<'tcx> for PathStatements {
if let hir::ExprKind::Path(_) = expr.kind { if let hir::ExprKind::Path(_) = expr.kind {
let ty = cx.typeck_results().expr_ty(expr); let ty = cx.typeck_results().expr_ty(expr);
if ty.needs_drop(cx.tcx, cx.param_env) { if ty.needs_drop(cx.tcx, cx.param_env) {
cx.struct_span_lint( let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
PATH_STATEMENTS, {
s.span, PathStatementDropSub::Suggestion { span: s.span, snippet }
fluent::lint_path_statement_drop, } else {
|lint| { PathStatementDropSub::Help { span: s.span }
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) { };
lint.span_suggestion( cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
s.span,
fluent::suggestion,
format!("drop({});", snippet),
Applicability::MachineApplicable,
);
} else {
lint.span_help(s.span, fluent::suggestion);
}
lint
},
);
} else { } else {
cx.struct_span_lint( cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
PATH_STATEMENTS,
s.span,
fluent::lint_path_statement_no_effect,
|lint| lint,
);
} }
} }
} }
@ -695,36 +665,35 @@ trait UnusedDelimLint {
} else { } else {
MultiSpan::from(value_span) MultiSpan::from(value_span)
}; };
cx.struct_span_lint(self.lint(), primary_span, fluent::lint_unused_delim, |lint| { let suggestion = spans.map(|(lo, hi)| {
lint.set_arg("delim", Self::DELIM_STR); let sm = cx.sess().source_map();
lint.set_arg("item", msg); let lo_replace =
if let Some((lo, hi)) = spans {
let sm = cx.sess().source_map();
let lo_replace =
if keep_space.0 && if keep_space.0 &&
let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') { let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') {
" ".to_string() " "
} else { } else {
"".to_string() ""
}; };
let hi_replace = let hi_replace =
if keep_space.1 && if keep_space.1 &&
let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(' ') { let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(' ') {
" ".to_string() " "
} else { } else {
"".to_string() ""
}; };
UnusedDelimSuggestion {
let replacement = vec![(lo, lo_replace), (hi, hi_replace)]; start_span: lo,
lint.multipart_suggestion( start_replace: lo_replace,
fluent::suggestion, end_span: hi,
replacement, end_replace: hi_replace,
Applicability::MachineApplicable,
);
} }
lint
}); });
cx.emit_spanned_lint(
self.lint(),
primary_span,
UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
);
} }
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
@ -1297,11 +1266,10 @@ impl UnusedImportBraces {
ast::UseTreeKind::Nested(_) => return, ast::UseTreeKind::Nested(_) => return,
}; };
cx.struct_span_lint( cx.emit_spanned_lint(
UNUSED_IMPORT_BRACES, UNUSED_IMPORT_BRACES,
item.span, item.span,
fluent::lint_unused_import_braces, UnusedImportBracesDiag { node: node_name },
|lint| lint.set_arg("node", node_name),
); );
} }
} }
@ -1351,17 +1319,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
for adj in cx.typeck_results().expr_adjustments(e) { for adj in cx.typeck_results().expr_adjustments(e) {
if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind { if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
cx.struct_span_lint( match m {
UNUSED_ALLOCATION, adjustment::AutoBorrowMutability::Not => {
e.span, cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
match m { }
adjustment::AutoBorrowMutability::Not => fluent::lint_unused_allocation, adjustment::AutoBorrowMutability::Mut { .. } => {
adjustment::AutoBorrowMutability::Mut { .. } => { cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
fluent::lint_unused_allocation_mut }
} };
},
|lint| lint,
);
} }
} }
} }