Rollup merge of #108760 - clubby789:autolintstuff, r=wesleywiser

Add lint to deny diagnostics composed of static strings

r? ghost

I'm hoping to have a lint that semi-automatically converts simple diagnostics such as `struct_span_err(span, "msg").help("msg").span_note(span2, "msg").emit()` to typed session diagnostics. It's quite hacky and not entirely working because of problems with `x fix` but should hopefully help reduce some of the work.
I'm going to start trying to apply what I can from this, but opening this as a draft in case anyone wants to develop on it.

cc #100717
This commit is contained in:
Matthias Krüger 2023-04-26 18:51:40 +02:00 committed by GitHub
commit 309496cf29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 578 additions and 201 deletions

View File

@ -149,6 +149,25 @@ builtin_macros_format_pos_mismatch = {$n} positional {$n ->
[one] argument
*[more] arguments
} in format string, but {$desc}
builtin_macros_offset_of_expected_field = expected field
builtin_macros_offset_of_expected_two_args = expected 2 arguments
builtin_macros_test_case_non_item = `#[test_case]` attribute is only allowed on items
builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests
.label = `{$kind}` because of this
builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names
builtin_macros_asm_mutually_exclusive = the `{$opt1}` and `{$opt2}` options are mutually exclusive
builtin_macros_asm_pure_combine = the `pure` option must be combined with either `nomem` or `readonly`
builtin_macros_asm_pure_no_output = asm with the `pure` option must have at least one output
builtin_macros_asm_modifier_invalid = asm template modifier must be a single character
builtin_macros_test_runner_invalid = `test_runner` argument must be a path
builtin_macros_test_runner_nargs = `#![test_runner(..)]` accepts exactly 1 argument

View File

@ -15,6 +15,8 @@ use rustc_span::{InnerSpan, Span};
use rustc_target::asm::InlineAsmArch;
use smallvec::smallvec;
use crate::errors;
pub struct AsmArgs {
pub templates: Vec<P<ast::Expr>>,
pub operands: Vec<(ast::InlineAsmOperand, Span)>,
@ -205,7 +207,7 @@ pub fn parse_asm_args<'a>(
// of the argument available.
if explicit_reg {
if name.is_some() {
diag.struct_span_err(span, "explicit register arguments cannot have names").emit();
diag.emit_err(errors::AsmExplicitRegisterName { span });
}
args.reg_args.insert(slot);
} else if let Some(name) = name {
@ -240,25 +242,19 @@ pub fn parse_asm_args<'a>(
&& args.options.contains(ast::InlineAsmOptions::READONLY)
{
let spans = args.options_spans.clone();
diag.struct_span_err(spans, "the `nomem` and `readonly` options are mutually exclusive")
.emit();
diag.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
}
if args.options.contains(ast::InlineAsmOptions::PURE)
&& args.options.contains(ast::InlineAsmOptions::NORETURN)
{
let spans = args.options_spans.clone();
diag.struct_span_err(spans, "the `pure` and `noreturn` options are mutually exclusive")
.emit();
diag.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
}
if args.options.contains(ast::InlineAsmOptions::PURE)
&& !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
{
let spans = args.options_spans.clone();
diag.struct_span_err(
spans,
"the `pure` option must be combined with either `nomem` or `readonly`",
)
.emit();
diag.emit_err(errors::AsmPureCombine { spans });
}
let mut have_real_output = false;
@ -285,11 +281,7 @@ pub fn parse_asm_args<'a>(
}
}
if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
diag.struct_span_err(
args.options_spans.clone(),
"asm with the `pure` option must have at least one output",
)
.emit();
diag.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
}
if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() {
let err = diag
@ -705,11 +697,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
.ty_span
.map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end)))
.unwrap_or(template_sp);
ecx.struct_span_err(
span,
"asm template modifier must be a single character",
)
.emit();
ecx.emit_err(errors::AsmModifierInvalid { span });
modifier = None;
}

View File

@ -551,3 +551,71 @@ pub(crate) struct FormatPositionalMismatch {
#[subdiagnostic]
pub(crate) highlight: SingleLabelManySpans,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_test_case_non_item)]
pub(crate) struct TestCaseNonItem {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_test_bad_fn)]
pub(crate) struct TestBadFn {
#[primary_span]
pub(crate) span: Span,
#[label]
pub(crate) cause: Span,
pub(crate) kind: &'static str,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_explicit_register_name)]
pub(crate) struct AsmExplicitRegisterName {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_mutually_exclusive)]
pub(crate) struct AsmMutuallyExclusive {
#[primary_span]
pub(crate) spans: Vec<Span>,
pub(crate) opt1: &'static str,
pub(crate) opt2: &'static str,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_pure_combine)]
pub(crate) struct AsmPureCombine {
#[primary_span]
pub(crate) spans: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_pure_no_output)]
pub(crate) struct AsmPureNoOutput {
#[primary_span]
pub(crate) spans: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_asm_modifier_invalid)]
pub(crate) struct AsmModifierInvalid {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_test_runner_invalid)]
pub(crate) struct TestRunnerInvalid {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_test_runner_nargs)]
pub(crate) struct TestRunnerNargs {
#[primary_span]
pub(crate) span: Span,
}

View File

@ -1,3 +1,4 @@
use crate::errors;
/// The expansion from a test function to the appropriate test struct for libtest
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
@ -40,12 +41,7 @@ pub fn expand_test_case(
unreachable!()
},
_ => {
ecx.struct_span_err(
anno_item.span(),
"`#[test_case]` attribute is only allowed on items",
)
.emit();
ecx.emit_err(errors::TestCaseNonItem { span: anno_item.span() });
return vec![];
}
};
@ -533,15 +529,11 @@ fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
match &i.kind {
ast::ItemKind::Fn(box ast::Fn { sig, generics, .. }) => {
if let ast::Unsafe::Yes(span) = sig.header.unsafety {
sd.struct_span_err(i.span, "unsafe functions cannot be used for tests")
.span_label(span, "`unsafe` because of this")
.emit();
sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" });
return false;
}
if let ast::Async::Yes { span, .. } = sig.header.asyncness {
sd.struct_span_err(i.span, "async functions cannot be used for tests")
.span_label(span, "`async` because of this")
.emit();
sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" });
return false;
}

View File

@ -19,6 +19,8 @@ use tracing::debug;
use std::{iter, mem};
use crate::errors;
#[derive(Clone)]
struct Test {
span: Span,
@ -385,11 +387,11 @@ fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast
[single] => match single.meta_item() {
Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()),
_ => {
sd.struct_span_err(span, "`test_runner` argument must be a path").emit();
sd.emit_err(errors::TestRunnerInvalid { span });
}
},
_ => {
sd.struct_span_err(span, "`#![test_runner(..)]` accepts exactly 1 argument").emit();
sd.emit_err(errors::TestRunnerNargs { span });
}
}
None

View File

@ -291,3 +291,16 @@ codegen_ssa_invalid_monomorphization_unsupported_cast = invalid monomorphization
codegen_ssa_invalid_monomorphization_unsupported_operation = invalid monomorphization of `{$name}` intrinsic: unsupported operation on `{$in_ty}` with element `{$in_elem}`
codegen_ssa_invalid_monomorphization_expected_vector_element_type = invalid monomorphization of `{$name}` intrinsic: expected element type `{$expected_element}` of vector type `{$vector_type}` to be a signed or unsigned integer type
codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize`
.note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`
codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]`
.note = the attribute requires exactly one argument
codegen_ssa_illegal_link_ordinal_format = illegal ordinal format in `link_ordinal`
.note = an unsuffixed integer value, e.g., `1`, is expected
codegen_ssa_target_feature_safe_trait = `#[target_feature(..)]` cannot be applied to safe trait method
.label = cannot be applied to safe trait method
.label_def = not an `unsafe` function

View File

@ -14,6 +14,7 @@ use rustc_span::symbol::Ident;
use rustc_span::{sym, Span};
use rustc_target::spec::{abi, SanitizerSet};
use crate::errors;
use crate::target_features::from_target_feature;
use crate::{errors::ExpectedUsedSymbol, target_features::check_target_feature_trait_unsafe};
@ -334,10 +335,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS
}
_ => {
tcx.sess
.struct_span_err(item.span(), "invalid argument for `no_sanitize`")
.note("expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
.emit();
tcx.sess.emit_err(errors::InvalidNoSanitize { span: item.span() });
}
}
}
@ -608,10 +606,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
let sole_meta_list = match meta_item_list {
Some([item]) => item.lit(),
Some(_) => {
tcx.sess
.struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`")
.note("the attribute requires exactly one argument")
.emit();
tcx.sess.emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span });
return None;
}
_ => None,
@ -642,10 +637,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
None
}
} else {
tcx.sess
.struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`")
.note("an unsuffixed integer value, e.g., `1`, is expected")
.emit();
tcx.sess.emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span });
None
}
}

View File

@ -981,3 +981,37 @@ impl IntoDiagnosticArg for ExpectedPointerMutability {
}
}
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_invalid_no_sanitize)]
#[note]
pub struct InvalidNoSanitize {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_invalid_link_ordinal_nargs)]
#[note]
pub struct InvalidLinkOrdinalNargs {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_illegal_link_ordinal_format)]
#[note]
pub struct InvalidLinkOrdinalFormat {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_target_feature_safe_trait)]
pub struct TargetFeatureSafeTrait {
#[primary_span]
#[label]
pub span: Span,
#[label(codegen_ssa_label_def)]
pub def: Span,
}

View File

@ -1,3 +1,4 @@
use crate::errors;
use rustc_ast::ast;
use rustc_attr::InstructionSetAttr;
use rustc_data_structures::fx::FxHashMap;
@ -443,14 +444,10 @@ pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_s
if let DefKind::AssocFn = tcx.def_kind(id) {
let parent_id = tcx.local_parent(id);
if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) {
tcx.sess
.struct_span_err(
attr_span,
"`#[target_feature(..)]` cannot be applied to safe trait method",
)
.span_label(attr_span, "cannot be applied to safe trait method")
.span_label(tcx.def_span(id), "not an `unsafe` function")
.emit();
tcx.sess.emit_err(errors::TargetFeatureSafeTrait {
span: attr_span,
def: tcx.def_span(id),
});
}
}
}

View File

@ -136,3 +136,7 @@ expand_proc_macro_panicked =
expand_proc_macro_derive_tokens =
proc-macro derive produced unparsable tokens
expand_duplicate_matcher_binding = duplicate matcher binding
.label = duplicate binding
.label2 = previous binding

View File

@ -397,3 +397,13 @@ pub struct ProcMacroDeriveTokens {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(expand_duplicate_matcher_binding)]
pub struct DuplicateMatcherBinding {
#[primary_span]
#[label]
pub span: Span,
#[label(expand_label2)]
pub prev: Span,
}

View File

@ -104,6 +104,7 @@
//! Kleene operators under which a meta-variable is repeating is the concatenation of the stacks
//! stored when entering a macro definition starting from the state in which the meta-variable is
//! bound.
use crate::errors;
use crate::mbe::{KleeneToken, TokenTree};
use rustc_ast::token::{Delimiter, Token, TokenKind};
@ -281,10 +282,7 @@ fn check_binders(
// Duplicate binders at the top-level macro definition are errors. The lint is only
// for nested macro definitions.
sess.span_diagnostic
.struct_span_err(span, "duplicate matcher binding")
.span_label(span, "duplicate binding")
.span_label(prev_info.span, "previous binding")
.emit();
.emit_err(errors::DuplicateMatcherBinding { span, prev: prev_info.span });
*valid = false;
} else {
binders.insert(name, BinderInfo { span, ops: ops.into() });

View File

@ -262,3 +262,17 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de
hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one non-zero-sized field, but has {$field_count}
.label = needs at most one non-zero-sized field, but has {$field_count}
.labels = this field is non-zero-sized
hir_analysis_too_large_static = extern static is too large for the current architecture
hir_analysis_specialization_trait = implementing `rustc_specialization_trait` traits is unstable
.help = add `#![feature(min_specialization)]` to the crate attributes to enable
hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present
.label = `for<...>` is here
hir_analysis_const_specialize = cannot specialize on const impl with non-const impl
hir_analysis_static_specialize = cannot specialize on `'static` lifetime
hir_analysis_missing_tilde_const = missing `~const` qualifier for specialization

View File

@ -170,9 +170,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
if matches!(tcx.def_kind(def_id), DefKind::Static(_)
if tcx.def_kind(tcx.local_parent(def_id)) == DefKind::ForeignMod) =>
{
tcx.sess
.struct_span_err(span, "extern static is too large for the current architecture")
.emit();
tcx.sess.emit_err(errors::TooLargeStatic { span });
return;
}
// Generic statics are rejected, but we still reach this case.

View File

@ -5,6 +5,7 @@
// done by the orphan and overlap modules. Then we build up various
// mappings. That mapping code resides here.
use crate::errors;
use rustc_errors::{error_code, struct_span_err};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::query::Providers;
@ -67,13 +68,7 @@ fn enforce_trait_manually_implementable(
tcx.trait_def(trait_def_id).specialization_kind
{
if !tcx.features().specialization && !tcx.features().min_specialization {
tcx.sess
.struct_span_err(
impl_header_span,
"implementing `rustc_specialization_trait` traits is unstable",
)
.help("add `#![feature(min_specialization)]` to the crate attributes to enable")
.emit();
tcx.sess.emit_err(errors::SpecializationTrait { span: impl_header_span });
return;
}
}

View File

@ -455,13 +455,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
.collect::<Vec<_>>();
if !infer_spans.is_empty() {
self.tcx.sess
.struct_span_err(
infer_spans,
"implicit types in closure signatures are forbidden when `for<...>` is present",
)
.span_label(for_sp, "`for<...>` is here")
.emit();
self.tcx
.sess
.emit_err(errors::ClosureImplicitHrtb { spans: infer_spans, for_sp });
}
}

View File

@ -633,6 +633,7 @@ pub(crate) struct SIMDFFIHighlyExperimental {
}
#[derive(Diagnostic)]
pub enum ImplNotMarkedDefault {
#[diag(hir_analysis_impl_not_marked_default, code = "E0520")]
#[note]
@ -769,3 +770,48 @@ pub(crate) struct TransparentNonZeroSized<'a> {
pub field_count: usize,
pub desc: &'a str,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_too_large_static)]
pub(crate) struct TooLargeStatic {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_specialization_trait)]
#[help]
pub(crate) struct SpecializationTrait {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_closure_implicit_hrtb)]
pub(crate) struct ClosureImplicitHrtb {
#[primary_span]
pub spans: Vec<Span>,
#[label]
pub for_sp: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_const_specialize)]
pub(crate) struct ConstSpecialize {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_static_specialize)]
pub(crate) struct StaticSpecialize {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_missing_tilde_const)]
pub(crate) struct MissingTildeConst {
#[primary_span]
pub span: Span,
}

View File

@ -65,8 +65,8 @@
//! cause use after frees with purely safe code in the same way as specializing
//! on traits with methods can.
use crate::constrained_generic_params as cgp;
use crate::errors::SubstsOnOverriddenImpl;
use crate::{constrained_generic_params as cgp, errors};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
@ -137,9 +137,7 @@ fn check_constness(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node,
if let hir::Constness::Const = impl2_constness {
if let hir::Constness::NotConst = impl1_constness {
tcx.sess
.struct_span_err(span, "cannot specialize on const impl with non-const impl")
.emit();
tcx.sess.emit_err(errors::ConstSpecialize { span });
}
}
}
@ -293,7 +291,7 @@ fn check_static_lifetimes<'tcx>(
span: Span,
) {
if tcx.any_free_region_meets(parent_substs, |r| r.is_static()) {
tcx.sess.struct_span_err(span, "cannot specialize on `'static` lifetime").emit();
tcx.sess.emit_err(errors::StaticSpecialize { span });
}
}
@ -438,7 +436,7 @@ fn trait_predicates_eq<'tcx>(
// the one on the base.
match (trait_pred2.constness, trait_pred1.constness) {
(ty::BoundConstness::ConstIfConst, ty::BoundConstness::NotConst) => {
tcx.sess.struct_span_err(span, "missing `~const` qualifier for specialization").emit();
tcx.sess.emit_err(errors::MissingTildeConst { span });
}
_ => {}
}

View File

@ -62,3 +62,16 @@ hir_typeck_fru_suggestion =
[NONE]{""}
*[other] {" "}from `{$expr}`
}, separate the last named field with a comma
hir_typeck_const_select_must_be_const = this argument must be a `const fn`
.help = consult the documentation on `const_eval_select` for more information
hir_typeck_const_select_must_be_fn = this argument must be a function item
.note = expected a function item, found {$ty}
.help = consult the documentation on `const_eval_select` for more information
hir_typeck_union_pat_multiple_fields = union patterns should have exactly one field
hir_typeck_union_pat_dotdot = `..` cannot be used in union patterns
hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but rustc had trouble determining where
.note = we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new

View File

@ -228,3 +228,42 @@ impl HelpUseLatestEdition {
}
}
}
#[derive(Diagnostic)]
#[diag(hir_typeck_const_select_must_be_const)]
#[help]
pub struct ConstSelectMustBeConst {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_typeck_const_select_must_be_fn)]
#[note]
#[help]
pub struct ConstSelectMustBeFn<'a> {
#[primary_span]
pub span: Span,
pub ty: Ty<'a>,
}
#[derive(Diagnostic)]
#[diag(hir_typeck_union_pat_multiple_fields)]
pub struct UnionPatMultipleFields {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_typeck_union_pat_dotdot)]
pub struct UnionPatDotDot {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_typeck_arg_mismatch_indeterminate)]
pub struct ArgMismatchIndeterminate {
#[primary_span]
pub span: Span,
}

View File

@ -2,8 +2,8 @@ use crate::coercion::CoerceMany;
use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx};
use crate::gather_locals::Declaration;
use crate::method::MethodCallee;
use crate::Expectation::*;
use crate::TupleArgumentsFlag::*;
use crate::{errors, Expectation::*};
use crate::{
struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, RawTy,
TupleArgumentsFlag,
@ -283,19 +283,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) {
self.tcx
.sess
.struct_span_err(provided_arg.span, "this argument must be a `const fn`")
.help("consult the documentation on `const_eval_select` for more information")
.emit();
.emit_err(errors::ConstSelectMustBeConst { span: provided_arg.span });
}
} else {
self.tcx
.sess
.struct_span_err(provided_arg.span, "this argument must be a function item")
.note(format!("expected a function item, found {checked_ty}"))
.help(
"consult the documentation on `const_eval_select` for more information",
)
.emit();
self.tcx.sess.emit_err(errors::ConstSelectMustBeFn {
span: provided_arg.span,
ty: checked_ty,
});
}
}
@ -744,17 +738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if cfg!(debug_assertions) {
span_bug!(error_span, "expected errors from argument matrix");
} else {
tcx.sess
.struct_span_err(
error_span,
"argument type mismatch was detected, \
but rustc had trouble determining where",
)
.note(
"we would appreciate a bug report: \
https://github.com/rust-lang/rust/issues/new",
)
.emit();
tcx.sess.emit_err(errors::ArgMismatchIndeterminate { span: error_span });
}
return;
}

View File

@ -1,4 +1,4 @@
use crate::{FnCtxt, RawTy};
use crate::{errors, FnCtxt, RawTy};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{
@ -1410,12 +1410,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Report an error if an incorrect number of fields was specified.
if adt.is_union() {
if fields.len() != 1 {
tcx.sess
.struct_span_err(pat.span, "union patterns should have exactly one field")
.emit();
tcx.sess.emit_err(errors::UnionPatMultipleFields { span: pat.span });
}
if has_rest_pat {
tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
tcx.sess.emit_err(errors::UnionPatDotDot { span: pat.span });
}
} else if !unmentioned_fields.is_empty() {
let accessible_unmentioned_fields: Vec<_> = unmentioned_fields

View File

@ -99,6 +99,8 @@ lint_diag_out_of_impl =
lint_untranslatable_diag = diagnostics should be created using translatable messages
lint_trivial_untranslatable_diag = diagnostic with static strings only
lint_bad_opt_access = {$msg}
lint_cstring_ptr = getting the inner pointer of a temporary `CString`

View File

@ -4,6 +4,7 @@
use crate::lints::{
BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword,
QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag,
UntranslatableDiagnosticTrivial,
};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
@ -366,7 +367,15 @@ declare_tool_lint! {
report_in_external_macro: true
}
declare_lint_pass!(Diagnostics => [ UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL ]);
declare_tool_lint! {
/// The `untranslatable_diagnostic_trivial` lint detects diagnostics created using only static strings.
pub rustc::UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL,
Deny,
"prevent creation of diagnostics which cannot be translated, which use only static strings",
report_in_external_macro: true
}
declare_lint_pass!(Diagnostics => [ UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL, UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL ]);
impl LateLintPass<'_> for Diagnostics {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
@ -423,6 +432,75 @@ impl LateLintPass<'_> for Diagnostics {
}
}
impl EarlyLintPass for Diagnostics {
#[allow(unused_must_use)]
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
// Looking for a straight chain of method calls from 'struct_span_err' to 'emit'.
let ast::StmtKind::Semi(expr) = &stmt.kind else {
return;
};
let ast::ExprKind::MethodCall(meth) = &expr.kind else {
return;
};
if meth.seg.ident.name != sym::emit || !meth.args.is_empty() {
return;
}
let mut segments = vec![];
let mut cur = &meth.receiver;
let fake = &[].into();
loop {
match &cur.kind {
ast::ExprKind::Call(func, args) => {
if let ast::ExprKind::Path(_, path) = &func.kind {
segments.push((path.segments.last().unwrap().ident.name, args))
}
break;
}
ast::ExprKind::MethodCall(method) => {
segments.push((method.seg.ident.name, &method.args));
cur = &method.receiver;
}
ast::ExprKind::MacCall(mac) => {
segments.push((mac.path.segments.last().unwrap().ident.name, fake));
break;
}
_ => {
break;
}
}
}
segments.reverse();
if segments.is_empty() {
return;
}
if segments[0].0.as_str() != "struct_span_err" {
return;
}
if !segments.iter().all(|(name, args)| {
let arg = match name.as_str() {
"struct_span_err" | "span_note" | "span_label" | "span_help" => &args[1],
"note" | "help" => &args[0],
_ => {
return false;
}
};
if let ast::ExprKind::Lit(lit) = arg.kind
&& let ast::token::LitKind::Str = lit.kind {
true
} else {
false
}
}) {
return;
}
cx.emit_spanned_lint(
UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL,
stmt.span,
UntranslatableDiagnosticTrivial,
);
}
}
declare_tool_lint! {
/// The `bad_opt_access` lint detects accessing options by field instead of
/// the wrapper function.

View File

@ -518,6 +518,7 @@ fn register_internals(store: &mut LintStore) {
store.register_lints(&TyTyKind::get_lints());
store.register_late_pass(|_| Box::new(TyTyKind));
store.register_lints(&Diagnostics::get_lints());
store.register_early_pass(|| Box::new(Diagnostics));
store.register_late_pass(|_| Box::new(Diagnostics));
store.register_lints(&BadOptAccess::get_lints());
store.register_late_pass(|_| Box::new(BadOptAccess));

View File

@ -820,6 +820,10 @@ pub struct DiagOutOfImpl;
#[diag(lint_untranslatable_diag)]
pub struct UntranslatableDiag;
#[derive(LintDiagnostic)]
#[diag(lint_trivial_untranslatable_diag)]
pub struct UntranslatableDiagnosticTrivial;
#[derive(LintDiagnostic)]
#[diag(lint_bad_opt_access)]
pub struct BadOptAccessDiag<'a> {

View File

@ -742,3 +742,33 @@ parse_bad_return_type_notation_output =
parse_bad_return_type_notation_dotdot =
return type notation uses `()` instead of `(..)` for elided arguments
.suggestion = remove the `..`
parse_bad_assoc_type_bounds = bounds on associated types do not belong here
.label = belongs in `where` clause
parse_attr_after_generic = trailing attribute after generic parameter
.label = attributes must go before parameters
parse_attr_without_generics = attribute without generic parameters
.label = attributes are only permitted when preceding parameters
parse_where_generics = generic parameters on `where` clauses are reserved for future use
.label = currently unsupported
parse_generics_in_path = unexpected generic arguments in path
parse_assoc_lifetime = associated lifetimes are not supported
.label = the lifetime is given here
.help = if you meant to specify a trait object, write `dyn Trait + 'lifetime`
parse_tilde_const_lifetime = `~const` may only modify trait bounds, not lifetime bounds
parse_maybe_lifetime = `?` may only modify trait bounds, not lifetime bounds
parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported
.suggestion = remove the parentheses
parse_const_bounds_missing_tilde = const bounds must start with `~`
.suggestion = add `~`
parse_underscore_literal_suffix = underscore literal suffix is not allowed

View File

@ -2332,3 +2332,92 @@ pub(crate) struct BadReturnTypeNotationDotDot {
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_bad_assoc_type_bounds)]
pub(crate) struct BadAssocTypeBounds {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_attr_after_generic)]
pub(crate) struct AttrAfterGeneric {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_attr_without_generics)]
pub(crate) struct AttrWithoutGenerics {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_where_generics)]
pub(crate) struct WhereOnGenerics {
#[primary_span]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_generics_in_path)]
pub(crate) struct GenericsInPath {
#[primary_span]
pub span: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(parse_assoc_lifetime)]
#[help]
pub(crate) struct AssocLifetime {
#[primary_span]
pub span: Span,
#[label]
pub lifetime: Span,
}
#[derive(Diagnostic)]
#[diag(parse_tilde_const_lifetime)]
pub(crate) struct TildeConstLifetime {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_maybe_lifetime)]
pub(crate) struct MaybeLifetime {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_parenthesized_lifetime)]
pub(crate) struct ParenthesizedLifetime {
#[primary_span]
pub span: Span,
#[suggestion(style = "short", applicability = "machine-applicable", code = "{snippet}")]
pub sugg: Option<Span>,
pub snippet: String,
}
#[derive(Diagnostic)]
#[diag(parse_const_bounds_missing_tilde)]
pub(crate) struct ConstMissingTilde {
#[primary_span]
pub span: Span,
#[suggestion(code = "~", applicability = "machine-applicable")]
pub start: Span,
}
#[derive(Diagnostic)]
#[diag(parse_underscore_literal_suffix)]
pub(crate) struct UnderscoreLiteralSuffix {
#[primary_span]
pub span: Span,
}

View File

@ -209,11 +209,7 @@ impl<'a> StringReader<'a> {
if string == "_" {
self.sess
.span_diagnostic
.struct_span_err(
self.mk_sp(suffix_start, self.pos),
"underscore literal suffix is not allowed",
)
.emit();
.emit_err(errors::UnderscoreLiteralSuffix { span: self.mk_sp(suffix_start, self.pos) });
None
} else {
Some(Symbol::intern(string))

View File

@ -1,5 +1,5 @@
use crate::errors::{
MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters,
self, MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters,
UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody,
WhereClauseBeforeTupleStructBodySugg,
};
@ -181,12 +181,9 @@ impl<'a> Parser<'a> {
let snapshot = this.create_snapshot_for_diagnostic();
match this.parse_ty_where_predicate() {
Ok(where_predicate) => {
this.struct_span_err(
where_predicate.span(),
"bounds on associated types do not belong here",
)
.span_label(where_predicate.span(), "belongs in `where` clause")
.emit();
this.sess.emit_err(errors::BadAssocTypeBounds {
span: where_predicate.span(),
});
// FIXME - try to continue parsing other generics?
return Ok((None, TrailingToken::None));
}
@ -201,22 +198,11 @@ impl<'a> Parser<'a> {
// Check for trailing attributes and stop parsing.
if !attrs.is_empty() {
if !params.is_empty() {
this.struct_span_err(
attrs[0].span,
"trailing attribute after generic parameter",
)
.span_label(attrs[0].span, "attributes must go before parameters")
.emit();
this.sess
.emit_err(errors::AttrAfterGeneric { span: attrs[0].span });
} else {
this.struct_span_err(
attrs[0].span,
"attribute without generic parameters",
)
.span_label(
attrs[0].span,
"attributes are only permitted when preceding parameters",
)
.emit();
this.sess
.emit_err(errors::AttrWithoutGenerics { span: attrs[0].span });
}
}
return Ok((None, TrailingToken::None));
@ -304,12 +290,7 @@ impl<'a> Parser<'a> {
// change we parse those generics now, but report an error.
if self.choose_generics_over_qpath(0) {
let generics = self.parse_generics()?;
self.struct_span_err(
generics.span,
"generic parameters on `where` clauses are reserved for future use",
)
.span_label(generics.span, "currently unsupported")
.emit();
self.sess.emit_err(errors::WhereOnGenerics { span: generics.span });
}
loop {

View File

@ -150,16 +150,13 @@ impl<'a> Parser<'a> {
//
if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some())
{
parser
.struct_span_err(
path.segments
.iter()
.filter_map(|segment| segment.args.as_ref())
.map(|arg| arg.span())
.collect::<Vec<_>>(),
"unexpected generic arguments in path",
)
.emit();
let span = path
.segments
.iter()
.filter_map(|segment| segment.args.as_ref())
.map(|arg| arg.span())
.collect::<Vec<_>>();
parser.sess.emit_err(errors::GenericsInPath { span });
}
};
@ -620,10 +617,7 @@ impl<'a> Parser<'a> {
c.into()
}
Some(GenericArg::Lifetime(lt)) => {
self.struct_span_err(span, "associated lifetimes are not supported")
.span_label(lt.ident.span, "the lifetime is given here")
.help("if you meant to specify a trait object, write `dyn Trait + 'lifetime`")
.emit();
self.sess.emit_err(errors::AssocLifetime { span, lifetime: lt.ident.span });
self.mk_ty(span, ast::TyKind::Err).into()
}
None => {

View File

@ -1,7 +1,7 @@
use super::{Parser, PathStyle, TokenType};
use crate::errors::{
DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg,
InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime,
NegativeBoundsNotSupported, NegativeBoundsNotSupportedSugg, NestedCVariadicType,
@ -807,16 +807,11 @@ impl<'a> Parser<'a> {
/// Emits an error if any trait bound modifiers were present.
fn error_lt_bound_with_modifiers(&self, modifiers: BoundModifiers) {
if let Some(span) = modifiers.maybe_const {
self.struct_span_err(
span,
"`~const` may only modify trait bounds, not lifetime bounds",
)
.emit();
self.sess.emit_err(errors::TildeConstLifetime { span });
}
if let Some(span) = modifiers.maybe {
self.struct_span_err(span, "`?` may only modify trait bounds, not lifetime bounds")
.emit();
self.sess.emit_err(errors::MaybeLifetime { span });
}
}
@ -824,19 +819,14 @@ impl<'a> Parser<'a> {
fn recover_paren_lifetime(&mut self, lo: Span, inner_lo: Span) -> PResult<'a, ()> {
let inner_span = inner_lo.to(self.prev_token.span);
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
let mut err = self.struct_span_err(
lo.to(self.prev_token.span),
"parenthesized lifetime bounds are not supported",
);
if let Ok(snippet) = self.span_to_snippet(inner_span) {
err.span_suggestion_short(
lo.to(self.prev_token.span),
"remove the parentheses",
snippet,
Applicability::MachineApplicable,
);
}
err.emit();
let span = lo.to(self.prev_token.span);
let (sugg, snippet) = if let Ok(snippet) = self.span_to_snippet(inner_span) {
(Some(span), snippet)
} else {
(None, String::new())
};
self.sess.emit_err(errors::ParenthesizedLifetime { span, sugg, snippet });
Ok(())
}
@ -857,15 +847,7 @@ impl<'a> Parser<'a> {
} else if self.eat_keyword(kw::Const) {
let span = self.prev_token.span;
self.sess.gated_spans.gate(sym::const_trait_impl, span);
self.struct_span_err(span, "const bounds must start with `~`")
.span_suggestion(
span.shrink_to_lo(),
"add `~`",
"~",
Applicability::MachineApplicable,
)
.emit();
self.sess.emit_err(errors::ConstMissingTilde { span, start: span.shrink_to_lo() });
Some(span)
} else {

View File

@ -226,3 +226,10 @@ resolve_add_as_non_derive =
resolve_proc_macro_same_crate = can't use a procedural macro from the same crate that defines it
.help = you can define integration tests in a directory named `tests`
resolve_imported_crate = `$crate` may not be imported
resolve_macro_use_extern_crate_self = `#[macro_use]` is not supported on `extern crate self`
resolve_accessible_unsure = not sure whether the path is accessible or not
.note = the type may have associated items, but we are currently not checking them

View File

@ -9,7 +9,9 @@ use crate::def_collector::collect_definitions;
use crate::imports::{Import, ImportKind};
use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
use crate::Namespace::{self, MacroNS, TypeNS, ValueNS};
use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot};
use crate::{
errors, Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot,
};
use crate::{
MacroData, NameBinding, NameBindingKind, ParentScope, PathResult, PerNS, ResolutionError,
};
@ -523,11 +525,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
ident.name = crate_name;
}
self.r
.tcx
.sess
.struct_span_err(item.span, "`$crate` may not be imported")
.emit();
self.r.tcx.sess.emit_err(errors::CrateImported { span: item.span });
}
}
@ -1028,11 +1026,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
self.r
.tcx
.sess
.struct_span_err(
attr.span,
"`#[macro_use]` is not supported on `extern crate self`",
)
.emit();
.emit_err(errors::MacroUseExternCrateSelf { span: attr.span });
}
}
let ill_formed = |span| {

View File

@ -517,3 +517,25 @@ pub(crate) struct ProcMacroSameCrate {
#[help]
pub(crate) is_test: bool,
}
#[derive(Diagnostic)]
#[diag(resolve_imported_crate)]
pub(crate) struct CrateImported {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(resolve_macro_use_extern_crate_self)]
pub(crate) struct MacroUseExternCrateSelf {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(resolve_accessible_unsure)]
#[note]
pub(crate) struct CfgAccessibleUnsure {
#[primary_span]
pub(crate) span: Span,
}

View File

@ -436,9 +436,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
// HACK(Urgau): This shouldn't be necessary
PathResult::Failed { is_error_from_last_segment: false, .. } => {
self.tcx.sess
.struct_span_err(span, "not sure whether the path is accessible or not")
.note("the type may have associated items, but we are currently not checking them")
.emit();
.emit_err(errors::CfgAccessibleUnsure { span });
// If we get a partially resolved NonModule in one namespace, we should get the
// same result in any other namespaces, so we can return early.

View File

@ -651,6 +651,7 @@ symbols! {
edition_panic,
eh_catch_typeinfo,
eh_personality,
emit,
emit_enum,
emit_enum_variant,
emit_enum_variant_arg,