diff options
124 files changed, 5241 insertions, 1673 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 340302766d2..60b7f2e4c22 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3039,7 +3039,7 @@ pub type ForeignItem = Item<ForeignItemKind>; mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(AssocItem, 104); static_assert_size!(AssocItemKind, 32); static_assert_size!(Attribute, 32); @@ -3060,11 +3060,12 @@ mod size_asserts { static_assert_size!(Local, 72); static_assert_size!(Param, 40); static_assert_size!(Pat, 120); - static_assert_size!(PatKind, 96); static_assert_size!(Path, 40); static_assert_size!(PathSegment, 24); + static_assert_size!(PatKind, 96); static_assert_size!(Stmt, 32); static_assert_size!(StmtKind, 16); static_assert_size!(Ty, 96); static_assert_size!(TyKind, 72); + // tidy-alphabetical-end } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 16224d71e45..83b10d906e2 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -889,10 +889,11 @@ where mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(Lit, 12); static_assert_size!(LitKind, 2); static_assert_size!(Nonterminal, 16); static_assert_size!(Token, 24); static_assert_size!(TokenKind, 16); + // tidy-alphabetical-end } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 4d2049cbc41..015f5c1ee8a 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -245,12 +245,12 @@ impl AttrTokenStream { // properly implemented - we always synthesize fake tokens, // so we never reach this code. - let mut builder = TokenStreamBuilder::new(); + let mut stream = TokenStream::default(); for inner_attr in inner_attrs { - builder.push(inner_attr.tokens()); + stream.push_stream(inner_attr.tokens()); } - builder.push(delim_tokens.clone()); - *tree = TokenTree::Delimited(*span, *delim, builder.build()); + stream.push_stream(delim_tokens.clone()); + *tree = TokenTree::Delimited(*span, *delim, stream); found = true; break; } @@ -505,76 +505,49 @@ impl TokenStream { self.trees().map(|tree| TokenStream::flatten_token_tree(tree)).collect() } -} -// 99.5%+ of the time we have 1 or 2 elements in this vector. -#[derive(Clone)] -pub struct TokenStreamBuilder(SmallVec<[TokenStream; 2]>); - -impl TokenStreamBuilder { - pub fn new() -> TokenStreamBuilder { - TokenStreamBuilder(SmallVec::new()) - } - - pub fn push(&mut self, stream: TokenStream) { - self.0.push(stream); - } - - pub fn build(self) -> TokenStream { - let mut streams = self.0; - match streams.len() { - 0 => TokenStream::default(), - 1 => streams.pop().unwrap(), - _ => { - // We will extend the first stream in `streams` with the - // elements from the subsequent streams. This requires using - // `make_mut()` on the first stream, and in practice this - // doesn't cause cloning 99.9% of the time. - // - // One very common use case is when `streams` has two elements, - // where the first stream has any number of elements within - // (often 1, but sometimes many more) and the second stream has - // a single element within. - - // Determine how much the first stream will be extended. - // Needed to avoid quadratic blow up from on-the-fly - // reallocations (#57735). - let num_appends = streams.iter().skip(1).map(|ts| ts.len()).sum(); - - // Get the first stream, which will become the result stream. - // If it's `None`, create an empty stream. - let mut iter = streams.into_iter(); - let mut res_stream_lrc = iter.next().unwrap().0; - - // Append the subsequent elements to the result stream, after - // reserving space for them. - let res_vec_mut = Lrc::make_mut(&mut res_stream_lrc); - res_vec_mut.reserve(num_appends); - for stream in iter { - let stream_iter = stream.0.iter().cloned(); - - // If (a) `res_mut_vec` is not empty and the last tree - // within it is a token tree marked with `Joint`, and (b) - // `stream` is not empty and the first tree within it is a - // token tree, and (c) the two tokens can be glued - // together... - if let Some(TokenTree::Token(last_tok, Spacing::Joint)) = res_vec_mut.last() - && let Some(TokenTree::Token(tok, spacing)) = stream.0.first() - && let Some(glued_tok) = last_tok.glue(&tok) - { - // ...then overwrite the last token tree in - // `res_vec_mut` with the glued token, and skip the - // first token tree from `stream`. - *res_vec_mut.last_mut().unwrap() = TokenTree::Token(glued_tok, *spacing); - res_vec_mut.extend(stream_iter.skip(1)); - } else { - // Append all of `stream`. - res_vec_mut.extend(stream_iter); - } - } + // If `vec` is not empty, try to glue `tt` onto its last token. The return + // value indicates if gluing took place. + fn try_glue_to_last(vec: &mut Vec<TokenTree>, tt: &TokenTree) -> bool { + if let Some(TokenTree::Token(last_tok, Spacing::Joint)) = vec.last() + && let TokenTree::Token(tok, spacing) = tt + && let Some(glued_tok) = last_tok.glue(&tok) + { + // ...then overwrite the last token tree in `vec` with the + // glued token, and skip the first token tree from `stream`. + *vec.last_mut().unwrap() = TokenTree::Token(glued_tok, *spacing); + true + } else { + false + } + } - TokenStream(res_stream_lrc) - } + // Push `tt` onto the end of the stream, possibly gluing it to the last + // token. Uses `make_mut` to maximize efficiency. + pub fn push_tree(&mut self, tt: TokenTree) { + let vec_mut = Lrc::make_mut(&mut self.0); + + if Self::try_glue_to_last(vec_mut, &tt) { + // nothing else to do + } else { + vec_mut.push(tt); + } + } + + // Push `stream` onto the end of the stream, possibly gluing the first + // token tree to the last token. (No other token trees will be glued.) + // Uses `make_mut` to maximize efficiency. + pub fn push_stream(&mut self, stream: TokenStream) { + let vec_mut = Lrc::make_mut(&mut self.0); + + let stream_iter = stream.0.iter().cloned(); + + if let Some(first) = stream.0.first() && Self::try_glue_to_last(vec_mut, first) { + // Now skip the first token tree from `stream`. + vec_mut.extend(stream_iter.skip(1)); + } else { + // Append all of `stream`. + vec_mut.extend(stream_iter); } } } @@ -673,10 +646,11 @@ impl DelimSpan { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(AttrTokenStream, 8); static_assert_size!(AttrTokenTree, 32); static_assert_size!(LazyAttrTokenStream, 8); static_assert_size!(TokenStream, 8); static_assert_size!(TokenTree, 32); + // tidy-alphabetical-end } diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 63ff64b00be..c6c85ffa84d 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -1,4 +1,7 @@ -use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay}; +use rustc_errors::{ + fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay, + SubdiagnosticMessage, +}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -19,7 +22,10 @@ pub struct UseAngleBrackets { } impl AddToDiagnostic for UseAngleBrackets { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { diag.multipart_suggestion( fluent::ast_lowering::use_angle_brackets, vec![(self.open_param, String::from("<")), (self.close_param, String::from(">"))], @@ -69,7 +75,10 @@ pub enum AssocTyParenthesesSub { } impl AddToDiagnostic for AssocTyParenthesesSub { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self { Self::Empty { parentheses_span } => diag.multipart_suggestion( fluent::ast_lowering::remove_parentheses, diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 035f0ce1cbc..ba2ed24fc08 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -1,6 +1,6 @@ //! Errors emitted by ast_passes. -use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic}; +use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -17,7 +17,10 @@ pub struct ForbiddenLet { } impl AddToDiagnostic for ForbiddenLetReason { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self { Self::GenericForbidden => {} Self::NotSupportedOr(span) => { @@ -228,7 +231,10 @@ pub struct ExternBlockSuggestion { } impl AddToDiagnostic for ExternBlockSuggestion { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { let start_suggestion = if let Some(abi) = self.abi { format!("extern \"{}\" {{", abi) } else { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6c4b2b3724e..dc0f0e7cd3c 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -584,6 +584,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // modify their locations. let all_facts = &mut None; let mut constraints = Default::default(); + let mut type_tests = Default::default(); let mut closure_bounds = Default::default(); let mut liveness_constraints = LivenessValues::new(Rc::new(RegionValueElements::new(&promoted_body))); @@ -595,6 +596,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { &mut this.cx.borrowck_context.constraints.outlives_constraints, &mut constraints, ); + mem::swap(&mut this.cx.borrowck_context.constraints.type_tests, &mut type_tests); mem::swap( &mut this.cx.borrowck_context.constraints.closure_bounds_mapping, &mut closure_bounds, @@ -619,6 +621,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { swap_constraints(self); let locations = location.to_locations(); + + // Use location of promoted const in collected constraints + for type_test in type_tests.iter() { + let mut type_test = type_test.clone(); + type_test.locations = locations; + self.cx.borrowck_context.constraints.type_tests.push(type_test) + } for constraint in constraints.outlives().iter() { let mut constraint = constraint.clone(); constraint.locations = locations; diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 1f577e9f352..a292bfce31e 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -15,7 +15,10 @@ use rustc_data_structures::profiling::TimingGuard; use rustc_data_structures::profiling::VerboseTimingGuard; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::Emitter; -use rustc_errors::{translation::Translate, DiagnosticId, FatalError, Handler, Level}; +use rustc_errors::{ + translation::{to_fluent_args, Translate}, + DiagnosticId, FatalError, Handler, Level, +}; use rustc_fs_util::link_or_copy; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ @@ -1740,7 +1743,7 @@ impl Translate for SharedEmitter { impl Emitter for SharedEmitter { fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) { - let fluent_args = self.to_fluent_args(diag.args()); + let fluent_args = to_fluent_args(diag.args()); drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { msg: self.translate_messages(&diag.message, &fluent_args).to_string(), code: diag.code.clone(), diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index f1674d04f8d..cdcebb61c2e 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -25,12 +25,10 @@ pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { /// report whether said intrinsic has a `rustc_const_{un,}stable` attribute. Otherwise, return /// `Constness::NotConst`. fn constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness { - let def_id = def_id.expect_local(); - let node = tcx.hir().get_by_def_id(def_id); - - match node { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + match tcx.hir().get(hir_id) { hir::Node::Ctor(_) => hir::Constness::Const, - hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness, + hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => { // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other // foreign items cannot be evaluated at compile-time. @@ -41,20 +39,62 @@ fn constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness { }; if is_const { hir::Constness::Const } else { hir::Constness::NotConst } } - _ => { - if let Some(fn_kind) = node.fn_kind() { - if fn_kind.constness() == hir::Constness::Const { - return hir::Constness::Const; - } - // If the function itself is not annotated with `const`, it may still be a `const fn` - // if it resides in a const trait impl. - let is_const = is_parent_const_impl_raw(tcx, def_id); - if is_const { hir::Constness::Const } else { hir::Constness::NotConst } - } else { - hir::Constness::NotConst + hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) + if tcx.is_const_default_method(def_id) => + { + hir::Constness::Const + } + + hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(..), .. }) + | hir::Node::Item(hir::Item { kind: hir::ItemKind::Static(..), .. }) + | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Const(..), .. }) + | hir::Node::AnonConst(_) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) + | hir::Node::ImplItem(hir::ImplItem { + kind: + hir::ImplItemKind::Fn( + hir::FnSig { + header: hir::FnHeader { constness: hir::Constness::Const, .. }, + .. + }, + .., + ), + .. + }) => hir::Constness::Const, + + hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Type(..) | hir::ImplItemKind::Fn(..), + .. + }) => { + let parent_hir_id = tcx.hir().get_parent_node(hir_id); + match tcx.hir().get(parent_hir_id) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), + .. + }) => *constness, + _ => span_bug!( + tcx.def_span(parent_hir_id.owner), + "impl item's parent node is not an impl", + ), } } + + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..), + .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: + hir::TraitItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..), + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), + .. + }) => *constness, + + _ => hir::Constness::NotConst, } } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 510adde6296..719588a936c 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -788,9 +788,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(Immediate, 48); static_assert_size!(ImmTy<'_>, 64); static_assert_size!(Operand, 56); static_assert_size!(OpTy<'_>, 80); + // tidy-alphabetical-end } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index eeeb7d6d3e5..b0625b5f412 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -892,10 +892,11 @@ where mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. - static_assert_size!(MemPlaceMeta, 24); + // tidy-alphabetical-start static_assert_size!(MemPlace, 40); + static_assert_size!(MemPlaceMeta, 24); static_assert_size!(MPlaceTy<'_>, 64); static_assert_size!(Place, 40); static_assert_size!(PlaceTy<'_>, 64); + // tidy-alphabetical-end } diff --git a/compiler/rustc_error_messages/locales/en-US/middle.ftl b/compiler/rustc_error_messages/locales/en-US/middle.ftl index ca3c91ce24a..b9e4499d47f 100644 --- a/compiler/rustc_error_messages/locales/en-US/middle.ftl +++ b/compiler/rustc_error_messages/locales/en-US/middle.ftl @@ -18,3 +18,12 @@ middle_limit_invalid = middle_const_eval_non_int = constant evaluation of enum discriminant resulted in non-integer + +middle_unknown_layout = + the type `{$ty}` has an unknown layout + +middle_values_too_big = + values of the type `{$ty}` are too big for the current architecture + +middle_cannot_be_normalized = + unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index 995ad4fe258..1f1c9c29d66 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -10,88 +10,119 @@ passes_outer_crate_level_attr = passes_inner_crate_level_attr = crate-level attribute should be in the root module -passes_ignored_attr_with_macro = `#[{$sym}]` is ignored on struct fields, match arms and macro defs +passes_ignored_attr_with_macro = + `#[{$sym}]` is ignored on struct fields, match arms and macro defs .warn = {-passes_previously_accepted} .note = {-passes_see_issue(issue: "80564")} -passes_ignored_attr = `#[{$sym}]` is ignored on struct fields and match arms +passes_ignored_attr = + `#[{$sym}]` is ignored on struct fields and match arms .warn = {-passes_previously_accepted} .note = {-passes_see_issue(issue: "80564")} -passes_inline_ignored_function_prototype = `#[inline]` is ignored on function prototypes +passes_inline_ignored_function_prototype = + `#[inline]` is ignored on function prototypes -passes_inline_ignored_constants = `#[inline]` is ignored on constants +passes_inline_ignored_constants = + `#[inline]` is ignored on constants .warn = {-passes_previously_accepted} .note = {-passes_see_issue(issue: "65833")} -passes_inline_not_fn_or_closure = attribute should be applied to function or closure +passes_inline_not_fn_or_closure = + attribute should be applied to function or closure .label = not a function or closure -passes_no_coverage_ignored_function_prototype = `#[no_coverage]` is ignored on function prototypes +passes_no_coverage_ignored_function_prototype = + `#[no_coverage]` is ignored on function prototypes passes_no_coverage_propagate = `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly -passes_no_coverage_fn_defn = `#[no_coverage]` may only be applied to function definitions +passes_no_coverage_fn_defn = + `#[no_coverage]` may only be applied to function definitions -passes_no_coverage_not_coverable = `#[no_coverage]` must be applied to coverable code +passes_no_coverage_not_coverable = + `#[no_coverage]` must be applied to coverable code .label = not coverable code -passes_should_be_applied_to_fn = attribute should be applied to a function definition +passes_should_be_applied_to_fn = + attribute should be applied to a function definition .label = not a function definition -passes_naked_tracked_caller = cannot use `#[track_caller]` with `#[naked]` +passes_naked_tracked_caller = + cannot use `#[track_caller]` with `#[naked]` -passes_should_be_applied_to_struct_enum = attribute should be applied to a struct or enum +passes_should_be_applied_to_struct_enum = + attribute should be applied to a struct or enum .label = not a struct or enum -passes_should_be_applied_to_trait = attribute should be applied to a trait +passes_should_be_applied_to_trait = + attribute should be applied to a trait .label = not a trait -passes_target_feature_on_statement = {passes_should_be_applied_to_fn} +passes_target_feature_on_statement = + {passes_should_be_applied_to_fn} .warn = {-passes_previously_accepted} .label = {passes_should_be_applied_to_fn.label} -passes_should_be_applied_to_static = attribute should be applied to a static +passes_should_be_applied_to_static = + attribute should be applied to a static .label = not a static -passes_doc_expect_str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")] +passes_doc_expect_str = + doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")] -passes_doc_alias_empty = {$attr_str} attribute cannot have empty value +passes_doc_alias_empty = + {$attr_str} attribute cannot have empty value -passes_doc_alias_bad_char = {$char_} character isn't allowed in {$attr_str} +passes_doc_alias_bad_char = + {$char_} character isn't allowed in {$attr_str} -passes_doc_alias_start_end = {$attr_str} cannot start or end with ' ' +passes_doc_alias_start_end = + {$attr_str} cannot start or end with ' ' -passes_doc_alias_bad_location = {$attr_str} isn't allowed on {$location} +passes_doc_alias_bad_location = + {$attr_str} isn't allowed on {$location} -passes_doc_alias_not_an_alias = {$attr_str} is the same as the item's name +passes_doc_alias_not_an_alias = + {$attr_str} is the same as the item's name passes_doc_alias_duplicated = doc alias is duplicated .label = first defined here -passes_doc_alias_not_string_literal = `#[doc(alias("a"))]` expects string literals +passes_doc_alias_not_string_literal = + `#[doc(alias("a"))]` expects string literals passes_doc_alias_malformed = doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` -passes_doc_keyword_empty_mod = `#[doc(keyword = "...")]` should be used on empty modules +passes_doc_keyword_empty_mod = + `#[doc(keyword = "...")]` should be used on empty modules -passes_doc_keyword_not_mod = `#[doc(keyword = "...")]` should be used on modules +passes_doc_keyword_not_mod = + `#[doc(keyword = "...")]` should be used on modules -passes_doc_keyword_invalid_ident = `{$doc_keyword}` is not a valid identifier +passes_doc_keyword_invalid_ident = + `{$doc_keyword}` is not a valid identifier passes_doc_fake_variadic_not_valid = `#[doc(fake_variadic)]` must be used on the first of a set of tuple or fn pointer trait impls with varying arity -passes_doc_keyword_only_impl = `#[doc(keyword = "...")]` should be used on impl blocks +passes_doc_keyword_only_impl = + `#[doc(keyword = "...")]` should be used on impl blocks -passes_doc_inline_conflict_first = this attribute... -passes_doc_inline_conflict_second = ...conflicts with this attribute -passes_doc_inline_conflict = conflicting doc inlining attributes +passes_doc_inline_conflict_first = + this attribute... + +passes_doc_inline_conflict_second = + {"."}..conflicts with this attribute + +passes_doc_inline_conflict = + conflicting doc inlining attributes .help = remove one of the conflicting attributes -passes_doc_inline_only_use = this attribute can only be applied to a `use` item +passes_doc_inline_only_use = + this attribute can only be applied to a `use` item .label = only applicable on `use` items .not_a_use_item_label = not a `use` item .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline> for more information @@ -99,30 +130,39 @@ passes_doc_inline_only_use = this attribute can only be applied to a `use` item passes_doc_attr_not_crate_level = `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute -passes_attr_crate_level = this attribute can only be applied at the crate level +passes_attr_crate_level = + this attribute can only be applied at the crate level .suggestion = to apply to the crate, use an inner attribute .help = to apply to the crate, use an inner attribute .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information -passes_doc_test_unknown = unknown `doc(test)` attribute `{$path}` +passes_doc_test_unknown = + unknown `doc(test)` attribute `{$path}` -passes_doc_test_takes_list = `#[doc(test(...)]` takes a list of attributes +passes_doc_test_takes_list = + `#[doc(test(...)]` takes a list of attributes -passes_doc_primitive = `doc(primitive)` should never have been stable +passes_doc_primitive = + `doc(primitive)` should never have been stable -passes_doc_test_unknown_any = unknown `doc` attribute `{$path}` +passes_doc_test_unknown_any = + unknown `doc` attribute `{$path}` -passes_doc_test_unknown_spotlight = unknown `doc` attribute `{$path}` +passes_doc_test_unknown_spotlight = + unknown `doc` attribute `{$path}` .note = `doc(spotlight)` was renamed to `doc(notable_trait)` .suggestion = use `notable_trait` instead .no_op_note = `doc(spotlight)` is now a no-op -passes_doc_test_unknown_include = unknown `doc` attribute `{$path}` +passes_doc_test_unknown_include = + unknown `doc` attribute `{$path}` .suggestion = use `doc = include_str!` instead -passes_doc_invalid = invalid `doc` attribute +passes_doc_invalid = + invalid `doc` attribute -passes_pass_by_value = `pass_by_value` attribute should be applied to a struct, enum or type alias +passes_pass_by_value = + `pass_by_value` attribute should be applied to a struct, enum or type alias .label = is not a struct, enum or type alias passes_allow_incoherent_impl = @@ -137,42 +177,54 @@ passes_must_use_async = `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within .label = this attribute does nothing, the `Future`s returned by async functions are already `must_use` -passes_must_use_no_effect = `#[must_use]` has no effect when applied to {$article} {$target} +passes_must_use_no_effect = + `#[must_use]` has no effect when applied to {$article} {$target} -passes_must_not_suspend = `must_not_suspend` attribute should be applied to a struct, enum, or trait +passes_must_not_suspend = + `must_not_suspend` attribute should be applied to a struct, enum, or trait .label = is not a struct, enum, or trait -passes_cold = {passes_should_be_applied_to_fn} +passes_cold = + {passes_should_be_applied_to_fn} .warn = {-passes_previously_accepted} .label = {passes_should_be_applied_to_fn.label} -passes_link = attribute should be applied to an `extern` block with non-Rust ABI +passes_link = + attribute should be applied to an `extern` block with non-Rust ABI .warn = {-passes_previously_accepted} .label = not an `extern` block -passes_link_name = attribute should be applied to a foreign function or static +passes_link_name = + attribute should be applied to a foreign function or static .warn = {-passes_previously_accepted} .label = not a foreign function or static .help = try `#[link(name = "{$value}")]` instead -passes_no_link = attribute should be applied to an `extern crate` item +passes_no_link = + attribute should be applied to an `extern crate` item .label = not an `extern crate` item -passes_export_name = attribute should be applied to a free function, impl method or static +passes_export_name = + attribute should be applied to a free function, impl method or static .label = not a free function, impl method or static -passes_rustc_layout_scalar_valid_range_not_struct = attribute should be applied to a struct +passes_rustc_layout_scalar_valid_range_not_struct = + attribute should be applied to a struct .label = not a struct -passes_rustc_layout_scalar_valid_range_arg = expected exactly one integer literal argument +passes_rustc_layout_scalar_valid_range_arg = + expected exactly one integer literal argument -passes_rustc_legacy_const_generics_only = #[rustc_legacy_const_generics] functions must only have const generics +passes_rustc_legacy_const_generics_only = + #[rustc_legacy_const_generics] functions must only have const generics .label = non-const generic parameter -passes_rustc_legacy_const_generics_index = #[rustc_legacy_const_generics] must have one index for each generic parameter +passes_rustc_legacy_const_generics_index = + #[rustc_legacy_const_generics] must have one index for each generic parameter .label = generic parameters -passes_rustc_legacy_const_generics_index_exceed = index exceeds number of arguments +passes_rustc_legacy_const_generics_index_exceed = + index exceeds number of arguments .label = there {$arg_count -> [one] is *[other] are @@ -181,93 +233,438 @@ passes_rustc_legacy_const_generics_index_exceed = index exceeds number of argume *[other] arguments } -passes_rustc_legacy_const_generics_index_negative = arguments should be non-negative integers +passes_rustc_legacy_const_generics_index_negative = + arguments should be non-negative integers -passes_rustc_dirty_clean = attribute requires -Z query-dep-graph to be enabled +passes_rustc_dirty_clean = + attribute requires -Z query-dep-graph to be enabled -passes_link_section = attribute should be applied to a function or static +passes_link_section = + attribute should be applied to a function or static .warn = {-passes_previously_accepted} .label = not a function or static -passes_no_mangle_foreign = `#[no_mangle]` has no effect on a foreign {$foreign_item_kind} +passes_no_mangle_foreign = + `#[no_mangle]` has no effect on a foreign {$foreign_item_kind} .warn = {-passes_previously_accepted} .label = foreign {$foreign_item_kind} .note = symbol names in extern blocks are not mangled .suggestion = remove this attribute -passes_no_mangle = attribute should be applied to a free function, impl method or static +passes_no_mangle = + attribute should be applied to a free function, impl method or static .warn = {-passes_previously_accepted} .label = not a free function, impl method or static -passes_repr_ident = meta item in `repr` must be an identifier +passes_repr_ident = + meta item in `repr` must be an identifier -passes_repr_conflicting = conflicting representation hints +passes_repr_conflicting = + conflicting representation hints -passes_used_static = attribute must be applied to a `static` variable +passes_used_static = + attribute must be applied to a `static` variable -passes_used_compiler_linker = `used(compiler)` and `used(linker)` can't be used together +passes_used_compiler_linker = + `used(compiler)` and `used(linker)` can't be used together -passes_allow_internal_unstable = attribute should be applied to a macro +passes_allow_internal_unstable = + attribute should be applied to a macro .label = not a macro -passes_debug_visualizer_placement = attribute should be applied to a module +passes_debug_visualizer_placement = + attribute should be applied to a module -passes_debug_visualizer_invalid = invalid argument +passes_debug_visualizer_invalid = + invalid argument .note_1 = expected: `natvis_file = "..."` .note_2 = OR .note_3 = expected: `gdb_script_file = "..."` -passes_rustc_allow_const_fn_unstable = attribute should be applied to `const fn` +passes_debug_visualizer_unreadable = + couldn't read {$file}: {$error} + +passes_rustc_allow_const_fn_unstable = + attribute should be applied to `const fn` .label = not a `const fn` -passes_rustc_std_internal_symbol = attribute should be applied to functions or statics +passes_rustc_std_internal_symbol = + attribute should be applied to functions or statics .label = not a function or static -passes_const_trait = attribute should be applied to a trait +passes_const_trait = + attribute should be applied to a trait -passes_stability_promotable = attribute cannot be applied to an expression +passes_stability_promotable = + attribute cannot be applied to an expression -passes_deprecated = attribute is ignored here +passes_deprecated = + attribute is ignored here -passes_macro_use = `#[{$name}]` only has an effect on `extern crate` and modules +passes_macro_use = + `#[{$name}]` only has an effect on `extern crate` and modules -passes_macro_export = `#[macro_export]` only has an effect on macro definitions +passes_macro_export = + `#[macro_export]` only has an effect on macro definitions -passes_plugin_registrar = `#[plugin_registrar]` only has an effect on functions +passes_plugin_registrar = + `#[plugin_registrar]` only has an effect on functions -passes_unused_empty_lints_note = attribute `{$name}` with an empty list has no effect +passes_unused_empty_lints_note = + attribute `{$name}` with an empty list has no effect -passes_unused_no_lints_note = attribute `{$name}` without any lints has no effect +passes_unused_no_lints_note = + attribute `{$name}` without any lints has no effect passes_unused_default_method_body_const_note = `default_method_body_is_const` has been replaced with `#[const_trait]` on traits -passes_unused = unused attribute +passes_unused = + unused attribute .suggestion = remove this attribute -passes_non_exported_macro_invalid_attrs = attribute should be applied to function or closure +passes_non_exported_macro_invalid_attrs = + attribute should be applied to function or closure .label = not a function or closure -passes_unused_duplicate = unused attribute +passes_unused_duplicate = + unused attribute .suggestion = remove this attribute .note = attribute also specified here .warn = {-passes_previously_accepted} -passes_unused_multiple = multiple `{$name}` attributes +passes_unused_multiple = + multiple `{$name}` attributes .suggestion = remove this attribute .note = attribute also specified here -passes_rustc_lint_opt_ty = `#[rustc_lint_opt_ty]` should be applied to a struct +passes_rustc_lint_opt_ty = + `#[rustc_lint_opt_ty]` should be applied to a struct .label = not a struct -passes_rustc_lint_opt_deny_field_access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field +passes_rustc_lint_opt_deny_field_access = + `#[rustc_lint_opt_deny_field_access]` should be applied to a field .label = not a field -passes_link_ordinal = attribute should be applied to a foreign function or static +passes_link_ordinal = + attribute should be applied to a foreign function or static .label = not a foreign function or static -passes_collapse_debuginfo = `collapse_debuginfo` attribute should be applied to macro definitions +passes_collapse_debuginfo = + `collapse_debuginfo` attribute should be applied to macro definitions .label = not a macro definition -passes_deprecated_annotation_has_no_effect = this `#[deprecated]` annotation has no effect +passes_deprecated_annotation_has_no_effect = + this `#[deprecated]` annotation has no effect .suggestion = remove the unnecessary deprecation attribute + +passes_unknown_external_lang_item = + unknown external lang item: `{$lang_item}` + +passes_missing_panic_handler = + `#[panic_handler]` function required, but not found + +passes_alloc_func_required = + `#[alloc_error_handler]` function required, but not found + +passes_missing_alloc_error_handler = + use `#![feature(default_alloc_error_handler)]` for a default error handler + +passes_missing_lang_item = + language item required, but not found: `{$name}` + .note = this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library + .help = you may be able to compile for a target that doesn't need `{$name}`, specify a target with `--target` or in `.cargo/config` + +passes_lang_item_on_incorrect_target = + `{$name}` language item must be applied to a {$expected_target} + .label = attribute should be applied to a {$expected_target}, not a {$actual_target} + +passes_unknown_lang_item = + definition of an unknown language item: `{$name}` + .label = definition of unknown language item `{$name}` + +passes_invalid_attr_at_crate_level = + `{$name}` attribute cannot be used at crate level + .suggestion = perhaps you meant to use an outer attribute + +passes_duplicate_diagnostic_item = + duplicate diagnostic item found: `{$name}`. + +passes_duplicate_diagnostic_item_in_crate = + duplicate diagnostic item in crate `{$crate_name}`: `{$name}`. + +passes_diagnostic_item_first_defined = + the diagnostic item is first defined here + .note = the diagnostic item is first defined in crate `{$orig_crate_name}`. + +passes_abi = + abi: {$abi} + +passes_align = + align: {$align} + +passes_size = + size: {$size} + +passes_homogeneous_aggregate = + homogeneous_aggregate: {$homogeneous_aggregate} + +passes_layout_of = + layout_of({$normalized_ty}) = {$ty_layout} + +passes_unrecognized_field = + unrecognized field name `{$name}` + +passes_layout = + layout error: {$layout_error} + +passes_feature_stable_twice = + feature `{$feature}` is declared stable since {$since}, but was previously declared stable since {$prev_since} + +passes_feature_previously_declared = + feature `{$feature}` is declared {$declared}, but was previously declared {$prev_declared} + +passes_expr_not_allowed_in_context = + {$expr} is not allowed in a `{$context}` + +passes_const_impl_const_trait = + const `impl`s must be for traits marked with `#[const_trait]` + .note = this trait must be annotated with `#[const_trait]` + +passes_break_non_loop = + `break` with value from a `{$kind}` loop + .label = can only break with a value inside `loop` or breakable block + .label2 = you can't `break` with a value in a `{$kind}` loop + .suggestion = use `break` on its own without a value inside this `{$kind}` loop + .break_expr_suggestion = alternatively, you might have meant to use the available loop label + +passes_continue_labeled_block = + `continue` pointing to a labeled block + .label = labeled blocks cannot be `continue`'d + .block_label = labeled block the `continue` points to + +passes_break_inside_closure = + `{$name}` inside of a closure + .label = cannot `{$name}` inside of a closure + .closure_label = enclosing closure + +passes_break_inside_async_block = + `{$name}` inside of an `async` block + .label = cannot `{$name}` inside of an `async` block + .async_block_label = enclosing `async` block + +passes_outside_loop = + `{$name}` outside of a loop + .label = cannot `{$name}` outside of a loop + +passes_unlabeled_in_labeled_block = + unlabeled `{$cf_type}` inside of a labeled block + .label = `{$cf_type}` statements that would diverge to or through a labeled block need to bear a label + +passes_unlabeled_cf_in_while_condition = + `break` or `continue` with no label in the condition of a `while` loop + .label = unlabeled `{$cf_type}` in the condition of a `while` loop + +passes_cannot_inline_naked_function = + naked functions cannot be inlined + +passes_undefined_naked_function_abi = + Rust ABI is unsupported in naked functions + +passes_no_patterns = + patterns not allowed in naked function parameters + +passes_params_not_allowed = + referencing function parameters is not allowed in naked functions + .help = follow the calling convention in asm block to use parameters + +passes_naked_functions_asm_block = + naked functions must contain a single asm block + .label_multiple_asm = multiple asm blocks are unsupported in naked functions + .label_non_asm = non-asm is unsupported in naked functions + +passes_naked_functions_operands = + only `const` and `sym` operands are supported in naked functions + +passes_naked_functions_asm_options = + asm options unsupported in naked functions: {$unsupported_options} + +passes_naked_functions_must_use_noreturn = + asm in naked functions must use `noreturn` option + .suggestion = consider specifying that the asm block is responsible for returning from the function + +passes_attr_only_on_main = + `{$attr}` attribute can only be used on `fn main()` + +passes_attr_only_on_root_main = + `{$attr}` attribute can only be used on root `fn main()` + +passes_attr_only_in_functions = + `{$attr}` attribute can only be used on functions + +passes_multiple_rustc_main = + multiple functions with a `#[rustc_main]` attribute + .first = first `#[rustc_main]` function + .additional = additional `#[rustc_main]` function + +passes_multiple_start_functions = + multiple `start` functions + .label = multiple `start` functions + .previous = previous `#[start]` function here + +passes_extern_main = + the `main` function cannot be declared in an `extern` block + +passes_unix_sigpipe_values = + valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl` + +passes_no_main_function = + `main` function not found in crate `{$crate_name}` + .here_is_main = here is a function named `main` + .one_or_more_possible_main = you have one or more functions named `main` not defined at the crate level + .consider_moving_main = consider moving the `main` function definitions + .main_must_be_defined_at_crate = the main function must be defined at the crate level{$has_filename -> + [true] {" "}(in `{$filename}`) + *[false] {""} + } + .consider_adding_main_to_file = consider adding a `main` function to `{$filename}` + .consider_adding_main_at_crate = consider adding a `main` function at the crate level + .teach_note = If you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/ + .non_function_main = non-function item at `crate::main` is found + +passes_duplicate_lang_item = + found duplicate lang item `{$lang_item_name}` + .first_defined_span = the lang item is first defined here + .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on) + .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}`. + .first_definition_local = first definition in the local crate (`{$orig_crate_name}`) + .second_definition_local = second definition in the local crate (`{$crate_name}`) + .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} + .second_definition_path = second definition in `{$crate_name}` loaded from {$path} + +passes_duplicate_lang_item_crate = + duplicate lang item in crate `{$crate_name}`: `{$lang_item_name}`. + .first_defined_span = the lang item is first defined here + .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on) + .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}`. + .first_definition_local = first definition in the local crate (`{$orig_crate_name}`) + .second_definition_local = second definition in the local crate (`{$crate_name}`) + .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} + .second_definition_path = second definition in `{$crate_name}` loaded from {$path} + +passes_duplicate_lang_item_crate_depends = + duplicate lang item in crate `{$crate_name}` (which `{$dependency_of}` depends on): `{$lang_item_name}`. + .first_defined_span = the lang item is first defined here + .first_defined_crate_depends = the lang item is first defined in crate `{$orig_crate_name}` (which `{$orig_dependency_of}` depends on) + .first_defined_crate = the lang item is first defined in crate `{$orig_crate_name}`. + .first_definition_local = first definition in the local crate (`{$orig_crate_name}`) + .second_definition_local = second definition in the local crate (`{$crate_name}`) + .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} + .second_definition_path = second definition in `{$crate_name}` loaded from {$path} + +passes_incorrect_target = + `{$name}` language item must be applied to a {$kind} with {$at_least -> + [true] at least {$num} + *[false] {$num} + } generic {$num -> + [one] argument + *[other] arguments + } + .label = this {$kind} has {$actual_num} generic {$actual_num -> + [one] argument + *[other] arguments + } + +passes_useless_assignment = + useless assignment of {$is_field_assign -> + [true] field + *[false] variable + } of type `{$ty}` to itself + +passes_only_has_effect_on = + `#[{$attr_name}]` only has an effect on {$target_name -> + [function] functions + [module] modules + [implementation_block] implementation blocks + *[unspecified] (unspecified--this is a compiler bug) + } + +passes_object_lifetime_err = + {$repr} + +passes_unrecognized_repr_hint = + unrecognized representation hint + .help = valid reprs are `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` + +passes_attr_application_enum = + attribute should be applied to an enum + .label = not an enum + +passes_attr_application_struct = + attribute should be applied to a struct + .label = not a struct + +passes_attr_application_struct_union = + attribute should be applied to a struct or union + .label = not a struct or union + +passes_attr_application_struct_enum_union = + attribute should be applied to a struct, enum, or union + .label = not a struct, enum, or union + +passes_attr_application_struct_enum_function_union = + attribute should be applied to a struct, enum, function, or union + .label = not a struct, enum, function, or union + +passes_transparent_incompatible = + transparent {$target} cannot have other repr hints + +passes_deprecated_attribute = + deprecated attribute must be paired with either stable or unstable attribute + +passes_useless_stability = + this stability annotation is useless + .label = useless stability annotation + .item = the stability attribute annotates this item + +passes_invalid_stability = + invalid stability version found + .label = invalid stability version + .item = the stability attribute annotates this item + +passes_cannot_stabilize_deprecated = + an API can't be stabilized after it is deprecated + .label = invalid version + .item = the stability attribute annotates this item + +passes_invalid_deprecation_version = + invalid deprecation version found + .label = invalid deprecation version + .item = the stability attribute annotates this item + +passes_missing_stability_attr = + {$descr} has missing stability attribute + +passes_missing_const_stab_attr = + {$descr} has missing const stability attribute + +passes_trait_impl_const_stable = + trait implementations cannot be const stable yet + .note = see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information + +passes_feature_only_on_nightly = + `#![feature]` may not be used on the {$release_channel} release channel + +passes_unknown_feature = + unknown feature `{$feature}` + +passes_implied_feature_not_exist = + feature `{$implied_by}` implying `{$feature}` does not exist + +passes_duplicate_feature_err = + the feature `{$feature}` has already been declared + +passes_missing_const_err = + attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + .help = make the function or method const + .label = attribute specified here diff --git a/compiler/rustc_error_messages/locales/en-US/query_system.ftl b/compiler/rustc_error_messages/locales/en-US/query_system.ftl index b914ba52a73..870e824039c 100644 --- a/compiler/rustc_error_messages/locales/en-US/query_system.ftl +++ b/compiler/rustc_error_messages/locales/en-US/query_system.ftl @@ -12,6 +12,8 @@ query_system_cycle_usage = cycle used when {$usage} query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again +query_system_cycle_stack_middle = ...which requires {$desc}... + query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle query_system_cycle_recursive_ty_alias = type aliases cannot be recursive diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index a2d507328b3..a6024044ad8 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -35,6 +35,7 @@ pub use unic_langid::{langid, LanguageIdentifier}; // Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module. fluent_messages! { + // tidy-alphabetical-start ast_lowering => "../locales/en-US/ast_lowering.ftl", ast_passes => "../locales/en-US/ast_passes.ftl", attr => "../locales/en-US/attr.ftl", @@ -64,6 +65,7 @@ fluent_messages! { symbol_mangling => "../locales/en-US/symbol_mangling.ftl", trait_selection => "../locales/en-US/trait_selection.ftl", ty_utils => "../locales/en-US/ty_utils.ftl", + // tidy-alphabetical-end } pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES}; @@ -277,6 +279,18 @@ pub enum SubdiagnosticMessage { /// Non-translatable diagnostic message. // FIXME(davidtwco): can a `Cow<'static, str>` be used here? Str(String), + /// Translatable message which has already been translated eagerly. + /// + /// Some diagnostics have repeated subdiagnostics where the same interpolated variables would + /// be instantiated multiple times with different values. As translation normally happens + /// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run, + /// the setting of diagnostic arguments in the derived code will overwrite previous variable + /// values and only the final value will be set when translation occurs - resulting in + /// incorrect diagnostics. Eager translation results in translation for a subdiagnostic + /// happening immediately after the subdiagnostic derive's logic has been run. This variant + /// stores messages which have been translated eagerly. + // FIXME(#100717): can a `Cow<'static, str>` be used here? + Eager(String), /// Identifier of a Fluent message. Instances of this variant are generated by the /// `Subdiagnostic` derive. FluentIdentifier(FluentId), @@ -304,8 +318,20 @@ impl<S: Into<String>> From<S> for SubdiagnosticMessage { #[rustc_diagnostic_item = "DiagnosticMessage"] pub enum DiagnosticMessage { /// Non-translatable diagnostic message. - // FIXME(davidtwco): can a `Cow<'static, str>` be used here? + // FIXME(#100717): can a `Cow<'static, str>` be used here? Str(String), + /// Translatable message which has already been translated eagerly. + /// + /// Some diagnostics have repeated subdiagnostics where the same interpolated variables would + /// be instantiated multiple times with different values. As translation normally happens + /// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run, + /// the setting of diagnostic arguments in the derived code will overwrite previous variable + /// values and only the final value will be set when translation occurs - resulting in + /// incorrect diagnostics. Eager translation results in translation for a subdiagnostic + /// happening immediately after the subdiagnostic derive's logic has been run. This variant + /// stores messages which have been translated eagerly. + // FIXME(#100717): can a `Cow<'static, str>` be used here? + Eager(String), /// Identifier for a Fluent message (with optional attribute) corresponding to the diagnostic /// message. /// @@ -324,6 +350,7 @@ impl DiagnosticMessage { pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self { let attr = match sub { SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s), + SubdiagnosticMessage::Eager(s) => return DiagnosticMessage::Eager(s), SubdiagnosticMessage::FluentIdentifier(id) => { return DiagnosticMessage::FluentIdentifier(id, None); } @@ -332,6 +359,7 @@ impl DiagnosticMessage { match self { DiagnosticMessage::Str(s) => DiagnosticMessage::Str(s.clone()), + DiagnosticMessage::Eager(s) => DiagnosticMessage::Eager(s.clone()), DiagnosticMessage::FluentIdentifier(id, _) => { DiagnosticMessage::FluentIdentifier(id.clone(), Some(attr)) } @@ -367,6 +395,7 @@ impl Into<SubdiagnosticMessage> for DiagnosticMessage { fn into(self) -> SubdiagnosticMessage { match self { DiagnosticMessage::Str(s) => SubdiagnosticMessage::Str(s), + DiagnosticMessage::Eager(s) => SubdiagnosticMessage::Eager(s), DiagnosticMessage::FluentIdentifier(id, None) => { SubdiagnosticMessage::FluentIdentifier(id) } diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index b32fc3c719b..f14b8ee3254 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -7,7 +7,7 @@ use crate::emitter::FileWithAnnotatedLines; use crate::snippet::Line; -use crate::translation::Translate; +use crate::translation::{to_fluent_args, Translate}; use crate::{ CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle, LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic, @@ -46,7 +46,7 @@ impl Translate for AnnotateSnippetEmitterWriter { impl Emitter for AnnotateSnippetEmitterWriter { /// The entry point for the diagnostics generation fn emit_diagnostic(&mut self, diag: &Diagnostic) { - let fluent_args = self.to_fluent_args(diag.args()); + let fluent_args = to_fluent_args(diag.args()); let mut children = diag.children.clone(); let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args); diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 31e410aaaf0..3e0840caaa6 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -27,7 +27,11 @@ pub struct SuggestionsDisabled; /// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of /// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of /// diagnostic emission. -pub type DiagnosticArg<'source> = (Cow<'source, str>, DiagnosticArgValue<'source>); +pub type DiagnosticArg<'iter, 'source> = + (&'iter DiagnosticArgName<'source>, &'iter DiagnosticArgValue<'source>); + +/// Name of a diagnostic argument. +pub type DiagnosticArgName<'source> = Cow<'source, str>; /// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted /// to a `FluentValue` by the emitter to be used in diagnostic translation. @@ -199,9 +203,20 @@ impl IntoDiagnosticArg for ast::token::TokenKind { /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. #[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")] #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "AddToDiagnostic")] -pub trait AddToDiagnostic { +pub trait AddToDiagnostic +where + Self: Sized, +{ /// Add a subdiagnostic to an existing diagnostic. - fn add_to_diagnostic(self, diag: &mut Diagnostic); + fn add_to_diagnostic(self, diag: &mut Diagnostic) { + self.add_to_diagnostic_with(diag, |_, m| m); + } + + /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used + /// (to optionally perform eager translation). + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage; } /// Trait implemented by lint types. This should not be implemented manually. Instead, use @@ -229,7 +244,7 @@ pub struct Diagnostic { pub span: MultiSpan, pub children: Vec<SubDiagnostic>, pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, - args: Vec<DiagnosticArg<'static>>, + args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>, /// This is not used for highlighting or rendering any error message. Rather, it can be used /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of @@ -321,7 +336,7 @@ impl Diagnostic { span: MultiSpan::new(), children: vec![], suggestions: Ok(vec![]), - args: vec![], + args: Default::default(), sort_span: DUMMY_SP, is_lint: false, } @@ -917,13 +932,30 @@ impl Diagnostic { self } - /// Add a subdiagnostic from a type that implements `Subdiagnostic` - see - /// [rustc_macros::Subdiagnostic]. + /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see + /// [rustc_macros::Subdiagnostic]). pub fn subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self { subdiagnostic.add_to_diagnostic(self); self } + /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see + /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages + /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of + /// interpolated variables). + pub fn eager_subdiagnostic( + &mut self, + handler: &crate::Handler, + subdiagnostic: impl AddToDiagnostic, + ) -> &mut Self { + subdiagnostic.add_to_diagnostic_with(self, |diag, msg| { + let args = diag.args(); + let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); + handler.eagerly_translate(msg, args) + }); + self + } + pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self { self.span = sp.into(); if let Some(span) = self.span.primary_span() { @@ -956,8 +988,11 @@ impl Diagnostic { self } - pub fn args(&self) -> &[DiagnosticArg<'static>] { - &self.args + // Exact iteration order of diagnostic arguments shouldn't make a difference to output because + // they're only used in interpolation. + #[allow(rustc::potential_query_instability)] + pub fn args<'a>(&'a self) -> impl Iterator<Item = DiagnosticArg<'a, 'static>> { + self.args.iter() } pub fn set_arg( @@ -965,7 +1000,7 @@ impl Diagnostic { name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg, ) -> &mut Self { - self.args.push((name.into(), arg.into_diagnostic_arg())); + self.args.insert(name.into(), arg.into_diagnostic_arg()); self } @@ -976,7 +1011,7 @@ impl Diagnostic { /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by /// combining it with the primary message of the diagnostic (if translatable, otherwise it just /// passes the user's string along). - fn subdiagnostic_message_to_diagnostic_message( + pub(crate) fn subdiagnostic_message_to_diagnostic_message( &self, attr: impl Into<SubdiagnosticMessage>, ) -> DiagnosticMessage { diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index bbe6435be59..9b41234dcfb 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -255,6 +255,56 @@ impl EmissionGuarantee for () { } } +/// Marker type which enables implementation of `create_note` and `emit_note` functions for +/// note-without-error struct diagnostics. +#[derive(Copy, Clone)] +pub struct Noted; + +impl<'a> DiagnosticBuilder<'a, Noted> { + /// Convenience function for internal use, clients should use one of the + /// `struct_*` methods on [`Handler`]. + pub(crate) fn new_note(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self { + let diagnostic = Diagnostic::new_with_code(Level::Note, None, message); + Self::new_diagnostic_note(handler, diagnostic) + } + + /// Creates a new `DiagnosticBuilder` with an already constructed + /// diagnostic. + pub(crate) fn new_diagnostic_note(handler: &'a Handler, diagnostic: Diagnostic) -> Self { + debug!("Created new diagnostic"); + Self { + inner: DiagnosticBuilderInner { + state: DiagnosticBuilderState::Emittable(handler), + diagnostic: Box::new(diagnostic), + }, + _marker: PhantomData, + } + } +} + +impl EmissionGuarantee for Noted { + fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self { + match db.inner.state { + // First `.emit()` call, the `&Handler` is still available. + DiagnosticBuilderState::Emittable(handler) => { + db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; + handler.emit_diagnostic(&mut db.inner.diagnostic); + } + // `.emit()` was previously called, disallowed from repeating it. + DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} + } + + Noted + } + + fn make_diagnostic_builder( + handler: &Handler, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, Self> { + DiagnosticBuilder::new_note(handler, msg) + } +} + impl<'a> DiagnosticBuilder<'a, !> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 66fbb8f1213..cd6413bc3ec 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -14,7 +14,7 @@ use rustc_span::{FileLines, SourceFile, Span}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::styled_buffer::StyledBuffer; -use crate::translation::Translate; +use crate::translation::{to_fluent_args, Translate}; use crate::{ CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle, @@ -535,7 +535,7 @@ impl Emitter for EmitterWriter { } fn emit_diagnostic(&mut self, diag: &Diagnostic) { - let fluent_args = self.to_fluent_args(diag.args()); + let fluent_args = to_fluent_args(diag.args()); let mut children = diag.children.clone(); let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args); diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 1680c6accd7..4cc7be47fc2 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -13,7 +13,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap}; use crate::emitter::{Emitter, HumanReadableErrorType}; use crate::registry::Registry; -use crate::translation::Translate; +use crate::translation::{to_fluent_args, Translate}; use crate::DiagnosticId; use crate::{ CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic, @@ -312,7 +312,7 @@ struct UnusedExterns<'a, 'b, 'c> { impl Diagnostic { fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { - let args = je.to_fluent_args(diag.args()); + let args = to_fluent_args(diag.args()); let sugg = diag.suggestions.iter().flatten().map(|sugg| { let translated_message = je.translate_message(&sugg.msg, &args); Diagnostic { diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 2f6686f8196..b16c54e0aac 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -374,7 +374,7 @@ pub use diagnostic::{ AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgFromDisplay, DiagnosticArgValue, DiagnosticId, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, }; -pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee}; +pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted}; use std::backtrace::Backtrace; /// A handler deals with errors and other compiler output. @@ -598,6 +598,17 @@ impl Handler { } } + /// Translate `message` eagerly with `args`. + pub fn eagerly_translate<'a>( + &self, + message: DiagnosticMessage, + args: impl Iterator<Item = DiagnosticArg<'a, 'static>>, + ) -> SubdiagnosticMessage { + let inner = self.inner.borrow(); + let args = crate::translation::to_fluent_args(args); + SubdiagnosticMessage::Eager(inner.emitter.translate_message(&message, &args).to_string()) + } + // This is here to not allow mutation of flags; // as of this writing it's only used in tests in librustc_middle. pub fn can_emit_warnings(&self) -> bool { diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 4f407badb3f..a7737b467b7 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -4,6 +4,27 @@ use rustc_data_structures::sync::Lrc; use rustc_error_messages::FluentArgs; use std::borrow::Cow; +/// Convert diagnostic arguments (a rustc internal type that exists to implement +/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. +/// +/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then +/// passed around as a reference thereafter. +pub fn to_fluent_args<'iter, 'arg: 'iter>( + iter: impl Iterator<Item = DiagnosticArg<'iter, 'arg>>, +) -> FluentArgs<'arg> { + let mut args = if let Some(size) = iter.size_hint().1 { + FluentArgs::with_capacity(size) + } else { + FluentArgs::new() + }; + + for (k, v) in iter { + args.set(k.clone(), v.clone()); + } + + args +} + pub trait Translate { /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no /// language was requested by the user then this will be `None` and `fallback_fluent_bundle` @@ -15,15 +36,6 @@ pub trait Translate { /// unavailable for the requested locale. fn fallback_fluent_bundle(&self) -> &FluentBundle; - /// Convert diagnostic arguments (a rustc internal type that exists to implement - /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. - /// - /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then - /// passed around as a reference thereafter. - fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> { - FromIterator::from_iter(args.iter().cloned()) - } - /// Convert `DiagnosticMessage`s to a string, performing translation if necessary. fn translate_messages( &self, @@ -43,7 +55,9 @@ pub trait Translate { ) -> Cow<'_, str> { trace!(?message, ?args); let (identifier, attr) = match message { - DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg), + DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => { + return Cow::Borrowed(&msg); + } DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), }; diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 17a348ec6ba..cc2858d3f73 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -1,5 +1,8 @@ use crate::base::ExtCtxt; - +use pm::bridge::{ + server, DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, +}; +use pm::{Delimiter, Level, LineColumn}; use rustc_ast as ast; use rustc_ast::token; use rustc_ast::tokenstream::{self, Spacing::*, TokenStream}; @@ -13,11 +16,7 @@ use rustc_session::parse::ParseSess; use rustc_span::def_id::CrateNum; use rustc_span::symbol::{self, sym, Symbol}; use rustc_span::{BytePos, FileName, Pos, SourceFile, Span}; - -use pm::bridge::{ - server, DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, -}; -use pm::{Delimiter, Level, LineColumn}; +use smallvec::{smallvec, SmallVec}; use std::ops::Bound; trait FromInternal<T> { @@ -253,23 +252,57 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre } } -impl ToInternal<TokenStream> for (TokenTree<TokenStream, Span, Symbol>, &mut Rustc<'_, '_>) { - fn to_internal(self) -> TokenStream { +// We use a `SmallVec` because the output size is always one or two `TokenTree`s. +impl ToInternal<SmallVec<[tokenstream::TokenTree; 2]>> + for (TokenTree<TokenStream, Span, Symbol>, &mut Rustc<'_, '_>) +{ + fn to_internal(self) -> SmallVec<[tokenstream::TokenTree; 2]> { use rustc_ast::token::*; let (tree, rustc) = self; - let (ch, joint, span) = match tree { - TokenTree::Punct(Punct { ch, joint, span }) => (ch, joint, span), + match tree { + TokenTree::Punct(Punct { ch, joint, span }) => { + let kind = match ch { + b'=' => Eq, + b'<' => Lt, + b'>' => Gt, + b'!' => Not, + b'~' => Tilde, + b'+' => BinOp(Plus), + b'-' => BinOp(Minus), + b'*' => BinOp(Star), + b'/' => BinOp(Slash), + b'%' => BinOp(Percent), + b'^' => BinOp(Caret), + b'&' => BinOp(And), + b'|' => BinOp(Or), + b'@' => At, + b'.' => Dot, + b',' => Comma, + b';' => Semi, + b':' => Colon, + b'#' => Pound, + b'$' => Dollar, + b'?' => Question, + b'\'' => SingleQuote, + _ => unreachable!(), + }; + smallvec![if joint { + tokenstream::TokenTree::token_joint(kind, span) + } else { + tokenstream::TokenTree::token_alone(kind, span) + }] + } TokenTree::Group(Group { delimiter, stream, span: DelimSpan { open, close, .. } }) => { - return tokenstream::TokenStream::delimited( + smallvec![tokenstream::TokenTree::Delimited( tokenstream::DelimSpan { open, close }, delimiter.to_internal(), stream.unwrap_or_default(), - ); + )] } TokenTree::Ident(self::Ident { sym, is_raw, span }) => { rustc.sess().symbol_gallery.insert(sym, span); - return tokenstream::TokenStream::token_alone(Ident(sym, is_raw), span); + smallvec![tokenstream::TokenTree::token_alone(Ident(sym, is_raw), span)] } TokenTree::Literal(self::Literal { kind: self::LitKind::Integer, @@ -282,7 +315,7 @@ impl ToInternal<TokenStream> for (TokenTree<TokenStream, Span, Symbol>, &mut Rus let integer = TokenKind::lit(token::Integer, symbol, suffix); let a = tokenstream::TokenTree::token_alone(minus, span); let b = tokenstream::TokenTree::token_alone(integer, span); - return [a, b].into_iter().collect(); + smallvec![a, b] } TokenTree::Literal(self::Literal { kind: self::LitKind::Float, @@ -295,46 +328,14 @@ impl ToInternal<TokenStream> for (TokenTree<TokenStream, Span, Symbol>, &mut Rus let float = TokenKind::lit(token::Float, symbol, suffix); let a = tokenstream::TokenTree::token_alone(minus, span); let b = tokenstream::TokenTree::token_alone(float, span); - return [a, b].into_iter().collect(); + smallvec![a, b] } TokenTree::Literal(self::Literal { kind, symbol, suffix, span }) => { - return tokenstream::TokenStream::token_alone( + smallvec![tokenstream::TokenTree::token_alone( TokenKind::lit(kind.to_internal(), symbol, suffix), span, - ); + )] } - }; - - let kind = match ch { - b'=' => Eq, - b'<' => Lt, - b'>' => Gt, - b'!' => Not, - b'~' => Tilde, - b'+' => BinOp(Plus), - b'-' => BinOp(Minus), - b'*' => BinOp(Star), - b'/' => BinOp(Slash), - b'%' => BinOp(Percent), - b'^' => BinOp(Caret), - b'&' => BinOp(And), - b'|' => BinOp(Or), - b'@' => At, - b'.' => Dot, - b',' => Comma, - b';' => Semi, - b':' => Colon, - b'#' => Pound, - b'$' => Dollar, - b'?' => Question, - b'\'' => SingleQuote, - _ => unreachable!(), - }; - - if joint { - tokenstream::TokenStream::token_joint(kind, span) - } else { - tokenstream::TokenStream::token_alone(kind, span) } } } @@ -549,7 +550,7 @@ impl server::TokenStream for Rustc<'_, '_> { &mut self, tree: TokenTree<Self::TokenStream, Self::Span, Self::Symbol>, ) -> Self::TokenStream { - (tree, &mut *self).to_internal() + Self::TokenStream::new((tree, &mut *self).to_internal().into_iter().collect::<Vec<_>>()) } fn concat_trees( @@ -557,14 +558,14 @@ impl server::TokenStream for Rustc<'_, '_> { base: Option<Self::TokenStream>, trees: Vec<TokenTree<Self::TokenStream, Self::Span, Self::Symbol>>, ) -> Self::TokenStream { - let mut builder = tokenstream::TokenStreamBuilder::new(); - if let Some(base) = base { - builder.push(base); - } + let mut stream = + if let Some(base) = base { base } else { tokenstream::TokenStream::default() }; for tree in trees { - builder.push((tree, &mut *self).to_internal()); + for tt in (tree, &mut *self).to_internal() { + stream.push_tree(tt); + } } - builder.build() + stream } fn concat_streams( @@ -572,14 +573,12 @@ impl server::TokenStream for Rustc<'_, '_> { base: Option<Self::TokenStream>, streams: Vec<Self::TokenStream>, ) -> Self::TokenStream { - let mut builder = tokenstream::TokenStreamBuilder::new(); - if let Some(base) = base { - builder.push(base); + let mut stream = + if let Some(base) = base { base } else { tokenstream::TokenStream::default() }; + for s in streams { + stream.push_stream(s); } - for stream in streams { - builder.push(stream); - } - builder.build() + stream } fn into_trees( @@ -705,6 +704,7 @@ impl server::Span for Rustc<'_, '_> { fn source_text(&mut self, span: Self::Span) -> Option<String> { self.sess().source_map().span_to_snippet(span).ok() } + /// Saves the provided span into the metadata of /// *the crate we are currently compiling*, which must /// be a proc-macro crate. This id can be passed to diff --git a/compiler/rustc_expand/src/tokenstream/tests.rs b/compiler/rustc_expand/src/tokenstream/tests.rs index eed69681011..91c4dd732e3 100644 --- a/compiler/rustc_expand/src/tokenstream/tests.rs +++ b/compiler/rustc_expand/src/tokenstream/tests.rs @@ -1,7 +1,7 @@ use crate::tests::string_to_stream; use rustc_ast::token; -use rustc_ast::tokenstream::{TokenStream, TokenStreamBuilder}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_span::create_default_session_globals_then; use rustc_span::{BytePos, Span, Symbol}; @@ -19,10 +19,9 @@ fn test_concat() { let test_res = string_to_ts("foo::bar::baz"); let test_fst = string_to_ts("foo::bar"); let test_snd = string_to_ts("::baz"); - let mut builder = TokenStreamBuilder::new(); - builder.push(test_fst); - builder.push(test_snd); - let eq_res = builder.build(); + let mut eq_res = TokenStream::default(); + eq_res.push_stream(test_fst); + eq_res.push_stream(test_snd); assert_eq!(test_res.trees().count(), 5); assert_eq!(eq_res.trees().count(), 5); assert_eq!(test_res.eq_unspanned(&eq_res), true); @@ -99,11 +98,10 @@ fn test_is_empty() { #[test] fn test_dotdotdot() { create_default_session_globals_then(|| { - let mut builder = TokenStreamBuilder::new(); - builder.push(TokenStream::token_joint(token::Dot, sp(0, 1))); - builder.push(TokenStream::token_joint(token::Dot, sp(1, 2))); - builder.push(TokenStream::token_alone(token::Dot, sp(2, 3))); - let stream = builder.build(); + let mut stream = TokenStream::default(); + stream.push_tree(TokenTree::token_joint(token::Dot, sp(0, 1))); + stream.push_tree(TokenTree::token_joint(token::Dot, sp(1, 2))); + stream.push_tree(TokenTree::token_alone(token::Dot, sp(2, 3))); assert!(stream.eq_unspanned(&string_to_ts("..."))); assert_eq!(stream.trees().count(), 1); }) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 48c40eae662..5ea433e6b3d 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -412,8 +412,6 @@ declare_features! ( (incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None), /// Allows non-trivial generic constants which have to have wfness manually propagated to callers (incomplete, generic_const_exprs, "1.56.0", Some(76560), None), - /// Allows using `..X`, `..=X`, `...X`, and `X..` as a pattern. - (active, half_open_range_patterns, "1.41.0", Some(67264), None), /// Allows using `..=X` as a patterns in slices. (active, half_open_range_patterns_in_slices, "CURRENT_RUSTC_VERSION", Some(67264), None), /// Allows `if let` guard in match arms. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 098f9d51549..bc149e48d89 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3514,7 +3514,7 @@ impl<'hir> Node<'hir> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { use super::*; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(Block<'_>, 48); static_assert_size!(Body<'_>, 32); static_assert_size!(Expr<'_>, 64); @@ -3533,9 +3533,9 @@ mod size_asserts { static_assert_size!(Local<'_>, 64); static_assert_size!(Param<'_>, 32); static_assert_size!(Pat<'_>, 72); - static_assert_size!(PatKind<'_>, 48); static_assert_size!(Path<'_>, 40); static_assert_size!(PathSegment<'_>, 48); + static_assert_size!(PatKind<'_>, 48); static_assert_size!(QPath<'_>, 24); static_assert_size!(Res, 12); static_assert_size!(Stmt<'_>, 32); @@ -3544,4 +3544,5 @@ mod size_asserts { static_assert_size!(TraitItemKind<'_>, 48); static_assert_size!(Ty<'_>, 48); static_assert_size!(TyKind<'_>, 32); + // tidy-alphabetical-end } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 77ab27266b2..f6f25603581 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -380,7 +380,6 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { let def = tcx.adt_def(def_id); let span = tcx.def_span(def_id); def.destructor(tcx); // force the destructor to be evaluated - let _ = tcx.representability(def_id); if def.repr().simd() { check_simd(tcx, span, def_id); @@ -394,7 +393,6 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { let def = tcx.adt_def(def_id); let span = tcx.def_span(def_id); def.destructor(tcx); // force the destructor to be evaluated - let _ = tcx.representability(def_id); check_transparent(tcx, span, def); check_union_fields(tcx, span, def_id); check_packed(tcx, span, def); @@ -1489,7 +1487,6 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp); - let _ = tcx.representability(def_id); check_transparent(tcx, sp, def); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0bd45bb1c91..0a8a1bec9b8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1041,6 +1041,8 @@ fn check_type_defn<'tcx, F>( ) where F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>, { + let _ = tcx.representability(item.def_id.def_id); + enter_wf_checking_ctxt(tcx, item.span, item.def_id.def_id, |wfcx| { let variants = lookup_fields(wfcx); let packed = tcx.adt_def(item.def_id).repr().packed(); diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 85b877652c6..500900d3d4a 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,6 +1,7 @@ use hir::GenericParamKind; use rustc_errors::{ - fluent, AddToDiagnostic, Applicability, DiagnosticMessage, DiagnosticStyledString, MultiSpan, + fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString, + MultiSpan, SubdiagnosticMessage, }; use rustc_hir as hir; use rustc_hir::{FnRetTy, Ty}; @@ -229,7 +230,10 @@ pub enum RegionOriginNote<'a> { } impl AddToDiagnostic for RegionOriginNote<'_> { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { let mut label_or_note = |span, msg: DiagnosticMessage| { let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); @@ -290,7 +294,10 @@ pub enum LifetimeMismatchLabels { } impl AddToDiagnostic for LifetimeMismatchLabels { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self { LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { diag.span_label(param_span, fluent::infer::declared_different); @@ -340,7 +347,10 @@ pub struct AddLifetimeParamsSuggestion<'a> { } impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { let mut mk_suggestion = || { let ( hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, @@ -439,7 +449,10 @@ pub struct IntroducesStaticBecauseUnmetLifetimeReq { } impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { - fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<F>(mut self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { self.unmet_requirements .push_span_label(self.binding_span, fluent::infer::msl_introduces_static); diag.span_note(self.unmet_requirements, fluent::infer::msl_unmet_req); @@ -451,7 +464,10 @@ pub struct ImplNote { } impl AddToDiagnostic for ImplNote { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self.impl_span { Some(span) => diag.span_note(span, fluent::infer::msl_impl_note), None => diag.note(fluent::infer::msl_impl_note), @@ -466,7 +482,10 @@ pub enum TraitSubdiag { // FIXME(#100717) used in `Vec<TraitSubdiag>` so requires eager translation/list support impl AddToDiagnostic for TraitSubdiag { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self { TraitSubdiag::Note { span } => { diag.span_note(span, "this has an implicit `'static` lifetime requirement"); diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 7f54918f736..201a3c7100c 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -1,5 +1,7 @@ use crate::infer::error_reporting::nice_region_error::find_anon_type; -use rustc_errors::{self, fluent, AddToDiagnostic, IntoDiagnosticArg}; +use rustc_errors::{ + self, fluent, AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessage, +}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{symbol::kw, Span}; @@ -159,7 +161,10 @@ impl RegionExplanation<'_> { } impl AddToDiagnostic for RegionExplanation<'_> { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { if let Some(span) = self.desc.span { diag.span_note(span, fluent::infer::region_explanation); } else { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 98eeaad976f..d64cdcdbaa9 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -540,7 +540,7 @@ fn test_codegen_options_tracking_hash() { } // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. - // This list is in alphabetical order. + // tidy-alphabetical-start untracked!(ar, String::from("abc")); untracked!(codegen_units, Some(42)); untracked!(default_linker_libraries, true); @@ -556,6 +556,7 @@ fn test_codegen_options_tracking_hash() { untracked!(rpath, true); untracked!(save_temps, true); untracked!(strip, Strip::Debuginfo); + // tidy-alphabetical-end macro_rules! tracked { ($name: ident, $non_default_value: expr) => { @@ -567,7 +568,7 @@ fn test_codegen_options_tracking_hash() { } // Make sure that changing a [TRACKED] option changes the hash. - // This list is in alphabetical order. + // tidy-alphabetical-start tracked!(code_model, Some(CodeModel::Large)); tracked!(control_flow_guard, CFGuard::Checks); tracked!(debug_assertions, Some(true)); @@ -577,8 +578,8 @@ fn test_codegen_options_tracking_hash() { tracked!(force_unwind_tables, Some(true)); tracked!(inline_threshold, Some(0xf007ba11)); tracked!(instrument_coverage, Some(InstrumentCoverage::All)); - tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); tracked!(link_dead_code, Some(true)); + tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); tracked!(llvm_args, vec![String::from("1"), String::from("2")]); tracked!(lto, LtoCli::Fat); tracked!(metadata, vec![String::from("A"), String::from("B")]); @@ -599,6 +600,7 @@ fn test_codegen_options_tracking_hash() { tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0)); tracked!(target_cpu, Some(String::from("abc"))); tracked!(target_feature, String::from("all the features, all of them")); + // tidy-alphabetical-end } #[test] @@ -619,12 +621,13 @@ fn test_top_level_options_tracked_no_crate() { } // Make sure that changing a [TRACKED_NO_CRATE_HASH] option leaves the crate hash unchanged but changes the incremental hash. - // This list is in alphabetical order. - tracked!(remap_path_prefix, vec![("/home/bors/rust".into(), "src".into())]); + // tidy-alphabetical-start tracked!( real_rust_source_base_dir, Some("/home/bors/rust/.rustup/toolchains/nightly/lib/rustlib/src/rust".into()) ); + tracked!(remap_path_prefix, vec![("/home/bors/rust".into(), "src".into())]); + // tidy-alphabetical-end } #[test] @@ -641,7 +644,7 @@ fn test_unstable_options_tracking_hash() { } // Make sure that changing an [UNTRACKED] option leaves the hash unchanged. - // This list is in alphabetical order. + // tidy-alphabetical-start untracked!(assert_incr_state, Some(String::from("loaded"))); untracked!(deduplicate_diagnostics, false); untracked!(dep_tasks, true); @@ -678,12 +681,12 @@ fn test_unstable_options_tracking_hash() { untracked!(perf_stats, true); // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); - untracked!(profile_closures, true); untracked!(print_llvm_passes, true); untracked!(print_mono_items, Some(String::from("abc"))); untracked!(print_type_sizes, true); untracked!(proc_macro_backtrace, true); untracked!(proc_macro_execution_strategy, ProcMacroExecutionStrategy::CrossThread); + untracked!(profile_closures, true); untracked!(query_dep_graph, true); untracked!(save_analysis, true); untracked!(self_profile, SwitchWithOptPath::Enabled(None)); @@ -701,6 +704,7 @@ fn test_unstable_options_tracking_hash() { untracked!(unstable_options, true); untracked!(validate_mir, true); untracked!(verbose, true); + // tidy-alphabetical-end macro_rules! tracked { ($name: ident, $non_default_value: expr) => { @@ -712,7 +716,7 @@ fn test_unstable_options_tracking_hash() { } // Make sure that changing a [TRACKED] option changes the hash. - // This list is in alphabetical order. + // tidy-alphabetical-start tracked!(allow_features, Some(vec![String::from("lang_items")])); tracked!(always_encode_mir, true); tracked!(asm_comments, true); @@ -733,10 +737,10 @@ fn test_unstable_options_tracking_hash() { tracked!(debug_macros, true); tracked!(dep_info_omit_d_target, true); tracked!(drop_tracking, true); - tracked!(export_executable_symbols, true); tracked!(dual_proc_macros, true); tracked!(dwarf_version, Some(5)); tracked!(emit_thin_lto, false); + tracked!(export_executable_symbols, true); tracked!(fewer_names, Some(true)); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99))); @@ -759,8 +763,8 @@ fn test_unstable_options_tracking_hash() { tracked!(mutable_noalias, Some(true)); tracked!(no_generate_arange_section, true); tracked!(no_link, true); - tracked!(no_unique_section_names, true); tracked!(no_profiler_runtime, true); + tracked!(no_unique_section_names, true); tracked!(oom, OomStrategy::Panic); tracked!(osx_rpath_install_name, true); tracked!(packed_bundled_libs, true); @@ -773,8 +777,8 @@ fn test_unstable_options_tracking_hash() { tracked!(print_fuel, Some("abc".to_string())); tracked!(profile, true); tracked!(profile_emit, Some(PathBuf::from("abc"))); - tracked!(profiler_runtime, "abc".to_string()); tracked!(profile_sample_use, Some(PathBuf::from("abc"))); + tracked!(profiler_runtime, "abc".to_string()); tracked!(relax_elf_relocations, Some(true)); tracked!(relro_level, Some(RelroLevel::Full)); tracked!(remap_cwd_prefix, Some(PathBuf::from("abc"))); @@ -803,6 +807,7 @@ fn test_unstable_options_tracking_hash() { tracked!(verify_llvm_ir, true); tracked!(virtual_function_elimination, true); tracked!(wasi_exec_model, Some(WasiExecModel::Reactor)); + // tidy-alphabetical-end macro_rules! tracked_no_crate_hash { ($name: ident, $non_default_value: expr) => { diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index 880f3fbd00e..97d012fb611 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -1,4 +1,7 @@ -use rustc_errors::{fluent, AddToDiagnostic, ErrorGuaranteed, Handler, IntoDiagnostic}; +use rustc_errors::{ + fluent, AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic, + SubdiagnosticMessage, +}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::lint::Level; use rustc_span::{Span, Symbol}; @@ -23,7 +26,10 @@ pub enum OverruledAttributeSub { } impl AddToDiagnostic for OverruledAttributeSub { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { match self { OverruledAttributeSub::DefaultSource { id } => { diag.note(fluent::lint::default_source); @@ -88,7 +94,10 @@ pub struct RequestedLevel { } impl AddToDiagnostic for RequestedLevel { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { diag.note(fluent::lint::requested_level); diag.set_arg( "level", diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 83040a652b1..8cf307df5a5 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -10,27 +10,31 @@ use synstructure::Structure; /// The central struct for constructing the `into_diagnostic` method from an annotated struct. pub(crate) struct DiagnosticDerive<'a> { structure: Structure<'a>, - handler: syn::Ident, builder: DiagnosticDeriveBuilder, } impl<'a> DiagnosticDerive<'a> { pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self { Self { - builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::Diagnostic }, - handler, + builder: DiagnosticDeriveBuilder { + diag, + kind: DiagnosticDeriveKind::Diagnostic { handler }, + }, structure, } } pub(crate) fn into_tokens(self) -> TokenStream { - let DiagnosticDerive { mut structure, handler, mut builder } = self; + let DiagnosticDerive { mut structure, mut builder } = self; let implementation = builder.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(&variant); let body = builder.body(&variant); let diag = &builder.parent.diag; + let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else { + unreachable!() + }; let init = match builder.slug.value_ref() { None => { span_err(builder.span, "diagnostic slug not specified") @@ -48,14 +52,17 @@ impl<'a> DiagnosticDerive<'a> { } }; + let formatting_init = &builder.formatting_init; quote! { #init + #formatting_init #preamble #body #diag } }); + let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() }; structure.gen_impl(quote! { gen impl<'__diagnostic_handler_sess, G> rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G> @@ -96,17 +103,18 @@ impl<'a> LintDiagnosticDerive<'a> { let body = builder.body(&variant); let diag = &builder.parent.diag; - + let formatting_init = &builder.formatting_init; quote! { #preamble + #formatting_init #body #diag } }); let msg = builder.each_variant(&mut structure, |mut builder, variant| { - // HACK(wafflelapkin): initialize slug (???) - let _preamble = builder.preamble(&variant); + // Collect the slug by generating the preamble. + let _ = builder.preamble(&variant); match builder.slug.value_ref() { None => { @@ -125,7 +133,10 @@ impl<'a> LintDiagnosticDerive<'a> { let diag = &builder.diag; structure.gen_impl(quote! { gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self { - fn decorate_lint<'__b>(self, #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> { + fn decorate_lint<'__b>( + self, + #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> + ) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> { use rustc_errors::IntoDiagnosticArg; #implementation } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 9e88dc7a913..dcbe89251cb 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -5,9 +5,9 @@ use crate::diagnostics::error::{ DiagnosticDeriveError, }; use crate::diagnostics::utils::{ - bind_style_of_field, build_field_mapping, report_error_if_not_applied_to_span, - report_type_error, should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo, - FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, + build_field_mapping, report_error_if_not_applied_to_span, report_type_error, + should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo, FieldInnerTy, FieldMap, + HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; @@ -17,9 +17,9 @@ use syn::{ use synstructure::{BindingInfo, Structure, VariantInfo}; /// What kind of diagnostic is being derived - a fatal/error/warning or a lint? -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub(crate) enum DiagnosticDeriveKind { - Diagnostic, + Diagnostic { handler: syn::Ident }, LintDiagnostic, } @@ -40,6 +40,9 @@ pub(crate) struct DiagnosticDeriveVariantBuilder<'parent> { /// The parent builder for the entire type. pub parent: &'parent DiagnosticDeriveBuilder, + /// Initialization of format strings for code suggestions. + pub formatting_init: TokenStream, + /// Span of the struct or the enum variant. pub span: proc_macro::Span, @@ -88,19 +91,7 @@ impl DiagnosticDeriveBuilder { } } - for variant in structure.variants_mut() { - // First, change the binding style of each field based on the code that will be - // generated for the field - e.g. `set_arg` calls needs by-move bindings, whereas - // `set_primary_span` only needs by-ref. - variant.bind_with(|bi| bind_style_of_field(bi.ast()).0); - - // Then, perform a stable sort on bindings which generates code for by-ref bindings - // before code generated for by-move bindings. Any code generated for the by-ref - // bindings which creates a reference to the by-move fields will happen before the - // by-move bindings move those fields and make them inaccessible. - variant.bindings_mut().sort_by_cached_key(|bi| bind_style_of_field(bi.ast())); - } - + structure.bind_with(|_| synstructure::BindStyle::Move); let variants = structure.each_variant(|variant| { let span = match structure.ast().data { syn::Data::Struct(..) => span, @@ -112,6 +103,7 @@ impl DiagnosticDeriveBuilder { parent: &self, span, field_map: build_field_mapping(variant), + formatting_init: TokenStream::new(), slug: None, code: None, }; @@ -143,16 +135,14 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { /// Generates calls to `span_label` and similar functions based on the attributes on fields or /// calls to `set_arg` when no attributes are present. - /// - /// Expects use of `Self::each_variant` which will have sorted bindings so that by-ref bindings - /// (which may create references to by-move bindings) have their code generated first - - /// necessary as code for suggestions uses formatting machinery and the value of other fields - /// (any given field can be referenced multiple times, so must be accessed through a borrow); - /// and when passing fields to `add_subdiagnostic` or `set_arg` for Fluent, fields must be - /// accessed by-move. pub fn body<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream { let mut body = quote! {}; - for binding in variant.bindings() { + // Generate `set_arg` calls first.. + for binding in variant.bindings().iter().filter(|bi| should_generate_set_arg(bi.ast())) { + body.extend(self.generate_field_code(binding)); + } + // ..and then subdiagnostic additions. + for binding in variant.bindings().iter().filter(|bi| !should_generate_set_arg(bi.ast())) { body.extend(self.generate_field_attrs_code(binding)); } body @@ -274,24 +264,27 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { } } - fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { + fn generate_field_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { + let diag = &self.parent.diag; + let field = binding_info.ast(); let field_binding = &binding_info.binding; - if should_generate_set_arg(&field) { - let diag = &self.parent.diag; - let ident = field.ident.as_ref().unwrap(); - // strip `r#` prefix, if present - let ident = format_ident!("{}", ident); - return quote! { - #diag.set_arg( - stringify!(#ident), - #field_binding - ); - }; + let ident = field.ident.as_ref().unwrap(); + let ident = format_ident!("{}", ident); // strip `r#` prefix, if present + + quote! { + #diag.set_arg( + stringify!(#ident), + #field_binding + ); } + } + + fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { + let field = binding_info.ast(); + let field_binding = &binding_info.binding; - let needs_move = bind_style_of_field(&field).is_move(); let inner_ty = FieldInnerTy::from_type(&field.ty); field @@ -304,10 +297,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { let (binding, needs_destructure) = if needs_clone { // `primary_span` can accept a `Vec<Span>` so don't destructure that. (quote! { #field_binding.clone() }, false) - } else if needs_move { - (quote! { #field_binding }, true) } else { - (quote! { *#field_binding }, true) + (quote! { #field_binding }, true) }; let generated_code = self @@ -340,18 +331,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { let diag = &self.parent.diag; let meta = attr.parse_meta()?; - if let Meta::Path(_) = meta { - let ident = &attr.path.segments.last().unwrap().ident; - let name = ident.to_string(); - let name = name.as_str(); - match name { - "skip_arg" => { - // Don't need to do anything - by virtue of the attribute existing, the - // `set_arg` call will not be generated. - return Ok(quote! {}); - } - "primary_span" => match self.parent.kind { - DiagnosticDeriveKind::Diagnostic => { + let ident = &attr.path.segments.last().unwrap().ident; + let name = ident.to_string(); + match (&meta, name.as_str()) { + // Don't need to do anything - by virtue of the attribute existing, the + // `set_arg` call will not be generated. + (Meta::Path(_), "skip_arg") => return Ok(quote! {}), + (Meta::Path(_), "primary_span") => { + match self.parent.kind { + DiagnosticDeriveKind::Diagnostic { .. } => { report_error_if_not_applied_to_span(attr, &info)?; return Ok(quote! { @@ -363,10 +351,50 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { diag.help("the `primary_span` field attribute is not valid for lint diagnostics") }) } - }, - "subdiagnostic" => return Ok(quote! { #diag.subdiagnostic(#binding); }), - _ => {} + } + } + (Meta::Path(_), "subdiagnostic") => { + return Ok(quote! { #diag.subdiagnostic(#binding); }); + } + (Meta::NameValue(_), "subdiagnostic") => { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help("`eager` is the only supported nested attribute for `subdiagnostic`") + }) + } + (Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => { + if nested.len() != 1 { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help( + "`eager` is the only supported nested attribute for `subdiagnostic`", + ) + }) + } + + let handler = match &self.parent.kind { + DiagnosticDeriveKind::Diagnostic { handler } => handler, + DiagnosticDeriveKind::LintDiagnostic => { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help("eager subdiagnostics are not supported on lints") + }) + } + }; + + let nested_attr = nested.first().expect("pop failed for single element list"); + match nested_attr { + NestedMeta::Meta(meta @ Meta::Path(_)) + if meta.path().segments.last().unwrap().ident.to_string().as_str() + == "eager" => + { + return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); }); + } + _ => { + throw_invalid_nested_attr!(attr, nested_attr, |diag| { + diag.help("`eager` is the only supported nested attribute for `subdiagnostic`") + }) + } + } } + _ => (), } let (subdiag, slug) = self.parse_subdiag_attribute(attr)?; @@ -389,7 +417,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { SubdiagnosticKind::Suggestion { suggestion_kind, applicability: static_applicability, - code, + code_field, + code_init, } => { let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?; @@ -402,11 +431,12 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); let style = suggestion_kind.to_suggestion_style(); + self.formatting_init.extend(code_init); Ok(quote! { #diag.span_suggestion_with_style( #span_field, rustc_errors::fluent::#slug, - #code, + #code_field, #applicability, #style ); @@ -451,7 +481,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`. ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => { let binding = &info.binding.binding; - Ok((quote!(*#binding), None)) + Ok((quote!(#binding), None)) } // If `ty` is `(Span, Applicability)` then return tokens accessing those. Type::Tuple(tup) => { diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 4166816b5e3..f98cc66e9e9 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -9,7 +9,7 @@ use diagnostic::{DiagnosticDerive, LintDiagnosticDerive}; pub(crate) use fluent::fluent_messages; use proc_macro2::TokenStream; use quote::format_ident; -use subdiagnostic::SubdiagnosticDerive; +use subdiagnostic::SubdiagnosticDeriveBuilder; use synstructure::Structure; /// Implements `#[derive(Diagnostic)]`, which allows for errors to be specified as a struct, @@ -155,5 +155,5 @@ pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { /// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident }); /// ``` pub fn session_subdiagnostic_derive(s: Structure<'_>) -> TokenStream { - SubdiagnosticDerive::new(s).into_tokens() + SubdiagnosticDeriveBuilder::new().into_tokens(s) } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 9a2588513f0..3d4c3ab9fd7 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -5,7 +5,7 @@ use crate::diagnostics::error::{ DiagnosticDeriveError, }; use crate::diagnostics::utils::{ - build_field_mapping, report_error_if_not_applied_to_applicability, + build_field_mapping, new_code_ident, report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; @@ -15,19 +15,19 @@ use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta use synstructure::{BindingInfo, Structure, VariantInfo}; /// The central struct for constructing the `add_to_diagnostic` method from an annotated struct. -pub(crate) struct SubdiagnosticDerive<'a> { - structure: Structure<'a>, +pub(crate) struct SubdiagnosticDeriveBuilder { diag: syn::Ident, + f: syn::Ident, } -impl<'a> SubdiagnosticDerive<'a> { - pub(crate) fn new(structure: Structure<'a>) -> Self { +impl SubdiagnosticDeriveBuilder { + pub(crate) fn new() -> Self { let diag = format_ident!("diag"); - Self { structure, diag } + let f = format_ident!("f"); + Self { diag, f } } - pub(crate) fn into_tokens(self) -> TokenStream { - let SubdiagnosticDerive { mut structure, diag } = self; + pub(crate) fn into_tokens<'a>(self, mut structure: Structure<'a>) -> TokenStream { let implementation = { let ast = structure.ast(); let span = ast.span().unwrap(); @@ -53,10 +53,11 @@ impl<'a> SubdiagnosticDerive<'a> { structure.bind_with(|_| synstructure::BindStyle::Move); let variants_ = structure.each_variant(|variant| { - let mut builder = SubdiagnosticDeriveBuilder { - diag: &diag, + let mut builder = SubdiagnosticDeriveVariantBuilder { + parent: &self, variant, span, + formatting_init: TokenStream::new(), fields: build_field_mapping(variant), span_field: None, applicability: None, @@ -72,9 +73,17 @@ impl<'a> SubdiagnosticDerive<'a> { } }; + let diag = &self.diag; + let f = &self.f; let ret = structure.gen_impl(quote! { gen impl rustc_errors::AddToDiagnostic for @Self { - fn add_to_diagnostic(self, #diag: &mut rustc_errors::Diagnostic) { + fn add_to_diagnostic_with<__F>(self, #diag: &mut rustc_errors::Diagnostic, #f: __F) + where + __F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage + ) -> rustc_errors::SubdiagnosticMessage, + { use rustc_errors::{Applicability, IntoDiagnosticArg}; #implementation } @@ -88,15 +97,18 @@ impl<'a> SubdiagnosticDerive<'a> { /// for the final generated method. This is a separate struct to `SubdiagnosticDerive` /// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a /// double mut borrow later on. -struct SubdiagnosticDeriveBuilder<'a> { +struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> { /// The identifier to use for the generated `DiagnosticBuilder` instance. - diag: &'a syn::Ident, + parent: &'parent SubdiagnosticDeriveBuilder, /// Info for the current variant (or the type if not an enum). variant: &'a VariantInfo<'a>, /// Span for the entire type. span: proc_macro::Span, + /// Initialization of format strings for code suggestions. + formatting_init: TokenStream, + /// Store a map of field name to its corresponding field. This is built on construction of the /// derive builder. fields: FieldMap, @@ -112,7 +124,7 @@ struct SubdiagnosticDeriveBuilder<'a> { has_suggestion_parts: bool, } -impl<'a> HasFieldMap for SubdiagnosticDeriveBuilder<'a> { +impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> { fn get_field_binding(&self, field: &String) -> Option<&TokenStream> { self.fields.get(field) } @@ -156,7 +168,7 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics { } } -impl<'a> SubdiagnosticDeriveBuilder<'a> { +impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> { let mut kind_slugs = vec![]; @@ -187,7 +199,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { let ast = binding.ast(); assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg"); - let diag = &self.diag; + let diag = &self.parent.diag; let ident = ast.ident.as_ref().unwrap(); // strip `r#` prefix, if present let ident = format_ident!("{}", ident); @@ -222,7 +234,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { }; let generated = self - .generate_field_code_inner(kind_stats, attr, info) + .generate_field_code_inner(kind_stats, attr, info, inner_ty.will_iterate()) .unwrap_or_else(|v| v.to_compile_error()); inner_ty.with(binding, generated) @@ -235,13 +247,18 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { kind_stats: KindsStatistics, attr: &Attribute, info: FieldInfo<'_>, + clone_suggestion_code: bool, ) -> Result<TokenStream, DiagnosticDeriveError> { let meta = attr.parse_meta()?; match meta { Meta::Path(path) => self.generate_field_code_inner_path(kind_stats, attr, info, path), - Meta::List(list @ MetaList { .. }) => { - self.generate_field_code_inner_list(kind_stats, attr, info, list) - } + Meta::List(list @ MetaList { .. }) => self.generate_field_code_inner_list( + kind_stats, + attr, + info, + list, + clone_suggestion_code, + ), _ => throw_invalid_attr!(attr, &meta), } } @@ -345,6 +362,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { attr: &Attribute, info: FieldInfo<'_>, list: MetaList, + clone_suggestion_code: bool, ) -> Result<TokenStream, DiagnosticDeriveError> { let span = attr.span().unwrap(); let ident = &list.path.segments.last().unwrap().ident; @@ -382,7 +400,8 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { match nested_name { "code" => { let formatted_str = self.build_format(&value.value(), value.span()); - code.set_once(formatted_str, span); + let code_field = new_code_ident(); + code.set_once((code_field, formatted_str), span); } _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { diag.help("`code` is the only valid nested attribute") @@ -390,14 +409,20 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { } } - let Some((code, _)) = code else { + let Some((code_field, formatted_str)) = code.value() else { span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`") .emit(); return Ok(quote! {}); }; let binding = info.binding; - Ok(quote! { suggestions.push((#binding, #code)); }) + self.formatting_init.extend(quote! { let #code_field = #formatted_str; }); + let code_field = if clone_suggestion_code { + quote! { #code_field.clone() } + } else { + quote! { #code_field } + }; + Ok(quote! { suggestions.push((#binding, #code_field)); }) } _ => throw_invalid_attr!(attr, &Meta::List(list), |diag| { let mut span_attrs = vec![]; @@ -442,13 +467,23 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { let span_field = self.span_field.value_ref(); - let diag = &self.diag; + let diag = &self.parent.diag; + let f = &self.parent.f; let mut calls = TokenStream::new(); for (kind, slug) in kind_slugs { + let message = format_ident!("__message"); + calls.extend(quote! { let #message = #f(#diag, rustc_errors::fluent::#slug.into()); }); + let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); - let message = quote! { rustc_errors::fluent::#slug }; let call = match kind { - SubdiagnosticKind::Suggestion { suggestion_kind, applicability, code } => { + SubdiagnosticKind::Suggestion { + suggestion_kind, + applicability, + code_init, + code_field, + } => { + self.formatting_init.extend(code_init); + let applicability = applicability .value() .map(|a| quote! { #a }) @@ -457,8 +492,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { if let Some(span) = span_field { let style = suggestion_kind.to_suggestion_style(); - - quote! { #diag.#name(#span, #message, #code, #applicability, #style); } + quote! { #diag.#name(#span, #message, #code_field, #applicability, #style); } } else { span_err(self.span, "suggestion without `#[primary_span]` field").emit(); quote! { unreachable!(); } @@ -499,6 +533,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { } } }; + calls.extend(call); } @@ -510,11 +545,13 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { .map(|binding| self.generate_field_set_arg(binding)) .collect(); + let formatting_init = &self.formatting_init; Ok(quote! { #init + #formatting_init #attr_args - #calls #plain_args + #calls }) } } diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 162699c2868..4fd4adc5112 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -4,16 +4,29 @@ use crate::diagnostics::error::{ use proc_macro::Span; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; -use std::cmp::Ordering; +use std::cell::RefCell; use std::collections::{BTreeSet, HashMap}; use std::fmt; use std::str::FromStr; use syn::{spanned::Spanned, Attribute, Field, Meta, Type, TypeTuple}; use syn::{MetaList, MetaNameValue, NestedMeta, Path}; -use synstructure::{BindStyle, BindingInfo, VariantInfo}; +use synstructure::{BindingInfo, VariantInfo}; use super::error::invalid_nested_attr; +thread_local! { + pub static CODE_IDENT_COUNT: RefCell<u32> = RefCell::new(0); +} + +/// Returns an ident of the form `__code_N` where `N` is incremented once with every call. +pub(crate) fn new_code_ident() -> syn::Ident { + CODE_IDENT_COUNT.with(|count| { + let ident = format_ident!("__code_{}", *count.borrow()); + *count.borrow_mut() += 1; + ident + }) +} + /// Checks whether the type name of `ty` matches `name`. /// /// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or @@ -142,6 +155,15 @@ impl<'ty> FieldInnerTy<'ty> { unreachable!(); } + /// Returns `true` if `FieldInnerTy::with` will result in iteration for this inner type (i.e. + /// that cloning might be required for values moved in the loop body). + pub(crate) fn will_iterate(&self) -> bool { + match self { + FieldInnerTy::Vec(..) => true, + FieldInnerTy::Option(..) | FieldInnerTy::None => false, + } + } + /// Returns `Option` containing inner type if there is one. pub(crate) fn inner_type(&self) -> Option<&'ty Type> { match self { @@ -434,7 +456,12 @@ pub(super) enum SubdiagnosticKind { Suggestion { suggestion_kind: SuggestionKind, applicability: SpannedOption<Applicability>, - code: TokenStream, + /// Identifier for variable used for formatted code, e.g. `___code_0`. Enables separation + /// of formatting and diagnostic emission so that `set_arg` calls can happen in-between.. + code_field: syn::Ident, + /// Initialization logic for `code_field`'s variable, e.g. + /// `let __formatted_code = /* whatever */;` + code_init: TokenStream, }, /// `#[multipart_suggestion{,_short,_hidden,_verbose}]` MultipartSuggestion { @@ -469,7 +496,8 @@ impl SubdiagnosticKind { SubdiagnosticKind::Suggestion { suggestion_kind, applicability: None, - code: TokenStream::new(), + code_field: new_code_ident(), + code_init: TokenStream::new(), } } else if let Some(suggestion_kind) = name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok()) @@ -548,9 +576,10 @@ impl SubdiagnosticKind { }; match (nested_name, &mut kind) { - ("code", SubdiagnosticKind::Suggestion { .. }) => { + ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { let formatted_str = fields.build_format(&value.value(), value.span()); - code.set_once(formatted_str, span); + let code_init = quote! { let #code_field = #formatted_str; }; + code.set_once(code_init, span); } ( "applicability", @@ -582,13 +611,13 @@ impl SubdiagnosticKind { } match kind { - SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => { - *code_field = if let Some((code, _)) = code { - code + SubdiagnosticKind::Suggestion { ref code_field, ref mut code_init, .. } => { + *code_init = if let Some(init) = code.value() { + init } else { span_err(span, "suggestion without `code = \"...\"`").emit(); - quote! { "" } - } + quote! { let #code_field: String = unreachable!(); } + }; } SubdiagnosticKind::Label | SubdiagnosticKind::Note @@ -620,65 +649,8 @@ impl quote::IdentFragment for SubdiagnosticKind { } } -/// Wrapper around `synstructure::BindStyle` which implements `Ord`. -#[derive(PartialEq, Eq)] -pub(super) struct OrderedBindStyle(pub(super) BindStyle); - -impl OrderedBindStyle { - /// Is `BindStyle::Move` or `BindStyle::MoveMut`? - pub(super) fn is_move(&self) -> bool { - matches!(self.0, BindStyle::Move | BindStyle::MoveMut) - } -} - -impl Ord for OrderedBindStyle { - fn cmp(&self, other: &Self) -> Ordering { - match (self.is_move(), other.is_move()) { - // If both `self` and `other` are the same, then ordering is equal. - (true, true) | (false, false) => Ordering::Equal, - // If `self` is not a move then it should be considered less than `other` (so that - // references are sorted first). - (false, _) => Ordering::Less, - // If `self` is a move then it must be greater than `other` (again, so that references - // are sorted first). - (true, _) => Ordering::Greater, - } - } -} - -impl PartialOrd for OrderedBindStyle { - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic /// call (like `span_label`). pub(super) fn should_generate_set_arg(field: &Field) -> bool { field.attrs.is_empty() } - -/// Returns `true` if `field` needs to have code generated in the by-move branch of the -/// generated derive rather than the by-ref branch. -pub(super) fn bind_style_of_field(field: &Field) -> OrderedBindStyle { - let generates_set_arg = should_generate_set_arg(field); - let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]); - // FIXME(davidtwco): better support for one field needing to be in the by-move and - // by-ref branches. - let is_subdiagnostic = field - .attrs - .iter() - .map(|attr| attr.path.segments.last().unwrap().ident.to_string()) - .any(|attr| attr == "subdiagnostic"); - - // `set_arg` calls take their argument by-move.. - let needs_move = generates_set_arg - // If this is a `MultiSpan` field then it needs to be moved to be used by any - // attribute.. - || is_multispan - // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is - // unlikely to be `Copy`.. - || is_subdiagnostic; - - OrderedBindStyle(if needs_move { BindStyle::Move } else { BindStyle::Ref }) -} diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 3fc10197b91..68119598285 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1059,6 +1059,43 @@ fn should_encode_const(def_kind: DefKind) -> bool { } } +fn should_encode_constness(def_kind: DefKind) -> bool { + match def_kind { + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::AssocTy + | DefKind::Fn + | DefKind::Const + | DefKind::Static(..) + | DefKind::Ctor(..) + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::ImplTraitPlaceholder + | DefKind::Impl + | DefKind::Closure + | DefKind::Generator + | DefKind::TyAlias => true, + DefKind::Variant + | DefKind::TraitAlias + | DefKind::ForeignTy + | DefKind::Field + | DefKind::TyParam + | DefKind::Mod + | DefKind::ForeignMod + | DefKind::ConstParam + | DefKind::Macro(..) + | DefKind::Use + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::ExternCrate => false, + } +} + fn should_encode_trait_impl_trait_tys<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { if tcx.def_kind(def_id) != DefKind::AssocFn { return false; @@ -1165,6 +1202,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { { record!(self.tables.trait_impl_trait_tys[def_id] <- table); } + if should_encode_constness(def_kind) { + self.tables.constness.set(def_id.index, tcx.constness(def_id)); + } } let inherent_impls = tcx.crate_inherent_impls(()); for (def_id, implementations) in inherent_impls.inherent_impls.iter() { @@ -1192,7 +1232,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }; record!(self.tables.variant_data[def_id] <- data); - self.tables.constness.set(def_id.index, hir::Constness::Const); record_array!(self.tables.children[def_id] <- variant.fields.iter().map(|f| { assert!(f.did.is_local()); f.did.index @@ -1220,7 +1259,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }; record!(self.tables.variant_data[def_id] <- data); - self.tables.constness.set(def_id.index, hir::Constness::Const); if variant.ctor_kind == CtorKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } @@ -1284,7 +1322,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.repr_options[def_id] <- adt_def.repr()); record!(self.tables.variant_data[def_id] <- data); - self.tables.constness.set(def_id.index, hir::Constness::Const); if variant.ctor_kind == CtorKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); } @@ -1320,7 +1357,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } }; self.tables.asyncness.set(def_id.index, m_sig.header.asyncness); - self.tables.constness.set(def_id.index, hir::Constness::NotConst); } ty::AssocKind::Type => { self.encode_explicit_item_bounds(def_id); @@ -1345,13 +1381,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let hir::ImplItemKind::Fn(ref sig, body) = ast_item.kind else { bug!() }; self.tables.asyncness.set(def_id.index, sig.header.asyncness); record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body)); - // Can be inside `impl const Trait`, so using sig.header.constness is not reliable - let constness = if self.tcx.is_const_fn_raw(def_id) { - hir::Constness::Const - } else { - hir::Constness::NotConst - }; - self.tables.constness.set(def_id.index, constness); } ty::AssocKind::Const | ty::AssocKind::Type => {} } @@ -1474,7 +1503,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hir::ItemKind::Fn(ref sig, .., body) => { self.tables.asyncness.set(def_id.index, sig.header.asyncness); record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body)); - self.tables.constness.set(def_id.index, sig.header.constness); } hir::ItemKind::Macro(ref macro_def, _) => { if macro_def.macro_rules { @@ -1495,7 +1523,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hir::ItemKind::Struct(ref struct_def, _) => { let adt_def = self.tcx.adt_def(def_id); record!(self.tables.repr_options[def_id] <- adt_def.repr()); - self.tables.constness.set(def_id.index, hir::Constness::Const); // Encode def_ids for each field and method // for methods, write all the stuff get_trait_method @@ -1524,9 +1551,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { is_non_exhaustive: variant.is_field_list_non_exhaustive(), }); } - hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => { + hir::ItemKind::Impl(hir::Impl { defaultness, .. }) => { self.tables.impl_defaultness.set(def_id.index, *defaultness); - self.tables.constness.set(def_id.index, *constness); let trait_ref = self.tcx.impl_trait_ref(def_id); if let Some(trait_ref) = trait_ref { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index d3a98e43c53..d4258151ff3 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2946,11 +2946,12 @@ impl Location { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(BasicBlockData<'_>, 144); static_assert_size!(LocalDecl<'_>, 56); static_assert_size!(Statement<'_>, 32); static_assert_size!(StatementKind<'_>, 16); static_assert_size!(Terminator<'_>, 112); static_assert_size!(TerminatorKind<'_>, 96); + // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 9a22a12b93b..85ef51f129b 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1245,10 +1245,11 @@ pub enum BinOp { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { use super::*; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(AggregateKind<'_>, 40); static_assert_size!(Operand<'_>, 24); static_assert_size!(Place<'_>, 16); static_assert_size!(PlaceElem<'_>, 24); static_assert_size!(Rvalue<'_>, 40); + // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a175b1eb467..06eb10c9137 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -613,16 +613,8 @@ rustc_queries! { separate_provide_extern } - // The cycle error here should be reported as an error by `check_representable`. - // We consider the type as Sized in the meanwhile to avoid - // further errors (done in impl Value for AdtSizedConstraint). - // Use `cycle_delay_bug` to delay the cycle error here to be emitted later - // in case we accidentally otherwise don't emit an error. - query adt_sized_constraint( - key: DefId - ) -> AdtSizedConstraint<'tcx> { + query adt_sized_constraint(key: DefId) -> &'tcx [Ty<'tcx>] { desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) } - cycle_delay_bug } query adt_dtorck_constraint( diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index f46f0ea4cab..ea7a507d7a4 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -848,7 +848,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] mod size_asserts { use super::*; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(Block, 56); static_assert_size!(Expr<'_>, 64); static_assert_size!(ExprKind<'_>, 40); @@ -856,4 +856,5 @@ mod size_asserts { static_assert_size!(PatKind<'_>, 56); static_assert_size!(Stmt<'_>, 48); static_assert_size!(StmtKind<'_>, 40); + // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 26b6be7a1b8..b0a2412ab15 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -26,9 +26,6 @@ use super::{ Destructor, FieldDef, GenericPredicates, ReprOptions, Ty, TyCtxt, VariantDef, VariantDiscr, }; -#[derive(Copy, Clone, HashStable, Debug)] -pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]); - bitflags! { #[derive(HashStable, TyEncodable, TyDecodable)] pub struct AdtFlags: u32 { @@ -563,7 +560,7 @@ impl<'tcx> AdtDef<'tcx> { /// Due to normalization being eager, this applies even if /// the associated type is behind a pointer (e.g., issue #31299). pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<&'tcx [Ty<'tcx>]> { - ty::EarlyBinder(tcx.adt_sized_constraint(self.did()).0) + ty::EarlyBinder(tcx.adt_sized_constraint(self.did())) } } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 5f8729a8ddf..6045c1acdd0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -191,10 +191,29 @@ pub enum LayoutError<'tcx> { impl<'a> IntoDiagnostic<'a, !> for LayoutError<'a> { fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, !> { - handler.struct_fatal(self.to_string()) + let mut diag = handler.struct_fatal(""); + + match self { + LayoutError::Unknown(ty) => { + diag.set_arg("ty", ty); + diag.set_primary_message(rustc_errors::fluent::middle::unknown_layout); + } + LayoutError::SizeOverflow(ty) => { + diag.set_arg("ty", ty); + diag.set_primary_message(rustc_errors::fluent::middle::values_too_big); + } + LayoutError::NormalizationFailure(ty, e) => { + diag.set_arg("ty", ty); + diag.set_arg("failure_ty", e.get_type_for_failure()); + diag.set_primary_message(rustc_errors::fluent::middle::cannot_be_normalized); + } + } + diag } } +// FIXME: Once the other errors that embed this error have been converted to translateable +// diagnostics, this Display impl should be removed. impl<'tcx> fmt::Display for LayoutError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 753d7bffe84..12d24d6751e 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2668,8 +2668,9 @@ pub struct DestructuredConst<'tcx> { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(PredicateS<'_>, 48); static_assert_size!(TyS<'_>, 40); static_assert_size!(WithStableHash<TyS<'_>>, 56); + // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index b6cda34c51f..ce1b69935f2 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -32,7 +32,7 @@ use crate::ty::layout::TyAndLayout; use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::GeneratorDiagnosticData; -use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; +use crate::ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_attr as attr; diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index bb89959b29d..f4b4c3fb05a 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -3,7 +3,7 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_middle::ty::Representability; -use rustc_middle::ty::{self, AdtSizedConstraint, DefIdTree, Ty, TyCtxt}; +use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; use rustc_query_system::query::QueryInfo; use rustc_query_system::Value; use rustc_span::def_id::LocalDefId; @@ -31,18 +31,6 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> { } } -impl<'tcx> Value<TyCtxt<'tcx>> for AdtSizedConstraint<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self { - // SAFETY: This is never called when `Self` is not `AdtSizedConstraint<'tcx>`. - // FIXME: Represent the above fact in the trait system somehow. - unsafe { - std::mem::transmute::<AdtSizedConstraint<'tcx>, AdtSizedConstraint<'_>>( - AdtSizedConstraint(tcx.intern_type_list(&[tcx.ty_error()])), - ) - } - } -} - impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> { fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self { let err = tcx.ty_error(); diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 0dc05475ce9..81c051b8f35 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -459,7 +459,8 @@ fn make_token_stream( mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(AttrWrapper, 16); static_assert_size!(LazyAttrTokenStreamImpl, 144); + // tidy-alphabetical-end } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index e82044a89c4..ebcbc75ba32 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1789,20 +1789,25 @@ impl<'a> Parser<'a> { } } else { let mut err = self.expected_ident_found(); - if let Some((ident, _)) = self.token.ident() && ident.as_str() == "let" { - self.bump(); // `let` - let span = self.prev_token.span.until(self.token.span); + if self.eat_keyword_noexpect(kw::Let) + && let removal_span = self.prev_token.span.until(self.token.span) + && let Ok(ident) = self.parse_ident_common(false) + // Cancel this error, we don't need it. + .map_err(|err| err.cancel()) + && self.token.kind == TokenKind::Colon + { err.span_suggestion( - span, - "remove the let, the `let` keyword is not allowed in struct field definitions", + removal_span, + "remove this `let` keyword", String::new(), Applicability::MachineApplicable, ); err.note("the `let` keyword is not allowed in `struct` fields"); err.note("see <https://doc.rust-lang.org/book/ch05-01-defining-structs.html> for more information"); err.emit(); - self.bump(); return Ok(ident); + } else { + self.restore_snapshot(snapshot); } err }; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5455d063c13..73fb89bbc38 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -4,10 +4,13 @@ //! conflicts between multiple such attributes attached to the same //! item. -use crate::errors; +use crate::errors::{ + self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr, + OnlyHasEffectOn, TransparentIncompatible, UnrecognizedReprHint, +}; use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{fluent, struct_span_err, Applicability, MultiSpan}; +use rustc_errors::{fluent, Applicability, MultiSpan}; use rustc_expand::base::resolve_path; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; @@ -164,17 +167,17 @@ impl CheckAttrVisitor<'_> { sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target), sym::deprecated => self.check_deprecated(hir_id, attr, span, target), sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target), - sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]), + sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod), sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target), sym::macro_export => self.check_macro_export(hir_id, attr, target), sym::ignore | sym::should_panic | sym::proc_macro_derive => { - self.check_generic_attr(hir_id, attr, target, &[Target::Fn]) + self.check_generic_attr(hir_id, attr, target, Target::Fn) } sym::automatically_derived => { - self.check_generic_attr(hir_id, attr, target, &[Target::Impl]) + self.check_generic_attr(hir_id, attr, target, Target::Impl) } sym::no_implicit_prelude => { - self.check_generic_attr(hir_id, attr, target, &[Target::Mod]) + self.check_generic_attr(hir_id, attr, target, Target::Mod) } sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id), _ => {} @@ -351,31 +354,17 @@ impl CheckAttrVisitor<'_> { hir_id: HirId, attr: &Attribute, target: Target, - allowed_targets: &[Target], + allowed_target: Target, ) { - if !allowed_targets.iter().any(|t| t == &target) { - let name = attr.name_or_empty(); - let mut i = allowed_targets.iter(); - // Pluralize - let b = i.next().map_or_else(String::new, |t| t.to_string() + "s"); - let supported_names = i.enumerate().fold(b, |mut b, (i, allowed_target)| { - if allowed_targets.len() > 2 && i == allowed_targets.len() - 2 { - b.push_str(", and "); - } else if allowed_targets.len() == 2 && i == allowed_targets.len() - 2 { - b.push_str(" and "); - } else { - b.push_str(", "); - } - // Pluralize - b.push_str(&(allowed_target.to_string() + "s")); - b - }); - self.tcx.struct_span_lint_hir( + if target != allowed_target { + self.tcx.emit_spanned_lint( UNUSED_ATTRIBUTES, hir_id, attr.span, - &format!("`#[{name}]` only has an effect on {}", supported_names), - |lint| lint, + OnlyHasEffectOn { + attr_name: attr.name_or_empty(), + target_name: allowed_target.name().replace(" ", "_"), + }, ); } } @@ -432,7 +421,7 @@ impl CheckAttrVisitor<'_> { ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(), ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(), }; - tcx.sess.span_err(p.span, &repr); + tcx.sess.emit_err(ObjectLifetimeErr { span: p.span, repr }); } } } @@ -1605,12 +1594,17 @@ impl CheckAttrVisitor<'_> { continue; } - let (article, allowed_targets) = match hint.name_or_empty() { + match hint.name_or_empty() { sym::C => { is_c = true; match target { Target::Struct | Target::Union | Target::Enum => continue, - _ => ("a", "struct, enum, or union"), + _ => { + self.tcx.sess.emit_err(AttrApplication::StructEnumUnion { + hint_span: hint.span(), + span, + }); + } } } sym::align => { @@ -1626,12 +1620,20 @@ impl CheckAttrVisitor<'_> { match target { Target::Struct | Target::Union | Target::Enum | Target::Fn => continue, - _ => ("a", "struct, enum, function, or union"), + _ => { + self.tcx.sess.emit_err(AttrApplication::StructEnumFunctionUnion { + hint_span: hint.span(), + span, + }); + } } } sym::packed => { if target != Target::Struct && target != Target::Union { - ("a", "struct or union") + self.tcx.sess.emit_err(AttrApplication::StructUnion { + hint_span: hint.span(), + span, + }); } else { continue; } @@ -1639,7 +1641,9 @@ impl CheckAttrVisitor<'_> { sym::simd => { is_simd = true; if target != Target::Struct { - ("a", "struct") + self.tcx + .sess + .emit_err(AttrApplication::Struct { hint_span: hint.span(), span }); } else { continue; } @@ -1648,7 +1652,12 @@ impl CheckAttrVisitor<'_> { is_transparent = true; match target { Target::Struct | Target::Union | Target::Enum => continue, - _ => ("a", "struct, enum, or union"), + _ => { + self.tcx.sess.emit_err(AttrApplication::StructEnumUnion { + hint_span: hint.span(), + span, + }); + } } } sym::i8 @@ -1665,35 +1674,18 @@ impl CheckAttrVisitor<'_> { | sym::usize => { int_reprs += 1; if target != Target::Enum { - ("an", "enum") + self.tcx + .sess + .emit_err(AttrApplication::Enum { hint_span: hint.span(), span }); } else { continue; } } _ => { - struct_span_err!( - self.tcx.sess, - hint.span(), - E0552, - "unrecognized representation hint" - ) - .help("valid reprs are `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, \ - `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`") - .emit(); - + self.tcx.sess.emit_err(UnrecognizedReprHint { span: hint.span() }); continue; } }; - - struct_span_err!( - self.tcx.sess, - hint.span(), - E0517, - "{}", - &format!("attribute should be applied to {article} {allowed_targets}") - ) - .span_label(span, &format!("not {article} {allowed_targets}")) - .emit(); } // Just point at all repr hints if there are any incompatibilities. @@ -1703,14 +1695,9 @@ impl CheckAttrVisitor<'_> { // Error on repr(transparent, <anything else>). if is_transparent && hints.len() > 1 { let hint_spans: Vec<_> = hint_spans.clone().collect(); - struct_span_err!( - self.tcx.sess, - hint_spans, - E0692, - "transparent {} cannot have other repr hints", - target - ) - .emit(); + self.tcx + .sess + .emit_err(TransparentIncompatible { hint_spans, target: target.to_string() }); } // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8) if (int_reprs > 1) @@ -1862,14 +1849,12 @@ impl CheckAttrVisitor<'_> { match std::fs::File::open(&file) { Ok(_) => true, - Err(err) => { - self.tcx - .sess - .struct_span_err( - meta_item.span, - &format!("couldn't read {}: {}", file.display(), err), - ) - .emit(); + Err(error) => { + self.tcx.sess.emit_err(DebugVisualizerUnreadable { + span: meta_item.span, + file: &file, + error, + }); false } } @@ -2180,25 +2165,11 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { if attr.style == AttrStyle::Inner { for attr_to_check in ATTRS_TO_CHECK { if attr.has_name(*attr_to_check) { - let mut err = tcx.sess.struct_span_err( - attr.span, - &format!( - "`{}` attribute cannot be used at crate level", - attr_to_check.to_ident_string() - ), - ); - // Only emit an error with a suggestion if we can create a - // string out of the attribute span - if let Ok(src) = tcx.sess.source_map().span_to_snippet(attr.span) { - let replacement = src.replace("#!", "#"); - err.span_suggestion_verbose( - attr.span, - "perhaps you meant to use an outer attribute", - replacement, - rustc_errors::Applicability::MachineApplicable, - ); - } - err.emit(); + tcx.sess.emit_err(InvalidAttrAtCrateLevel { + span: attr.span, + snippet: tcx.sess.source_map().span_to_snippet(attr.span).ok(), + name: *attr_to_check, + }); } } } diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index e502b9b54e3..aa726d6cd92 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -8,7 +8,6 @@ //! through, but errors for structured control flow in a `const` should be emitted here. use rustc_attr as attr; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; @@ -18,6 +17,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; use rustc_span::{sym, Span, Symbol}; +use crate::errors::ExprNotAllowedInContext; + /// An expression that is not *always* legal in a const context. #[derive(Clone, Copy)] enum NonConstExpr { @@ -133,18 +134,22 @@ impl<'tcx> CheckConstVisitor<'tcx> { let const_kind = const_kind.expect("`const_check_violated` may only be called inside a const context"); - let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name()); - let required_gates = required_gates.unwrap_or(&[]); let missing_gates: Vec<_> = required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect(); match missing_gates.as_slice() { [] => { - struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit(); + tcx.sess.emit_err(ExprNotAllowedInContext { + span, + expr: expr.name(), + context: const_kind.keyword_name(), + }); } [missing_primary, ref missing_secondary @ ..] => { + let msg = + format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name()); let mut err = feature_err(&tcx.sess.parse_sess, *missing_primary, span, &msg); // If multiple feature gates would be required to enable this expression, include diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 08f704da62c..6a97ad3fe86 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -4,7 +4,7 @@ use itertools::Itertools; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{pluralize, Applicability, DelayDm, MultiSpan}; +use rustc_errors::{pluralize, Applicability, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -18,6 +18,8 @@ use rustc_session::lint; use rustc_span::symbol::{sym, Symbol}; use std::mem; +use crate::errors::UselessAssignment; + // Any local node that may call something in its body block should be // explored. For example, if it's a live Node::Item that is a // function, then we should explore its block to check for codes that @@ -180,19 +182,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && !assign.span.from_expansion() { let is_field_assign = matches!(lhs.kind, hir::ExprKind::Field(..)); - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( lint::builtin::DEAD_CODE, assign.hir_id, assign.span, - DelayDm(|| format!( - "useless assignment of {} of type `{}` to itself", - if is_field_assign { "field" } else { "variable" }, - self.typeck_results().expr_ty(lhs), - )), - |lint| { - lint - - }, + UselessAssignment { is_field_assign, ty: self.typeck_results().expr_ty(lhs) } ) } } diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index e08683fe23b..253b0a88e48 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -13,6 +13,8 @@ use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType}; use std::sync::Arc; +use crate::errors::DebugVisualizerUnreadable; + fn check_for_debugger_visualizer<'tcx>( tcx: TyCtxt<'tcx>, hir_id: HirId, @@ -54,13 +56,12 @@ fn check_for_debugger_visualizer<'tcx>( debugger_visualizers .insert(DebuggerVisualizerFile::new(Arc::from(contents), visualizer_type)); } - Err(err) => { - tcx.sess - .struct_span_err( - meta_item.span, - &format!("couldn't read {}: {}", file.display(), err), - ) - .emit(); + Err(error) => { + tcx.sess.emit_err(DebugVisualizerUnreadable { + span: meta_item.span, + file: &file, + error, + }); } } } diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index e428d9293db..3f991cf6572 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -14,7 +14,9 @@ use rustc_hir::diagnostic_items::DiagnosticItems; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{kw::Empty, sym, Symbol}; + +use crate::errors::{DuplicateDiagnosticItem, DuplicateDiagnosticItemInCrate}; fn observe_item<'tcx>( tcx: TyCtxt<'tcx>, @@ -33,25 +35,22 @@ fn collect_item(tcx: TyCtxt<'_>, items: &mut DiagnosticItems, name: Symbol, item items.id_to_name.insert(item_def_id, name); if let Some(original_def_id) = items.name_to_id.insert(name, item_def_id) { if original_def_id != item_def_id { - let mut err = match tcx.hir().span_if_local(item_def_id) { - Some(span) => tcx - .sess - .struct_span_err(span, &format!("duplicate diagnostic item found: `{name}`.")), - None => tcx.sess.struct_err(&format!( - "duplicate diagnostic item in crate `{}`: `{}`.", - tcx.crate_name(item_def_id.krate), - name - )), - }; - if let Some(span) = tcx.hir().span_if_local(original_def_id) { - err.span_note(span, "the diagnostic item is first defined here"); + let orig_span = tcx.hir().span_if_local(original_def_id); + let orig_crate_name = if orig_span.is_some() { + None } else { - err.note(&format!( - "the diagnostic item is first defined in crate `{}`.", - tcx.crate_name(original_def_id.krate) - )); - } - err.emit(); + Some(tcx.crate_name(original_def_id.krate)) + }; + match tcx.hir().span_if_local(item_def_id) { + Some(span) => tcx.sess.emit_err(DuplicateDiagnosticItem { span, name }), + None => tcx.sess.emit_err(DuplicateDiagnosticItemInCrate { + span: orig_span, + orig_crate_name: orig_crate_name.unwrap_or(Empty), + have_orig_crate_name: orig_crate_name.map(|_| ()), + crate_name: tcx.crate_name(item_def_id.krate), + name, + }), + }; } } } diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index eec37d19a88..38a259ca884 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -1,5 +1,5 @@ use rustc_ast::entry::EntryPointType; -use rustc_errors::struct_span_err; +use rustc_errors::error_code; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{ItemId, Node, CRATE_HIR_ID}; @@ -8,7 +8,12 @@ use rustc_middle::ty::{DefIdTree, TyCtxt}; use rustc_session::config::{sigpipe, CrateType, EntryFnType}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; -use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_span::{Span, Symbol}; + +use crate::errors::{ + AttrOnlyInFunctions, AttrOnlyOnMain, AttrOnlyOnRootMain, ExternMain, MultipleRustcMain, + MultipleStartFunctions, NoMainErr, UnixSigpipeValues, +}; struct EntryContext<'tcx> { tcx: TyCtxt<'tcx>, @@ -71,14 +76,9 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry } } -fn err_if_attr_found(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol, details: &str) { +fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Option<Span> { let attrs = ctxt.tcx.hir().attrs(id.hir_id()); - if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym) { - ctxt.tcx - .sess - .struct_span_err(attr.span, &format!("`{}` attribute {}", sym, details)) - .emit(); - } + ctxt.tcx.sess.find_by_name(attrs, sym).map(|attr| attr.span) } fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { @@ -86,49 +86,47 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { match entry_point_type(ctxt, id, at_root) { EntryPointType::None => { - err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`"); + if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) { + ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe }); + } } _ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => { - err_if_attr_found(ctxt, id, sym::start, "can only be used on functions"); - err_if_attr_found(ctxt, id, sym::rustc_main, "can only be used on functions"); + for attr in [sym::start, sym::rustc_main] { + if let Some(span) = attr_span_by_symbol(ctxt, id, attr) { + ctxt.tcx.sess.emit_err(AttrOnlyInFunctions { span, attr }); + } + } } EntryPointType::MainNamed => (), EntryPointType::OtherMain => { - err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on root `fn main()`"); + if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) { + ctxt.tcx.sess.emit_err(AttrOnlyOnRootMain { span, attr: sym::unix_sigpipe }); + } ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id)); } EntryPointType::RustcMainAttr => { if ctxt.attr_main_fn.is_none() { ctxt.attr_main_fn = Some((id.def_id.def_id, ctxt.tcx.def_span(id.def_id))); } else { - struct_span_err!( - ctxt.tcx.sess, - ctxt.tcx.def_span(id.def_id.to_def_id()), - E0137, - "multiple functions with a `#[rustc_main]` attribute" - ) - .span_label( - ctxt.tcx.def_span(id.def_id.to_def_id()), - "additional `#[rustc_main]` function", - ) - .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[rustc_main]` function") - .emit(); + ctxt.tcx.sess.emit_err(MultipleRustcMain { + span: ctxt.tcx.def_span(id.def_id.to_def_id()), + first: ctxt.attr_main_fn.unwrap().1, + additional: ctxt.tcx.def_span(id.def_id.to_def_id()), + }); } } EntryPointType::Start => { - err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`"); + if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) { + ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe }); + } if ctxt.start_fn.is_none() { ctxt.start_fn = Some((id.def_id.def_id, ctxt.tcx.def_span(id.def_id))); } else { - struct_span_err!( - ctxt.tcx.sess, - ctxt.tcx.def_span(id.def_id), - E0138, - "multiple `start` functions" - ) - .span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here") - .span_label(ctxt.tcx.def_span(id.def_id.to_def_id()), "multiple `start` functions") - .emit(); + ctxt.tcx.sess.emit_err(MultipleStartFunctions { + span: ctxt.tcx.def_span(id.def_id), + labeled: ctxt.tcx.def_span(id.def_id.to_def_id()), + previous: ctxt.start_fn.unwrap().1, + }); } } } @@ -144,12 +142,7 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, if let Some(main_def) = tcx.resolutions(()).main_def && let Some(def_id) = main_def.opt_fn_def_id() { // non-local main imports are handled below if let Some(def_id) = def_id.as_local() && matches!(tcx.hir().find_by_def_id(def_id), Some(Node::ForeignItem(_))) { - tcx.sess - .struct_span_err( - tcx.def_span(def_id), - "the `main` function cannot be declared in an `extern` block", - ) - .emit(); + tcx.sess.emit_err(ExternMain { span: tcx.def_span(def_id) }); return None; } @@ -182,12 +175,7 @@ fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 { sigpipe::DEFAULT } _ => { - tcx.sess - .struct_span_err( - attr.span, - "valid values for `#[unix_sigpipe = \"...\"]` are `inherit`, `sig_ign`, or `sig_dfl`", - ) - .emit(); + tcx.sess.emit_err(UnixSigpipeValues { span: attr.span }); sigpipe::DEFAULT } } @@ -206,52 +194,29 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) { } // There is no main function. - let mut err = struct_span_err!( - tcx.sess, - DUMMY_SP, - E0601, - "`main` function not found in crate `{}`", - tcx.crate_name(LOCAL_CRATE) - ); - let filename = &tcx.sess.local_crate_source_file; - let note = if !visitor.non_main_fns.is_empty() { - for &span in &visitor.non_main_fns { - err.span_note(span, "here is a function named `main`"); - } - err.note("you have one or more functions named `main` not defined at the crate level"); - err.help("consider moving the `main` function definitions"); - // There were some functions named `main` though. Try to give the user a hint. - format!( - "the main function must be defined at the crate level{}", - filename.as_ref().map(|f| format!(" (in `{}`)", f.display())).unwrap_or_default() - ) - } else if let Some(filename) = filename { - format!("consider adding a `main` function to `{}`", filename.display()) - } else { - String::from("consider adding a `main` function at the crate level") - }; + let mut has_filename = true; + let filename = tcx.sess.local_crate_source_file.clone().unwrap_or_else(|| { + has_filename = false; + Default::default() + }); + let main_def_opt = tcx.resolutions(()).main_def; + let diagnostic_id = error_code!(E0601); + let add_teach_note = tcx.sess.teach(&diagnostic_id); // The file may be empty, which leads to the diagnostic machinery not emitting this // note. This is a relatively simple way to detect that case and emit a span-less // note instead. - if tcx.sess.source_map().lookup_line(sp.hi()).is_ok() { - err.set_span(sp.shrink_to_hi()); - err.span_label(sp.shrink_to_hi(), ¬e); - } else { - err.note(¬e); - } - - if let Some(main_def) = tcx.resolutions(()).main_def && main_def.opt_fn_def_id().is_none(){ - // There is something at `crate::main`, but it is not a function definition. - err.span_label(main_def.span, "non-function item at `crate::main` is found"); - } - - if tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "If you don't know the basics of Rust, you can go look to the Rust Book \ - to get started: https://doc.rust-lang.org/book/", - ); - } - err.emit(); + let file_empty = !tcx.sess.source_map().lookup_line(sp.hi()).is_ok(); + + tcx.sess.emit_err(NoMainErr { + sp, + crate_name: tcx.crate_name(LOCAL_CRATE), + has_filename, + filename, + file_empty, + non_main_fns: visitor.non_main_fns.clone(), + main_def_opt, + add_teach_note, + }); } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index cc231af71a2..1cc81a9ab98 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1,6 +1,16 @@ -use rustc_errors::{Applicability, MultiSpan}; +use std::{ + io::Error, + path::{Path, PathBuf}, +}; + +use rustc_ast::Label; +use rustc_errors::{error_code, Applicability, ErrorGuaranteed, IntoDiagnostic, MultiSpan}; +use rustc_hir::{self as hir, ExprKind, Target}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; -use rustc_span::{Span, Symbol}; +use rustc_middle::ty::{MainDefinition, Ty}; +use rustc_span::{Span, Symbol, DUMMY_SP}; + +use crate::lang_items::Duplicate; #[derive(LintDiagnostic)] #[diag(passes::outer_crate_level_attr)] @@ -527,6 +537,15 @@ pub struct DebugVisualizerInvalid { } #[derive(Diagnostic)] +#[diag(passes::debug_visualizer_unreadable)] +pub struct DebugVisualizerUnreadable<'a> { + #[primary_span] + pub span: Span, + pub file: &'a Path, + pub error: Error, +} + +#[derive(Diagnostic)] #[diag(passes::rustc_allow_const_fn_unstable)] pub struct RustcAllowConstFnUnstable { #[primary_span] @@ -665,3 +684,765 @@ pub struct DeprecatedAnnotationHasNoEffect { #[suggestion(applicability = "machine-applicable", code = "")] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(passes::unknown_external_lang_item, code = "E0264")] +pub struct UnknownExternLangItem { + #[primary_span] + pub span: Span, + pub lang_item: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::missing_panic_handler)] +pub struct MissingPanicHandler; + +#[derive(Diagnostic)] +#[diag(passes::alloc_func_required)] +pub struct AllocFuncRequired; + +#[derive(Diagnostic)] +#[diag(passes::missing_alloc_error_handler)] +pub struct MissingAllocErrorHandler; + +#[derive(Diagnostic)] +#[diag(passes::missing_lang_item)] +#[note] +#[help] +pub struct MissingLangItem { + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::lang_item_on_incorrect_target, code = "E0718")] +pub struct LangItemOnIncorrectTarget { + #[primary_span] + #[label] + pub span: Span, + pub name: Symbol, + pub expected_target: Target, + pub actual_target: Target, +} + +#[derive(Diagnostic)] +#[diag(passes::unknown_lang_item, code = "E0522")] +pub struct UnknownLangItem { + #[primary_span] + #[label] + pub span: Span, + pub name: Symbol, +} + +pub struct InvalidAttrAtCrateLevel { + pub span: Span, + pub snippet: Option<String>, + pub name: Symbol, +} + +impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel { + fn into_diagnostic( + self, + handler: &'_ rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = + handler.struct_err(rustc_errors::fluent::passes::invalid_attr_at_crate_level); + diag.set_span(self.span); + diag.set_arg("name", self.name); + // Only emit an error with a suggestion if we can create a string out + // of the attribute span + if let Some(src) = self.snippet { + let replacement = src.replace("#!", "#"); + diag.span_suggestion_verbose( + self.span, + rustc_errors::fluent::passes::suggestion, + replacement, + rustc_errors::Applicability::MachineApplicable, + ); + } + diag + } +} + +#[derive(Diagnostic)] +#[diag(passes::duplicate_diagnostic_item)] +pub struct DuplicateDiagnosticItem { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::duplicate_diagnostic_item_in_crate)] +pub struct DuplicateDiagnosticItemInCrate { + #[note(passes::diagnostic_item_first_defined)] + pub span: Option<Span>, + pub orig_crate_name: Symbol, + #[note] + pub have_orig_crate_name: Option<()>, + pub crate_name: Symbol, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::abi)] +pub struct Abi { + #[primary_span] + pub span: Span, + pub abi: String, +} + +#[derive(Diagnostic)] +#[diag(passes::align)] +pub struct Align { + #[primary_span] + pub span: Span, + pub align: String, +} + +#[derive(Diagnostic)] +#[diag(passes::size)] +pub struct Size { + #[primary_span] + pub span: Span, + pub size: String, +} + +#[derive(Diagnostic)] +#[diag(passes::homogeneous_aggregate)] +pub struct HomogeneousAggregate { + #[primary_span] + pub span: Span, + pub homogeneous_aggregate: String, +} + +#[derive(Diagnostic)] +#[diag(passes::layout_of)] +pub struct LayoutOf { + #[primary_span] + pub span: Span, + pub normalized_ty: String, + pub ty_layout: String, +} + +#[derive(Diagnostic)] +#[diag(passes::unrecognized_field)] +pub struct UnrecognizedField { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::feature_stable_twice, code = "E0711")] +pub struct FeatureStableTwice { + #[primary_span] + pub span: Span, + pub feature: Symbol, + pub since: Symbol, + pub prev_since: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::feature_previously_declared, code = "E0711")] +pub struct FeaturePreviouslyDeclared<'a, 'b> { + #[primary_span] + pub span: Span, + pub feature: Symbol, + pub declared: &'a str, + pub prev_declared: &'b str, +} + +#[derive(Diagnostic)] +#[diag(passes::expr_not_allowed_in_context, code = "E0744")] +pub struct ExprNotAllowedInContext<'a> { + #[primary_span] + pub span: Span, + pub expr: String, + pub context: &'a str, +} + +pub struct BreakNonLoop<'a> { + pub span: Span, + pub head: Option<Span>, + pub kind: &'a str, + pub suggestion: String, + pub loop_label: Option<Label>, + pub break_label: Option<Label>, + pub break_expr_kind: &'a ExprKind<'a>, + pub break_expr_span: Span, +} + +impl<'a> IntoDiagnostic<'_> for BreakNonLoop<'a> { + fn into_diagnostic( + self, + handler: &rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_span_err_with_code( + self.span, + rustc_errors::fluent::passes::break_non_loop, + error_code!(E0571), + ); + diag.set_arg("kind", self.kind); + diag.span_label(self.span, rustc_errors::fluent::passes::label); + if let Some(head) = self.head { + diag.span_label(head, rustc_errors::fluent::passes::label2); + } + diag.span_suggestion( + self.span, + rustc_errors::fluent::passes::suggestion, + self.suggestion, + Applicability::MaybeIncorrect, + ); + if let (Some(label), None) = (self.loop_label, self.break_label) { + match self.break_expr_kind { + ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { segments: [segment], res: hir::def::Res::Err, .. }, + )) if label.ident.to_string() == format!("'{}", segment.ident) => { + // This error is redundant, we will have already emitted a + // suggestion to use the label when `segment` wasn't found + // (hence the `Res::Err` check). + diag.delay_as_bug(); + } + _ => { + diag.span_suggestion( + self.break_expr_span, + rustc_errors::fluent::passes::break_expr_suggestion, + label.ident, + Applicability::MaybeIncorrect, + ); + } + } + } + diag + } +} + +#[derive(Diagnostic)] +#[diag(passes::continue_labeled_block, code = "E0696")] +pub struct ContinueLabeledBlock { + #[primary_span] + #[label] + pub span: Span, + #[label(passes::block_label)] + pub block_span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::break_inside_closure, code = "E0267")] +pub struct BreakInsideClosure<'a> { + #[primary_span] + #[label] + pub span: Span, + #[label(passes::closure_label)] + pub closure_span: Span, + pub name: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes::break_inside_async_block, code = "E0267")] +pub struct BreakInsideAsyncBlock<'a> { + #[primary_span] + #[label] + pub span: Span, + #[label(passes::async_block_label)] + pub closure_span: Span, + pub name: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes::outside_loop, code = "E0268")] +pub struct OutsideLoop<'a> { + #[primary_span] + #[label] + pub span: Span, + pub name: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes::unlabeled_in_labeled_block, code = "E0695")] +pub struct UnlabeledInLabeledBlock<'a> { + #[primary_span] + #[label] + pub span: Span, + pub cf_type: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes::unlabeled_cf_in_while_condition, code = "E0590")] +pub struct UnlabeledCfInWhileCondition<'a> { + #[primary_span] + #[label] + pub span: Span, + pub cf_type: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes::cannot_inline_naked_function)] +pub struct CannotInlineNakedFunction { + #[primary_span] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(passes::undefined_naked_function_abi)] +pub struct UndefinedNakedFunctionAbi; + +#[derive(Diagnostic)] +#[diag(passes::no_patterns)] +pub struct NoPatterns { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::params_not_allowed)] +#[help] +pub struct ParamsNotAllowed { + #[primary_span] + pub span: Span, +} + +pub struct NakedFunctionsAsmBlock { + pub span: Span, + pub multiple_asms: Vec<Span>, + pub non_asms: Vec<Span>, +} + +impl IntoDiagnostic<'_> for NakedFunctionsAsmBlock { + fn into_diagnostic( + self, + handler: &rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_span_err_with_code( + self.span, + rustc_errors::fluent::passes::naked_functions_asm_block, + error_code!(E0787), + ); + for span in self.multiple_asms.iter() { + diag.span_label(*span, rustc_errors::fluent::passes::label_multiple_asm); + } + for span in self.non_asms.iter() { + diag.span_label(*span, rustc_errors::fluent::passes::label_non_asm); + } + diag + } +} + +#[derive(Diagnostic)] +#[diag(passes::naked_functions_operands, code = "E0787")] +pub struct NakedFunctionsOperands { + #[primary_span] + pub unsupported_operands: Vec<Span>, +} + +#[derive(Diagnostic)] +#[diag(passes::naked_functions_asm_options, code = "E0787")] +pub struct NakedFunctionsAsmOptions { + #[primary_span] + pub span: Span, + pub unsupported_options: String, +} + +#[derive(Diagnostic)] +#[diag(passes::naked_functions_must_use_noreturn, code = "E0787")] +pub struct NakedFunctionsMustUseNoreturn { + #[primary_span] + pub span: Span, + #[suggestion(code = ", options(noreturn)", applicability = "machine-applicable")] + pub last_span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::attr_only_on_main)] +pub struct AttrOnlyOnMain { + #[primary_span] + pub span: Span, + pub attr: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::attr_only_on_root_main)] +pub struct AttrOnlyOnRootMain { + #[primary_span] + pub span: Span, + pub attr: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::attr_only_in_functions)] +pub struct AttrOnlyInFunctions { + #[primary_span] + pub span: Span, + pub attr: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::multiple_rustc_main, code = "E0137")] +pub struct MultipleRustcMain { + #[primary_span] + pub span: Span, + #[label(passes::first)] + pub first: Span, + #[label(passes::additional)] + pub additional: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::multiple_start_functions, code = "E0138")] +pub struct MultipleStartFunctions { + #[primary_span] + pub span: Span, + #[label] + pub labeled: Span, + #[label(passes::previous)] + pub previous: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::extern_main)] +pub struct ExternMain { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::unix_sigpipe_values)] +pub struct UnixSigpipeValues { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::no_main_function, code = "E0601")] +pub struct NoMainFunction { + #[primary_span] + pub span: Span, + pub crate_name: String, +} + +pub struct NoMainErr { + pub sp: Span, + pub crate_name: Symbol, + pub has_filename: bool, + pub filename: PathBuf, + pub file_empty: bool, + pub non_main_fns: Vec<Span>, + pub main_def_opt: Option<MainDefinition>, + pub add_teach_note: bool, +} + +impl<'a> IntoDiagnostic<'a> for NoMainErr { + fn into_diagnostic( + self, + handler: &'a rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> { + let mut diag = handler.struct_span_err_with_code( + DUMMY_SP, + rustc_errors::fluent::passes::no_main_function, + error_code!(E0601), + ); + diag.set_arg("crate_name", self.crate_name); + diag.set_arg("filename", self.filename); + diag.set_arg("has_filename", self.has_filename); + let note = if !self.non_main_fns.is_empty() { + for &span in &self.non_main_fns { + diag.span_note(span, rustc_errors::fluent::passes::here_is_main); + } + diag.note(rustc_errors::fluent::passes::one_or_more_possible_main); + diag.help(rustc_errors::fluent::passes::consider_moving_main); + // There were some functions named `main` though. Try to give the user a hint. + rustc_errors::fluent::passes::main_must_be_defined_at_crate + } else if self.has_filename { + rustc_errors::fluent::passes::consider_adding_main_to_file + } else { + rustc_errors::fluent::passes::consider_adding_main_at_crate + }; + if self.file_empty { + diag.note(note); + } else { + diag.set_span(self.sp.shrink_to_hi()); + diag.span_label(self.sp.shrink_to_hi(), note); + } + + if let Some(main_def) = self.main_def_opt && main_def.opt_fn_def_id().is_none(){ + // There is something at `crate::main`, but it is not a function definition. + diag.span_label(main_def.span, rustc_errors::fluent::passes::non_function_main); + } + + if self.add_teach_note { + diag.note(rustc_errors::fluent::passes::teach_note); + } + diag + } +} + +pub struct DuplicateLangItem { + pub local_span: Option<Span>, + pub lang_item_name: Symbol, + pub crate_name: Symbol, + pub dependency_of: Symbol, + pub is_local: bool, + pub path: String, + pub first_defined_span: Option<Span>, + pub orig_crate_name: Symbol, + pub orig_dependency_of: Symbol, + pub orig_is_local: bool, + pub orig_path: String, + pub(crate) duplicate: Duplicate, +} + +impl IntoDiagnostic<'_> for DuplicateLangItem { + fn into_diagnostic( + self, + handler: &rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_err_with_code( + match self.duplicate { + Duplicate::Plain => rustc_errors::fluent::passes::duplicate_lang_item, + + Duplicate::Crate => rustc_errors::fluent::passes::duplicate_lang_item_crate, + Duplicate::CrateDepends => { + rustc_errors::fluent::passes::duplicate_lang_item_crate_depends + } + }, + error_code!(E0152), + ); + diag.set_arg("lang_item_name", self.lang_item_name); + diag.set_arg("crate_name", self.crate_name); + diag.set_arg("dependency_of", self.dependency_of); + diag.set_arg("path", self.path); + diag.set_arg("orig_crate_name", self.orig_crate_name); + diag.set_arg("orig_dependency_of", self.orig_dependency_of); + diag.set_arg("orig_path", self.orig_path); + if let Some(span) = self.local_span { + diag.set_span(span); + } + if let Some(span) = self.first_defined_span { + diag.span_note(span, rustc_errors::fluent::passes::first_defined_span); + } else { + if self.orig_dependency_of.is_empty() { + diag.note(rustc_errors::fluent::passes::first_defined_crate); + } else { + diag.note(rustc_errors::fluent::passes::first_defined_crate_depends); + } + + if self.orig_is_local { + diag.note(rustc_errors::fluent::passes::first_definition_local); + } else { + diag.note(rustc_errors::fluent::passes::first_definition_path); + } + + if self.is_local { + diag.note(rustc_errors::fluent::passes::second_definition_local); + } else { + diag.note(rustc_errors::fluent::passes::second_definition_path); + } + } + diag + } +} + +#[derive(Diagnostic)] +#[diag(passes::incorrect_target, code = "E0718")] +pub struct IncorrectTarget<'a> { + #[primary_span] + pub span: Span, + #[label] + pub generics_span: Span, + pub name: &'a str, // cannot be symbol because it renders e.g. `r#fn` instead of `fn` + pub kind: &'static str, + pub num: usize, + pub actual_num: usize, + pub at_least: bool, +} + +#[derive(LintDiagnostic)] +#[diag(passes::useless_assignment)] +pub struct UselessAssignment<'a> { + pub is_field_assign: bool, + pub ty: Ty<'a>, +} + +#[derive(LintDiagnostic)] +#[diag(passes::only_has_effect_on)] +pub struct OnlyHasEffectOn { + pub attr_name: Symbol, + pub target_name: String, +} + +#[derive(Diagnostic)] +#[diag(passes::object_lifetime_err)] +pub struct ObjectLifetimeErr { + #[primary_span] + pub span: Span, + pub repr: String, +} + +#[derive(Diagnostic)] +#[diag(passes::unrecognized_repr_hint, code = "E0552")] +#[help] +pub struct UnrecognizedReprHint { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +pub enum AttrApplication { + #[diag(passes::attr_application_enum, code = "E0517")] + Enum { + #[primary_span] + hint_span: Span, + #[label] + span: Span, + }, + #[diag(passes::attr_application_struct, code = "E0517")] + Struct { + #[primary_span] + hint_span: Span, + #[label] + span: Span, + }, + #[diag(passes::attr_application_struct_union, code = "E0517")] + StructUnion { + #[primary_span] + hint_span: Span, + #[label] + span: Span, + }, + #[diag(passes::attr_application_struct_enum_union, code = "E0517")] + StructEnumUnion { + #[primary_span] + hint_span: Span, + #[label] + span: Span, + }, + #[diag(passes::attr_application_struct_enum_function_union, code = "E0517")] + StructEnumFunctionUnion { + #[primary_span] + hint_span: Span, + #[label] + span: Span, + }, +} + +#[derive(Diagnostic)] +#[diag(passes::transparent_incompatible, code = "E0692")] +pub struct TransparentIncompatible { + #[primary_span] + pub hint_spans: Vec<Span>, + pub target: String, +} + +#[derive(Diagnostic)] +#[diag(passes::deprecated_attribute, code = "E0549")] +pub struct DeprecatedAttribute { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::useless_stability)] +pub struct UselessStability { + #[primary_span] + #[label] + pub span: Span, + #[label(passes::item)] + pub item_sp: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::invalid_stability)] +pub struct InvalidStability { + #[primary_span] + #[label] + pub span: Span, + #[label(passes::item)] + pub item_sp: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::cannot_stabilize_deprecated)] +pub struct CannotStabilizeDeprecated { + #[primary_span] + #[label] + pub span: Span, + #[label(passes::item)] + pub item_sp: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::invalid_deprecation_version)] +pub struct InvalidDeprecationVersion { + #[primary_span] + #[label] + pub span: Span, + #[label(passes::item)] + pub item_sp: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::missing_stability_attr)] +pub struct MissingStabilityAttr<'a> { + #[primary_span] + pub span: Span, + pub descr: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes::missing_const_stab_attr)] +pub struct MissingConstStabAttr<'a> { + #[primary_span] + pub span: Span, + pub descr: &'a str, +} + +#[derive(Diagnostic)] +#[diag(passes::trait_impl_const_stable)] +#[note] +pub struct TraitImplConstStable { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes::feature_only_on_nightly, code = "E0554")] +pub struct FeatureOnlyOnNightly { + #[primary_span] + pub span: Span, + pub release_channel: &'static str, +} + +#[derive(Diagnostic)] +#[diag(passes::unknown_feature, code = "E0635")] +pub struct UnknownFeature { + #[primary_span] + pub span: Span, + pub feature: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::implied_feature_not_exist)] +pub struct ImpliedFeatureNotExist { + #[primary_span] + pub span: Span, + pub feature: Symbol, + pub implied_by: Symbol, +} + +#[derive(Diagnostic)] +#[diag(passes::duplicate_feature_err, code = "E0636")] +pub struct DuplicateFeatureErr { + #[primary_span] + pub span: Span, + pub feature: Symbol, +} +#[derive(Diagnostic)] +#[diag(passes::missing_const_err)] +pub struct MissingConstErr { + #[primary_span] + #[help] + pub fn_sig_span: Span, + #[label] + pub const_span: Span, +} diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 79900a90aed..71b0735192a 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -8,9 +8,11 @@ //! * Functions called by the compiler itself. use crate::check_attr::target_from_impl_item; +use crate::errors::{ + DuplicateLangItem, IncorrectTarget, LangItemOnIncorrectTarget, UnknownLangItem, +}; use crate::weak_lang_items; -use rustc_errors::{pluralize, struct_span_err}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -18,10 +20,16 @@ use rustc_hir::lang_items::{extract, GenericRequirement, ITEM_REFS}; use rustc_hir::{HirId, LangItem, LanguageItems, Target}; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::ExternCrate; -use rustc_span::Span; +use rustc_span::{symbol::kw::Empty, Span}; use rustc_middle::ty::query::Providers; +pub(crate) enum Duplicate { + Plain, + Crate, + CrateDepends, +} + struct LanguageItemCollector<'tcx> { items: LanguageItems, tcx: TyCtxt<'tcx>, @@ -34,42 +42,24 @@ impl<'tcx> LanguageItemCollector<'tcx> { fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) { let attrs = self.tcx.hir().attrs(hir_id); - if let Some((value, span)) = extract(&attrs) { - match ITEM_REFS.get(&value).cloned() { + if let Some((name, span)) = extract(&attrs) { + match ITEM_REFS.get(&name).cloned() { // Known lang item with attribute on correct target. Some((item_index, expected_target)) if actual_target == expected_target => { self.collect_item_extended(item_index, hir_id, span); } // Known lang item with attribute on incorrect target. Some((_, expected_target)) => { - struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(LangItemOnIncorrectTarget { span, - E0718, - "`{}` language item must be applied to a {}", - value, + name, expected_target, - ) - .span_label( - span, - format!( - "attribute should be applied to a {}, not a {}", - expected_target, actual_target, - ), - ) - .emit(); + actual_target, + }); } // Unknown lang item. _ => { - struct_span_err!( - self.tcx.sess, - span, - E0522, - "definition of an unknown language item: `{}`", - value - ) - .span_label(span, format!("definition of unknown language item `{}`", value)) - .emit(); + self.tcx.sess.emit_err(UnknownLangItem { span, name }); } } } @@ -79,74 +69,72 @@ impl<'tcx> LanguageItemCollector<'tcx> { // Check for duplicates. if let Some(original_def_id) = self.items.items[item_index] { if original_def_id != item_def_id { - let lang_item = LangItem::from_u32(item_index as u32).unwrap(); - let name = lang_item.name(); - let mut err = match self.tcx.hir().span_if_local(item_def_id) { - Some(span) => struct_span_err!( - self.tcx.sess, - span, - E0152, - "found duplicate lang item `{}`", - name - ), - None => match self.tcx.extern_crate(item_def_id) { - Some(ExternCrate { dependency_of, .. }) => { - self.tcx.sess.struct_err(&format!( - "duplicate lang item in crate `{}` (which `{}` depends on): `{}`.", - self.tcx.crate_name(item_def_id.krate), - self.tcx.crate_name(*dependency_of), - name - )) - } - _ => self.tcx.sess.struct_err(&format!( - "duplicate lang item in crate `{}`: `{}`.", - self.tcx.crate_name(item_def_id.krate), - name - )), - }, + let local_span = self.tcx.hir().span_if_local(item_def_id); + let lang_item_name = LangItem::from_u32(item_index as u32).unwrap().name(); + let crate_name = self.tcx.crate_name(item_def_id.krate); + let mut dependency_of = Empty; + let is_local = item_def_id.is_local(); + let path = if is_local { + String::new() + } else { + self.tcx + .crate_extern_paths(item_def_id.krate) + .iter() + .map(|p| p.display().to_string()) + .collect::<Vec<_>>() + .join(", ") + .into() }; - if let Some(span) = self.tcx.hir().span_if_local(original_def_id) { - err.span_note(span, "the lang item is first defined here"); + let first_defined_span = self.tcx.hir().span_if_local(original_def_id); + let mut orig_crate_name = Empty; + let mut orig_dependency_of = Empty; + let orig_is_local = original_def_id.is_local(); + let orig_path = if orig_is_local { + String::new() } else { - match self.tcx.extern_crate(original_def_id) { - Some(ExternCrate { dependency_of, .. }) => { - err.note(&format!( - "the lang item is first defined in crate `{}` (which `{}` depends on)", - self.tcx.crate_name(original_def_id.krate), - self.tcx.crate_name(*dependency_of) - )); - } - _ => { - err.note(&format!( - "the lang item is first defined in crate `{}`.", - self.tcx.crate_name(original_def_id.krate) - )); - } + self.tcx + .crate_extern_paths(original_def_id.krate) + .iter() + .map(|p| p.display().to_string()) + .collect::<Vec<_>>() + .join(", ") + .into() + }; + if first_defined_span.is_none() { + orig_crate_name = self.tcx.crate_name(original_def_id.krate); + if let Some(ExternCrate { dependency_of: inner_dependency_of, .. }) = + self.tcx.extern_crate(original_def_id) + { + orig_dependency_of = self.tcx.crate_name(*inner_dependency_of); } - let mut note_def = |which, def_id: DefId| { - let crate_name = self.tcx.crate_name(def_id.krate); - let note = if def_id.is_local() { - format!("{} definition in the local crate (`{}`)", which, crate_name) - } else { - let paths: Vec<_> = self - .tcx - .crate_extern_paths(def_id.krate) - .iter() - .map(|p| p.display().to_string()) - .collect(); - format!( - "{} definition in `{}` loaded from {}", - which, - crate_name, - paths.join(", ") - ) - }; - err.note(¬e); - }; - note_def("first", original_def_id); - note_def("second", item_def_id); } - err.emit(); + + let duplicate = if local_span.is_some() { + Duplicate::Plain + } else { + match self.tcx.extern_crate(item_def_id) { + Some(ExternCrate { dependency_of: inner_dependency_of, .. }) => { + dependency_of = self.tcx.crate_name(*inner_dependency_of); + Duplicate::CrateDepends + } + _ => Duplicate::Crate, + } + }; + + self.tcx.sess.emit_err(DuplicateLangItem { + local_span, + lang_item_name, + crate_name, + dependency_of, + is_local, + path, + first_defined_span, + orig_crate_name, + orig_dependency_of, + orig_is_local, + orig_path, + duplicate, + }); } } @@ -179,41 +167,30 @@ impl<'tcx> LanguageItemCollector<'tcx> { None => (0, *item_span), }; + let mut at_least = false; let required = match lang_item.required_generics() { - GenericRequirement::Exact(num) if num != actual_num => { - Some((format!("{}", num), pluralize!(num))) - } + GenericRequirement::Exact(num) if num != actual_num => Some(num), GenericRequirement::Minimum(num) if actual_num < num => { - Some((format!("at least {}", num), pluralize!(num))) - } + at_least = true; + Some(num)} + , // If the number matches, or there is no requirement, handle it normally _ => None, }; - if let Some((range_str, pluralized)) = required { + if let Some(num) = required { // We are issuing E0718 "incorrect target" here, because while the // item kind of the target is correct, the target is still wrong // because of the wrong number of generic arguments. - struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(IncorrectTarget { span, - E0718, - "`{}` language item must be applied to a {} with {} generic argument{}", - name, - kind.descr(), - range_str, - pluralized, - ) - .span_label( generics_span, - format!( - "this {} has {} generic argument{}", - kind.descr(), - actual_num, - pluralize!(actual_num), - ), - ) - .emit(); + name: name.as_str(), + kind: kind.descr(), + num, + actual_num, + at_least, + }); // return early to not collect the lang item return; diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 46c4a702fde..c1085094962 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -3,10 +3,13 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::abi::{HasDataLayout, TargetDataLayout}; +use crate::errors::{Abi, Align, HomogeneousAggregate, LayoutOf, Size, UnrecognizedField}; + pub fn test_layout(tcx: TyCtxt<'_>) { if tcx.features().rustc_attrs { // if the `rustc_attrs` feature is not enabled, don't bother testing layout @@ -35,62 +38,64 @@ fn dump_layout_of<'tcx>(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, attr: &Attri for meta_item in meta_items { match meta_item.name_or_empty() { sym::abi => { - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!("abi: {:?}", ty_layout.abi), - ); + tcx.sess.emit_err(Abi { + span: tcx.def_span(item_def_id.to_def_id()), + abi: format!("{:?}", ty_layout.abi), + }); } sym::align => { - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!("align: {:?}", ty_layout.align), - ); + tcx.sess.emit_err(Align { + span: tcx.def_span(item_def_id.to_def_id()), + align: format!("{:?}", ty_layout.align), + }); } sym::size => { - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!("size: {:?}", ty_layout.size), - ); + tcx.sess.emit_err(Size { + span: tcx.def_span(item_def_id.to_def_id()), + size: format!("{:?}", ty_layout.size), + }); } sym::homogeneous_aggregate => { - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!( - "homogeneous_aggregate: {:?}", - ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }), + tcx.sess.emit_err(HomogeneousAggregate { + span: tcx.def_span(item_def_id.to_def_id()), + homogeneous_aggregate: format!( + "{:?}", + ty_layout.homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }) ), - ); + }); } sym::debug => { - let normalized_ty = tcx.normalize_erasing_regions( - param_env.with_reveal_all_normalized(tcx), - ty, - ); - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout), + let normalized_ty = format!( + "{:?}", + tcx.normalize_erasing_regions( + param_env.with_reveal_all_normalized(tcx), + ty, + ) ); + let ty_layout = format!("{:#?}", *ty_layout); + tcx.sess.emit_err(LayoutOf { + span: tcx.def_span(item_def_id.to_def_id()), + normalized_ty, + ty_layout, + }); } name => { - tcx.sess.span_err( - meta_item.span(), - &format!("unrecognized field name `{}`", name), - ); + tcx.sess.emit_err(UnrecognizedField { span: meta_item.span(), name }); } } } } Err(layout_error) => { - tcx.sess.span_err( - tcx.def_span(item_def_id.to_def_id()), - &format!("layout error: {:?}", layout_error), - ); + tcx.sess.emit_fatal(Spanned { + node: layout_error, + span: tcx.def_span(item_def_id.to_def_id()), + }); } } } diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 6e621b7eb5e..15f60f626c8 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -5,6 +5,8 @@ //! This API is completely unstable and subject to change. #![allow(rustc::potential_query_instability)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(iter_intersperse)] #![feature(let_chains)] diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 04173c792a9..b5843c0ae48 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -6,7 +6,6 @@ use rustc_ast::{Attribute, MetaItemKind}; use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER}; -use rustc_errors::struct_span_err; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::LibFeatures; @@ -15,6 +14,8 @@ use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; +use crate::errors::{FeaturePreviouslyDeclared, FeatureStableTwice}; + fn new_lib_features() -> LibFeatures { LibFeatures { stable: Default::default(), unstable: Default::default() } } @@ -92,14 +93,12 @@ impl<'tcx> LibFeatureCollector<'tcx> { (Some(since), _, false) => { if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) { if *prev_since != since { - self.span_feature_error( + self.tcx.sess.emit_err(FeatureStableTwice { span, - &format!( - "feature `{}` is declared stable since {}, \ - but was previously declared stable since {}", - feature, since, prev_since, - ), - ); + feature, + since, + prev_since: *prev_since, + }); return; } } @@ -110,22 +109,17 @@ impl<'tcx> LibFeatureCollector<'tcx> { self.lib_features.unstable.insert(feature, span); } (Some(_), _, true) | (None, true, _) => { - self.span_feature_error( + let declared = if since.is_some() { "stable" } else { "unstable" }; + let prev_declared = if since.is_none() { "stable" } else { "unstable" }; + self.tcx.sess.emit_err(FeaturePreviouslyDeclared { span, - &format!( - "feature `{}` is declared {}, but was previously declared {}", - feature, - if since.is_some() { "stable" } else { "unstable" }, - if since.is_none() { "stable" } else { "unstable" }, - ), - ); + feature, + declared, + prev_declared, + }); } } } - - fn span_feature_error(&self, span: Span, msg: &str) { - struct_span_err!(self.tcx.sess, span, E0711, "{}", &msg).emit(); - } } impl<'tcx> Visitor<'tcx> for LibFeatureCollector<'tcx> { diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index cdda0e388dd..077194ec687 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -1,6 +1,5 @@ use Context::*; -use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; @@ -13,6 +12,11 @@ use rustc_session::Session; use rustc_span::hygiene::DesugaringKind; use rustc_span::Span; +use crate::errors::{ + BreakInsideAsyncBlock, BreakInsideClosure, BreakNonLoop, ContinueLabeledBlock, OutsideLoop, + UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock, +}; + #[derive(Clone, Copy, Debug, PartialEq)] enum Context { Normal, @@ -90,7 +94,10 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { Ok(loop_id) => Some(loop_id), Err(hir::LoopIdError::OutsideLoopScope) => None, Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { - self.emit_unlabled_cf_in_while_condition(e.span, "break"); + self.sess.emit_err(UnlabeledCfInWhileCondition { + span: e.span, + cf_type: "break", + }); None } Err(hir::LoopIdError::UnresolvedLabel) => None, @@ -116,69 +123,22 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { match loop_kind { None | Some(hir::LoopSource::Loop) => (), Some(kind) => { - let mut err = struct_span_err!( - self.sess, - e.span, - E0571, - "`break` with value from a `{}` loop", - kind.name() - ); - err.span_label( - e.span, - "can only break with a value inside `loop` or breakable block", + let suggestion = format!( + "break{}", + break_label + .label + .map_or_else(String::new, |l| format!(" {}", l.ident)) ); - if let Some(head) = head { - err.span_label( - head, - &format!( - "you can't `break` with a value in a `{}` loop", - kind.name() - ), - ); - } - err.span_suggestion( - e.span, - &format!( - "use `break` on its own without a value inside this `{}` loop", - kind.name(), - ), - format!( - "break{}", - break_label - .label - .map_or_else(String::new, |l| format!(" {}", l.ident)) - ), - Applicability::MaybeIncorrect, - ); - if let (Some(label), None) = (loop_label, break_label.label) { - match break_expr.kind { - hir::ExprKind::Path(hir::QPath::Resolved( - None, - hir::Path { - segments: [segment], - res: hir::def::Res::Err, - .. - }, - )) if label.ident.to_string() - == format!("'{}", segment.ident) => - { - // This error is redundant, we will have already emitted a - // suggestion to use the label when `segment` wasn't found - // (hence the `Res::Err` check). - err.delay_as_bug(); - } - _ => { - err.span_suggestion( - break_expr.span, - "alternatively, you might have meant to use the \ - available loop label", - label.ident, - Applicability::MaybeIncorrect, - ); - } - } - } - err.emit(); + self.sess.emit_err(BreakNonLoop { + span: e.span, + head, + kind: kind.name(), + suggestion, + loop_label, + break_label: break_label.label, + break_expr_kind: &break_expr.kind, + break_expr_span: break_expr.span, + }); } } } @@ -191,19 +151,17 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { match destination.target_id { Ok(loop_id) => { if let Node::Block(block) = self.hir_map.find(loop_id).unwrap() { - struct_span_err!( - self.sess, - e.span, - E0696, - "`continue` pointing to a labeled block" - ) - .span_label(e.span, "labeled blocks cannot be `continue`'d") - .span_label(block.span, "labeled block the `continue` points to") - .emit(); + self.sess.emit_err(ContinueLabeledBlock { + span: e.span, + block_span: block.span, + }); } } Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { - self.emit_unlabled_cf_in_while_condition(e.span, "continue"); + self.sess.emit_err(UnlabeledCfInWhileCondition { + span: e.span, + cf_type: "continue", + }); } Err(_) => {} } @@ -226,21 +184,16 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { } fn require_break_cx(&self, name: &str, span: Span) { - let err_inside_of = |article, ty, closure_span| { - struct_span_err!(self.sess, span, E0267, "`{}` inside of {} {}", name, article, ty) - .span_label(span, format!("cannot `{}` inside of {} {}", name, article, ty)) - .span_label(closure_span, &format!("enclosing {}", ty)) - .emit(); - }; - match self.cx { LabeledBlock | Loop(_) => {} - Closure(closure_span) => err_inside_of("a", "closure", closure_span), - AsyncClosure(closure_span) => err_inside_of("an", "`async` block", closure_span), + Closure(closure_span) => { + self.sess.emit_err(BreakInsideClosure { span, closure_span, name }); + } + AsyncClosure(closure_span) => { + self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name }); + } Normal | AnonConst => { - struct_span_err!(self.sess, span, E0268, "`{}` outside of a loop", name) - .span_label(span, format!("cannot `{}` outside of a loop", name)) - .emit(); + self.sess.emit_err(OutsideLoop { span, name }); } } } @@ -251,37 +204,13 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { label: &Destination, cf_type: &str, ) -> bool { - if !span.is_desugaring(DesugaringKind::QuestionMark) && self.cx == LabeledBlock { - if label.label.is_none() { - struct_span_err!( - self.sess, - span, - E0695, - "unlabeled `{}` inside of a labeled block", - cf_type - ) - .span_label( - span, - format!( - "`{}` statements that would diverge to or through \ - a labeled block need to bear a label", - cf_type - ), - ) - .emit(); - return true; - } + if !span.is_desugaring(DesugaringKind::QuestionMark) + && self.cx == LabeledBlock + && label.label.is_none() + { + self.sess.emit_err(UnlabeledInLabeledBlock { span, cf_type }); + return true; } false } - fn emit_unlabled_cf_in_while_condition(&mut self, span: Span, cf_type: &str) { - struct_span_err!( - self.sess, - span, - E0590, - "`break` or `continue` with no label in the condition of a `while` loop" - ) - .span_label(span, format!("unlabeled `{}` in the condition of a `while` loop", cf_type)) - .emit(); - } } diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 2690be66c21..acc54e7e110 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -1,7 +1,6 @@ //! Checks validity of naked functions. use rustc_ast::InlineAsmOptions; -use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; @@ -14,6 +13,12 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi; +use crate::errors::{ + CannotInlineNakedFunction, NakedFunctionsAsmBlock, NakedFunctionsAsmOptions, + NakedFunctionsMustUseNoreturn, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed, + UndefinedNakedFunctionAbi, +}; + pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_naked_functions, ..*providers }; } @@ -56,7 +61,7 @@ fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { fn check_inline(tcx: TyCtxt<'_>, def_id: LocalDefId) { let attrs = tcx.get_attrs(def_id.to_def_id(), sym::inline); for attr in attrs { - tcx.sess.struct_span_err(attr.span, "naked functions cannot be inlined").emit(); + tcx.sess.emit_err(CannotInlineNakedFunction { span: attr.span }); } } @@ -65,12 +70,11 @@ fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) { if abi == Abi::Rust { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let span = tcx.def_span(def_id); - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( UNDEFINED_NAKED_FUNCTION_ABI, hir_id, span, - "Rust ABI is unsupported in naked functions", - |lint| lint, + UndefinedNakedFunctionAbi, ); } } @@ -82,12 +86,7 @@ fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) { hir::PatKind::Wild | hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {} _ => { - tcx.sess - .struct_span_err( - param.pat.span, - "patterns not allowed in naked function parameters", - ) - .emit(); + tcx.sess.emit_err(NoPatterns { span: param.pat.span }); } } } @@ -117,14 +116,7 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { )) = expr.kind { if self.params.contains(var_hir_id) { - self.tcx - .sess - .struct_span_err( - expr.span, - "referencing function parameters is not allowed in naked functions", - ) - .help("follow the calling convention in asm block to use parameters") - .emit(); + self.tcx.sess.emit_err(ParamsNotAllowed { span: expr.span }); return; } } @@ -139,26 +131,21 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body< if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] { // Ok. } else { - let mut diag = struct_span_err!( - tcx.sess, - tcx.def_span(def_id), - E0787, - "naked functions must contain a single asm block" - ); - let mut must_show_error = false; let mut has_asm = false; let mut has_err = false; + let mut multiple_asms = vec![]; + let mut non_asms = vec![]; for &(kind, span) in &this.items { match kind { ItemKind::Asm if has_asm => { must_show_error = true; - diag.span_label(span, "multiple asm blocks are unsupported in naked functions"); + multiple_asms.push(span); } ItemKind::Asm => has_asm = true, ItemKind::NonAsm => { must_show_error = true; - diag.span_label(span, "non-asm is unsupported in naked functions"); + non_asms.push(span); } ItemKind::Err => has_err = true, } @@ -168,9 +155,11 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body< // errors, then don't show an additional error. This allows for appending/prepending // `compile_error!("...")` statements and reduces error noise. if must_show_error || !has_err { - diag.emit(); - } else { - diag.cancel(); + tcx.sess.emit_err(NakedFunctionsAsmBlock { + span: tcx.def_span(def_id), + multiple_asms, + non_asms, + }); } } } @@ -251,13 +240,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> { }) .collect(); if !unsupported_operands.is_empty() { - struct_span_err!( - self.tcx.sess, - unsupported_operands, - E0787, - "only `const` and `sym` operands are supported in naked functions", - ) - .emit(); + self.tcx.sess.emit_err(NakedFunctionsOperands { unsupported_operands }); } let unsupported_options: Vec<&'static str> = [ @@ -273,14 +256,10 @@ impl<'tcx> CheckInlineAssembly<'tcx> { .collect(); if !unsupported_options.is_empty() { - struct_span_err!( - self.tcx.sess, + self.tcx.sess.emit_err(NakedFunctionsAsmOptions { span, - E0787, - "asm options unsupported in naked functions: {}", - unsupported_options.join(", ") - ) - .emit(); + unsupported_options: unsupported_options.join(", "), + }); } if !asm.options.contains(InlineAsmOptions::NORETURN) { @@ -290,20 +269,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> { .map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1) .shrink_to_hi(); - struct_span_err!( - self.tcx.sess, - span, - E0787, - "asm in naked functions must use `noreturn` option" - ) - .span_suggestion( - last_span, - "consider specifying that the asm block is responsible \ - for returning from the function", - ", options(noreturn)", - Applicability::MachineApplicable, - ) - .emit(); + self.tcx.sess.emit_err(NakedFunctionsMustUseNoreturn { span, last_span }); } } } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 34fa80228df..cfd6acd8d7c 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -1,13 +1,18 @@ //! A pass that annotates every item and method with its stability level, //! propagating default levels lexically from parent to children ast nodes. -use crate::errors; +use crate::errors::{ + self, CannotStabilizeDeprecated, DeprecatedAttribute, DuplicateFeatureErr, + FeatureOnlyOnNightly, ImpliedFeatureNotExist, InvalidDeprecationVersion, InvalidStability, + MissingConstErr, MissingConstStabAttr, MissingStabilityAttr, TraitImplConstStable, + UnknownFeature, UselessStability, +}; use rustc_attr::{ self as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable, UnstableReason, VERSION_PLACEHOLDER, }; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_errors::{struct_span_err, Applicability}; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; @@ -20,7 +25,6 @@ use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index}; use rustc_middle::ty::{query::Providers, TyCtxt}; use rustc_session::lint; use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED}; -use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_target::spec::abi::Abi; @@ -179,7 +183,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if !self.in_trait_impl || (self.in_trait_impl && !self.tcx.is_const_fn_raw(def_id.to_def_id())) { - missing_const_err(&self.tcx.sess, fn_sig.span, const_span); + self.tcx + .sess + .emit_err(MissingConstErr { fn_sig_span: fn_sig.span, const_span }); } } } @@ -197,14 +203,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr { if stab.is_none() { - struct_span_err!( - self.tcx.sess, - *span, - E0549, - "deprecated attribute must be paired with \ - either stable or unstable attribute" - ) - .emit(); + self.tcx.sess.emit_err(DeprecatedAttribute { span: *span }); } } @@ -220,10 +219,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if kind == AnnotationKind::Prohibited || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated) { - self.tcx.sess.struct_span_err(span,"this stability annotation is useless") - .span_label(span, "useless stability annotation") - .span_label(item_sp, "the stability attribute annotates this item") - .emit(); + self.tcx.sess.emit_err(UselessStability { span, item_sp }); } debug!("annotate: found {:?}", stab); @@ -239,19 +235,15 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { { match stab_v.parse::<u64>() { Err(_) => { - self.tcx.sess.struct_span_err(span, "invalid stability version found") - .span_label(span, "invalid stability version") - .span_label(item_sp, "the stability attribute annotates this item") - .emit(); + self.tcx.sess.emit_err(InvalidStability { span, item_sp }); break; } Ok(stab_vp) => match dep_v.parse::<u64>() { Ok(dep_vp) => match dep_vp.cmp(&stab_vp) { Ordering::Less => { - self.tcx.sess.struct_span_err(span, "an API can't be stabilized after it is deprecated") - .span_label(span, "invalid version") - .span_label(item_sp, "the stability attribute annotates this item") - .emit(); + self.tcx + .sess + .emit_err(CannotStabilizeDeprecated { span, item_sp }); break; } Ordering::Equal => continue, @@ -259,10 +251,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { }, Err(_) => { if dep_v != "TBD" { - self.tcx.sess.struct_span_err(span, "invalid deprecation version found") - .span_label(span, "invalid deprecation version") - .span_label(item_sp, "the stability attribute annotates this item") - .emit(); + self.tcx + .sess + .emit_err(InvalidDeprecationVersion { span, item_sp }); } break; } @@ -271,7 +262,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = stab { + if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = + stab + { self.index.implications.insert(implied_by, feature); } @@ -531,7 +524,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { let stab = self.tcx.stability().local_stability(def_id); if !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(def_id) { let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); - self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr)); + self.tcx.sess.emit_err(MissingStabilityAttr { span, descr }); } } @@ -551,7 +544,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { if is_const && is_stable && missing_const_stability_attribute && is_reachable { let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); - self.tcx.sess.span_err(span, &format!("{descr} has missing const stability attribute")); + self.tcx.sess.emit_err(MissingConstStabAttr { span, descr }); } } } @@ -764,11 +757,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { && *constness == hir::Constness::Const && const_stab.map_or(false, |(stab, _)| stab.is_const_stable()) { - self.tcx - .sess - .struct_span_err(item.span, "trait implementations cannot be const stable yet") - .note("see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information") - .emit(); + self.tcx.sess.emit_err(TraitImplConstStable { span: item.span }); } } @@ -929,7 +918,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { } if !lang_features.insert(feature) { // Warn if the user enables a lang feature multiple times. - duplicate_feature_err(tcx.sess, span, feature); + tcx.sess.emit_err(DuplicateFeatureErr { span, feature }); } } @@ -937,18 +926,14 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { let mut remaining_lib_features = FxIndexMap::default(); for (feature, span) in declared_lib_features { if !tcx.sess.opts.unstable_features.is_nightly_build() { - struct_span_err!( - tcx.sess, - *span, - E0554, - "`#![feature]` may not be used on the {} release channel", - env!("CFG_RELEASE_CHANNEL") - ) - .emit(); + tcx.sess.emit_err(FeatureOnlyOnNightly { + span: *span, + release_channel: env!("CFG_RELEASE_CHANNEL"), + }); } if remaining_lib_features.contains_key(&feature) { // Warn if the user enables a lib feature multiple times. - duplicate_feature_err(tcx.sess, *span, *feature); + tcx.sess.emit_err(DuplicateFeatureErr { span: *span, feature: *feature }); } remaining_lib_features.insert(feature, *span); } @@ -1049,23 +1034,18 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { } for (feature, span) in remaining_lib_features { - struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit(); + tcx.sess.emit_err(UnknownFeature { span, feature: *feature }); } for (implied_by, feature) in remaining_implications { let local_defined_features = tcx.lib_features(()); - let span = local_defined_features + let span = *local_defined_features .stable .get(&feature) .map(|(_, span)| span) .or_else(|| local_defined_features.unstable.get(&feature)) .expect("feature that implied another does not exist"); - tcx.sess - .struct_span_err( - *span, - format!("feature `{implied_by}` implying `{feature}` does not exist"), - ) - .emit(); + tcx.sess.emit_err(ImpliedFeatureNotExist { span, feature, implied_by }); } // FIXME(#44232): the `used_features` table no longer exists, so we @@ -1088,21 +1068,20 @@ fn unnecessary_partially_stable_feature_lint( by the feature `{implies}`" ), |lint| { - lint - .span_suggestion( - span, - &format!( + lint.span_suggestion( + span, + &format!( "if you are using features which are still unstable, change to using `{implies}`" ), - implies, - Applicability::MaybeIncorrect, - ) - .span_suggestion( - tcx.sess.source_map().span_extend_to_line(span), - "if you are using features which are now stable, remove this line", - "", - Applicability::MaybeIncorrect, - ) + implies, + Applicability::MaybeIncorrect, + ) + .span_suggestion( + tcx.sess.source_map().span_extend_to_line(span), + "if you are using features which are now stable, remove this line", + "", + Applicability::MaybeIncorrect, + ) }, ); } @@ -1120,20 +1099,3 @@ fn unnecessary_stable_feature_lint( lint }); } - -fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) { - struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature) - .emit(); -} - -fn missing_const_err(session: &Session, fn_sig_span: Span, const_span: Span) { - const ERROR_MSG: &'static str = "attributes `#[rustc_const_unstable]` \ - and `#[rustc_const_stable]` require \ - the function or method to be `const`"; - - session - .struct_span_err(fn_sig_span, ERROR_MSG) - .span_help(fn_sig_span, "make the function or method const") - .span_label(const_span, "attribute specified here") - .emit(); -} diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index c48b4ecf87a..92024989a75 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -1,13 +1,17 @@ //! Validity checking for weak lang items use rustc_data_structures::fx::FxHashSet; -use rustc_errors::struct_span_err; use rustc_hir::lang_items::{self, LangItem}; use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS; use rustc_middle::middle::lang_items::required; use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; +use crate::errors::{ + AllocFuncRequired, MissingAllocErrorHandler, MissingLangItem, MissingPanicHandler, + UnknownExternLangItem, +}; + /// Checks the crate for usage of weak lang items, returning a vector of all the /// language items required by this crate, but not defined yet. pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItems) { @@ -31,14 +35,7 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem } } else { let span = tcx.def_span(id.def_id); - struct_span_err!( - tcx.sess, - span, - E0264, - "unknown external lang item: `{}`", - lang_item - ) - .emit(); + tcx.sess.emit_err(UnknownExternLangItem { span, lang_item }); } } } @@ -71,20 +68,14 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { for (name, &item) in WEAK_ITEMS_REFS.iter() { if missing.contains(&item) && required(tcx, item) && items.require(item).is_err() { if item == LangItem::PanicImpl { - tcx.sess.err("`#[panic_handler]` function required, but not found"); + tcx.sess.emit_err(MissingPanicHandler); } else if item == LangItem::Oom { if !tcx.features().default_alloc_error_handler { - tcx.sess.err("`#[alloc_error_handler]` function required, but not found"); - tcx.sess.note_without_error("use `#![feature(default_alloc_error_handler)]` for a default error handler"); + tcx.sess.emit_err(AllocFuncRequired); + tcx.sess.emit_note(MissingAllocErrorHandler); } } else { - tcx - .sess - .diagnostic() - .struct_err(&format!("language item required, but not found: `{}`", name)) - .note(&format!("this can occur when a binary crate with `#![no_std]` is compiled for a target where `{}` is defined in the standard library", name)) - .help(&format!("you may be able to compile for a target that doesn't need `{}`, specify a target with `--target` or in `.cargo/config`", name)) - .emit(); + tcx.sess.emit_err(MissingLangItem { name: *name }); } } } diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index 8602a4cf5ae..1e74e0e2990 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -1,19 +1,15 @@ -use rustc_errors::AddToDiagnostic; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::Limit; use rustc_span::{Span, Symbol}; +#[derive(Subdiagnostic)] +#[note(query_system::cycle_stack_middle)] pub struct CycleStack { + #[primary_span] pub span: Span, pub desc: String, } -impl AddToDiagnostic for CycleStack { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { - diag.span_note(self.span, &format!("...which requires {}...", self.desc)); - } -} - #[derive(Copy, Clone)] pub enum HandleCycleError { Error, @@ -53,7 +49,7 @@ pub struct Cycle { #[primary_span] pub span: Span, pub stack_bottom: String, - #[subdiagnostic] + #[subdiagnostic(eager)] pub cycle_stack: Vec<CycleStack>, #[subdiagnostic] pub stack_count: StackCount, diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 8f6da73d1f2..f47760e9ae6 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -4,7 +4,7 @@ #![feature(min_specialization)] #![feature(extern_types)] #![allow(rustc::potential_query_instability)] -// #![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 83aae286402..c4644d4f076 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -139,8 +139,7 @@ impl<'a> Resolver<'a> { &candidates, if instead { Instead::Yes } else { Instead::No }, found_use, - IsPattern::No, - IsImport::No, + DiagnosticMode::Normal, path, ); err.emit(); @@ -699,8 +698,7 @@ impl<'a> Resolver<'a> { &import_suggestions, Instead::No, FoundUse::Yes, - IsPattern::Yes, - IsImport::No, + DiagnosticMode::Pattern, vec![], ); } @@ -1496,8 +1494,7 @@ impl<'a> Resolver<'a> { &import_suggestions, Instead::No, FoundUse::Yes, - IsPattern::No, - IsImport::No, + DiagnosticMode::Normal, vec![], ); @@ -2458,18 +2455,13 @@ enum FoundUse { No, } -/// Whether a binding is part of a pattern or an expression. Used for diagnostics. -enum IsPattern { +/// Whether a binding is part of a pattern or a use statement. Used for diagnostics. +enum DiagnosticMode { + Normal, /// The binding is part of a pattern - Yes, - /// The binding is part of an expression - No, -} - -/// Whether a binding is part of a use statement. Used for diagnostics. -enum IsImport { - Yes, - No, + Pattern, + /// The binding is part of a use statement + Import, } pub(crate) fn import_candidates( @@ -2488,8 +2480,7 @@ pub(crate) fn import_candidates( candidates, Instead::Yes, FoundUse::Yes, - IsPattern::No, - IsImport::Yes, + DiagnosticMode::Import, vec![], ); } @@ -2506,8 +2497,7 @@ fn show_candidates( candidates: &[ImportSuggestion], instead: Instead, found_use: FoundUse, - is_pattern: IsPattern, - is_import: IsImport, + mode: DiagnosticMode, path: Vec<Segment>, ) { if candidates.is_empty() { @@ -2542,7 +2532,7 @@ fn show_candidates( }; let instead = if let Instead::Yes = instead { " instead" } else { "" }; - let mut msg = if let IsPattern::Yes = is_pattern { + let mut msg = if let DiagnosticMode::Pattern = mode { format!( "if you meant to match on {}{}{}, use the full path in the pattern", kind, instead, name @@ -2555,19 +2545,24 @@ fn show_candidates( err.note(note); } - if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) { - err.span_suggestions( - span, - &msg, - accessible_path_strings.into_iter().map(|a| a.0), - Applicability::MaybeIncorrect, - ); - } else if let Some(span) = use_placement_span { + if let Some(span) = use_placement_span { + let add_use = match mode { + DiagnosticMode::Pattern => { + err.span_suggestions( + span, + &msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + ); + return; + } + DiagnosticMode::Import => "", + DiagnosticMode::Normal => "use ", + }; for candidate in &mut accessible_path_strings { // produce an additional newline to separate the new use statement // from the directly following item. let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" }; - let add_use = if let IsImport::Yes = is_import { "" } else { "use " }; candidate.0 = format!("{}{};\n{}", add_use, &candidate.0, additional_newline); } @@ -2598,11 +2593,14 @@ fn show_candidates( err.note(&msg); } - } else if matches!(is_import, IsImport::No) { + } else if !matches!(mode, DiagnosticMode::Import) { assert!(!inaccessible_path_strings.is_empty()); - let prefix = - if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" }; + let prefix = if let DiagnosticMode::Pattern = mode { + "you might have meant to match on " + } else { + "" + }; if inaccessible_path_strings.len() == 1 { let (name, descr, def_id, note) = &inaccessible_path_strings[0]; let msg = format!( @@ -2610,7 +2608,7 @@ fn show_candidates( prefix, descr, name, - if let IsPattern::Yes = is_pattern { ", which" } else { "" } + if let DiagnosticMode::Pattern = mode { ", which" } else { "" } ); if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 8d527c05122..102df3a4d7e 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1075,12 +1075,11 @@ mod parse { options! { CodegenOptions, CG_OPTIONS, cgopts, "C", "codegen", - // This list is in alphabetical order. - // // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs // - src/doc/rustc/src/codegen-options/index.md + // tidy-alphabetical-start ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing"), #[rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field")] @@ -1195,9 +1194,8 @@ options! { target_feature: String = (String::new(), parse_target_feature, [TRACKED], "target specific attributes. (`rustc --print target-features` for details). \ This feature is unsafe."), + // tidy-alphabetical-end - // This list is in alphabetical order. - // // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs // - src/doc/rustc/src/codegen-options/index.md @@ -1206,24 +1204,23 @@ options! { options! { UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable", - // This list is in alphabetical order. - // // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs // - src/doc/unstable-book/src/compiler-flags + // tidy-alphabetical-start allow_features: Option<Vec<String>> = (None, parse_opt_comma_list, [TRACKED], "only allow the listed language features to be enabled in code (space separated)"), always_encode_mir: bool = (false, parse_bool, [TRACKED], "encode MIR of all functions into the crate metadata (default: no)"), - assume_incomplete_release: bool = (false, parse_bool, [TRACKED], - "make cfg(version) treat the current version as incomplete (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field")] asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior) (default: no)"), assert_incr_state: Option<String> = (None, parse_opt_string, [UNTRACKED], "assert that the incremental cache is in given state: \ either `loaded` or `not-loaded`."), + assume_incomplete_release: bool = (false, parse_bool, [TRACKED], + "make cfg(version) treat the current version as incomplete (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field")] binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ @@ -1256,6 +1253,8 @@ options! { dep_tasks: bool = (false, parse_bool, [UNTRACKED], "print tasks that execute and the color their dep node gets (requires debug build) \ (default: no)"), + diagnostic_width: Option<usize> = (None, parse_opt_number, [UNTRACKED], + "set the current output width for diagnostic truncation"), dlltool: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED], "import library generation tool (windows-gnu only)"), dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], @@ -1337,16 +1336,16 @@ options! { "hash spans relative to their parent item for incr. comp. (default: no)"), incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], "verify incr. comp. hashes of green query instances (default: no)"), + inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED], + "control whether `#[inline]` functions are in all CGUs"), inline_llvm: bool = (true, parse_bool, [TRACKED], "enable LLVM inlining (default: yes)"), inline_mir: Option<bool> = (None, parse_opt_bool, [TRACKED], "enable MIR inlining (default: no)"), - inline_mir_threshold: Option<usize> = (None, parse_opt_number, [TRACKED], - "a default MIR inlining threshold (default: 50)"), inline_mir_hint_threshold: Option<usize> = (None, parse_opt_number, [TRACKED], "inlining threshold for functions with inline hint (default: 100)"), - inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED], - "control whether `#[inline]` functions are in all CGUs"), + inline_mir_threshold: Option<usize> = (None, parse_opt_number, [TRACKED], + "a default MIR inlining threshold (default: 50)"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] @@ -1363,6 +1362,8 @@ options! { "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], "keep hygiene data after analysis (default: no)"), + layout_seed: Option<u64> = (None, parse_opt_number, [TRACKED], + "seed layout randomization"), link_native_libraries: bool = (true, parse_bool, [UNTRACKED], "link native libraries in the linker invocation (default: yes)"), link_only: bool = (false, parse_bool, [TRACKED], @@ -1392,11 +1393,11 @@ options! { "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \ enabled, overriding all other checks. Passes that are not specified are enabled or \ disabled by other flags as usual."), - mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED], - "use line numbers relative to the function in mir pretty printing"), #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")] mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), + mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED], + "use line numbers relative to the function in mir pretty printing"), move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED], "the size at which the `large_assignments` lint starts to be emitted"), mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED], @@ -1419,18 +1420,16 @@ options! { "compile without linking"), no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED], "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), - no_unique_section_names: bool = (false, parse_bool, [TRACKED], - "do not use unique names for text and data sections when -Z function-sections is used"), no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED], "prevent automatic injection of the profiler_builtins crate"), + no_unique_section_names: bool = (false, parse_bool, [TRACKED], + "do not use unique names for text and data sections when -Z function-sections is used"), normalize_docs: bool = (false, parse_bool, [TRACKED], "normalize associated items in rustdoc when generating documentation"), oom: OomStrategy = (OomStrategy::Abort, parse_oom_strategy, [TRACKED], "panic strategy for out-of-memory handling"), osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], "pass `-install_name @rpath/...` to the macOS linker (default: no)"), - diagnostic_width: Option<usize> = (None, parse_opt_number, [UNTRACKED], - "set the current output width for diagnostic truncation"), packed_bundled_libs: bool = (false, parse_bool, [TRACKED], "change rlib format to store native libraries as archives"), panic_abort_tests: bool = (false, parse_bool, [TRACKED], @@ -1480,25 +1479,20 @@ options! { profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], "file path to emit profiling data at runtime when using 'profile' \ (default based on relative source path)"), - profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], - "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), profile_sample_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), + profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], + "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), - layout_seed: Option<u64> = (None, parse_opt_number, [TRACKED], - "seed layout randomization"), relax_elf_relocations: Option<bool> = (None, parse_opt_bool, [TRACKED], "whether ELF relocations can be relaxed"), relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED], "choose which RELRO level to use"), remap_cwd_prefix: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], "remap paths under the current working directory to this path prefix"), - simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], - "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \ - to rust's source base directory. only meant for testing purposes"), report_delayed_bugs: bool = (false, parse_bool, [TRACKED], "immediately print bugs registered with `delay_span_bug` (default: no)"), sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], @@ -1516,27 +1510,41 @@ options! { self_profile: SwitchWithOptPath = (SwitchWithOptPath::Disabled, parse_switch_with_opt_path, [UNTRACKED], "run the self profiler and output the raw event data"), - /// keep this in sync with the event filter names in librustc_data_structures/profiling.rs - self_profile_events: Option<Vec<String>> = (None, parse_opt_comma_list, [UNTRACKED], - "specify the events recorded by the self profiler; - for example: `-Z self-profile-events=default,query-keys` - all options: none, all, default, generic-activity, query-provider, query-cache-hit - query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"), self_profile_counter: String = ("wall-time".to_string(), parse_string, [UNTRACKED], "counter used by the self profiler (default: `wall-time`), one of: `wall-time` (monotonic clock, i.e. `std::time::Instant`) `instructions:u` (retired instructions, userspace-only) `instructions-minus-irqs:u` (subtracting hardware interrupt counts for extra accuracy)" ), + /// keep this in sync with the event filter names in librustc_data_structures/profiling.rs + self_profile_events: Option<Vec<String>> = (None, parse_opt_comma_list, [UNTRACKED], + "specify the events recorded by the self profiler; + for example: `-Z self-profile-events=default,query-keys` + all options: none, all, default, generic-activity, query-provider, query-cache-hit + query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"), share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED], "make the current crate share its generic instantiations"), show_span: Option<String> = (None, parse_opt_string, [TRACKED], "show spans for compiler debugging (expr|pat|ty)"), + simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], + "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \ + to rust's source base directory. only meant for testing purposes"), span_debug: bool = (false, parse_bool, [UNTRACKED], "forward proc_macro::Span's `Debug` impl to `Span`"), /// o/w tests have closure@path span_free_formats: bool = (false, parse_bool, [UNTRACKED], "exclude spans when debug-printing compiler state (default: no)"), + split_dwarf_inlining: bool = (true, parse_bool, [TRACKED], + "provide minimal debug info in the object/executable to facilitate online \ + symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), + split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED], + "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) + (default: `split`) + + `split`: sections which do not require relocation are written into a DWARF object (`.dwo`) + file which is ignored by the linker + `single`: sections which do not require relocation are written into object file but ignored + by the linker"), src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED], "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), #[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")] @@ -1546,17 +1554,6 @@ options! { "control if mem::uninitialized and mem::zeroed panic on more UB"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), - split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED], - "split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) - (default: `split`) - - `split`: sections which do not require relocation are written into a DWARF object (`.dwo`) - file which is ignored by the linker - `single`: sections which do not require relocation are written into object file but ignored - by the linker"), - split_dwarf_inlining: bool = (true, parse_bool, [TRACKED], - "provide minimal debug info in the object/executable to facilitate online \ - symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), symbol_mangling_version: Option<SymbolManglingVersion> = (None, parse_symbol_mangling_version, [TRACKED], "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), @@ -1565,17 +1562,6 @@ options! { "show extended diagnostic help (default: no)"), temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED], "the directory the intermediate files are written to"), - // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved - // alongside query results and changes to translation options can affect diagnostics - so - // translation options should be tracked. - translate_lang: Option<LanguageIdentifier> = (None, parse_opt_langid, [TRACKED], - "language identifier for diagnostic output"), - translate_additional_ftl: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], - "additional fluent translation to preferentially use (for testing translation)"), - translate_directionality_markers: bool = (false, parse_bool, [TRACKED], - "emit directionality isolation markers in translated diagnostics"), - tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED], - "select processor to schedule for (`rustc --print target-cpus` for details)"), #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")] thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), @@ -1599,6 +1585,15 @@ options! { "choose the TLS model to use (`rustc --print tls-models` for details)"), trace_macros: bool = (false, parse_bool, [UNTRACKED], "for every macro invocation, print its name and arguments (default: no)"), + // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved + // alongside query results and changes to translation options can affect diagnostics - so + // translation options should be tracked. + translate_additional_ftl: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], + "additional fluent translation to preferentially use (for testing translation)"), + translate_directionality_markers: bool = (false, parse_bool, [TRACKED], + "emit directionality isolation markers in translated diagnostics"), + translate_lang: Option<LanguageIdentifier> = (None, parse_opt_langid, [TRACKED], + "language identifier for diagnostic output"), translate_remapped_path_to_local_path: bool = (true, parse_bool, [TRACKED], "translate remapped paths into local paths when possible (default: yes)"), trap_unreachable: Option<bool> = (None, parse_opt_bool, [TRACKED], @@ -1607,6 +1602,8 @@ options! { "treat error number `val` that occurs as bug"), trim_diagnostic_paths: bool = (true, parse_bool, [UNTRACKED], "in diagnostics, use heuristics to shorten paths referring to items"), + tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED], + "select processor to schedule for (`rustc --print target-cpus` for details)"), ui_testing: bool = (false, parse_bool, [UNTRACKED], "emit compiler diagnostics in a form suitable for UI testing (default: no)"), uninit_const_chunk_threshold: usize = (16, parse_number, [TRACKED], @@ -1647,9 +1644,8 @@ options! { Requires `-Clto[=[fat,yes]]`"), wasi_exec_model: Option<WasiExecModel> = (None, parse_wasi_exec_model, [TRACKED], "whether to build a wasi command or reactor"), + // tidy-alphabetical-end - // This list is in alphabetical order. - // // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 2c3d8d5283b..a199947ebed 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -12,7 +12,7 @@ use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler}; use rustc_errors::{ fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, - EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic, MultiSpan, StashKey, + EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic, MultiSpan, Noted, StashKey, }; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; use rustc_span::edition::Edition; @@ -354,6 +354,17 @@ impl ParseSess { self.create_warning(warning).emit() } + pub fn create_note<'a>( + &'a self, + note: impl IntoDiagnostic<'a, Noted>, + ) -> DiagnosticBuilder<'a, Noted> { + note.into_diagnostic(&self.span_diagnostic) + } + + pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted { + self.create_note(note).emit() + } + pub fn create_fatal<'a>( &'a self, fatal: impl IntoDiagnostic<'a, !>, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 5926cdc9dad..beb22ab3eb9 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -28,7 +28,7 @@ use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; use rustc_errors::{ error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, - ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan, + ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted, }; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; @@ -489,6 +489,15 @@ impl Session { pub fn emit_warning<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) { self.parse_sess.emit_warning(warning) } + pub fn create_note<'a>( + &'a self, + note: impl IntoDiagnostic<'a, Noted>, + ) -> DiagnosticBuilder<'a, Noted> { + self.parse_sess.create_note(note) + } + pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted { + self.parse_sess.emit_note(note) + } pub fn create_fatal<'a>( &'a self, fatal: impl IntoDiagnostic<'a, !>, diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index d2fb8c32ffd..9fe7da3f29e 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -740,7 +740,8 @@ impl<'a, Ty> FnAbi<'a, Ty> { mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; - // These are in alphabetical order, which is easy to maintain. + // tidy-alphabetical-start static_assert_size!(ArgAbi<'_, usize>, 56); static_assert_size!(FnAbi<'_, usize>, 80); + // tidy-alphabetical-end } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 30efbf66175..196d70614e7 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -85,9 +85,13 @@ fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness { /// - a type parameter or projection whose Sizedness can't be known /// - a tuple of type parameters or projections, if there are multiple /// such. -/// - an Error, if a type contained itself. The representability -/// check should catch this case. -fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstraint<'_> { +/// - an Error, if a type is infinitely sized +fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> &[Ty<'_>] { + if let Some(def_id) = def_id.as_local() { + if matches!(tcx.representability(def_id), ty::Representability::Infinite) { + return tcx.intern_type_list(&[tcx.ty_error()]); + } + } let def = tcx.adt_def(def_id); let result = tcx.mk_type_list( @@ -99,7 +103,7 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstrain debug!("adt_sized_constraint: {:?} => {:?}", def, result); - ty::AdtSizedConstraint(result) + result } /// See `ParamEnv` struct definition for details. @@ -133,77 +137,10 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { let local_did = def_id.as_local(); let hir_id = local_did.map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)); - let constness = match hir_id { - Some(hir_id) => match tcx.hir().get(hir_id) { - hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) - if tcx.is_const_default_method(def_id) => - { - hir::Constness::Const - } - - hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(..), .. }) - | hir::Node::Item(hir::Item { kind: hir::ItemKind::Static(..), .. }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Const(..), .. - }) - | hir::Node::AnonConst(_) - | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) - | hir::Node::ImplItem(hir::ImplItem { - kind: - hir::ImplItemKind::Fn( - hir::FnSig { - header: hir::FnHeader { constness: hir::Constness::Const, .. }, - .. - }, - .., - ), - .. - }) => hir::Constness::Const, - - hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Type(..) | hir::ImplItemKind::Fn(..), - .. - }) => { - let parent_hir_id = tcx.hir().get_parent_node(hir_id); - match tcx.hir().get(parent_hir_id) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), - .. - }) => *constness, - _ => span_bug!( - tcx.def_span(parent_hir_id.owner), - "impl item's parent node is not an impl", - ), - } - } - - hir::Node::Item(hir::Item { - kind: - hir::ItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..), - .. - }) - | hir::Node::TraitItem(hir::TraitItem { - kind: - hir::TraitItemKind::Fn( - hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, - .., - ), - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), - .. - }) => *constness, - - _ => hir::Constness::NotConst, - }, - None => hir::Constness::NotConst, - }; - let unnormalized_env = ty::ParamEnv::new( tcx.intern_predicates(&predicates), traits::Reveal::UserFacing, - constness, + tcx.constness(def_id), ); let body_id = diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index d6aeee299e3..ff3b7bc2c90 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2279,9 +2279,8 @@ macro_rules! int_impl { /// /// # Panics /// - /// When the number is negative, zero, or if the base is not at least 2; it - /// panics in debug mode and the return value is 0 in release - /// mode. + /// This function will panic if `self` is less than or equal to zero, + /// or if `base` is less then 2. /// /// # Examples /// @@ -2294,27 +2293,16 @@ macro_rules! int_impl { without modifying the original"] #[inline] #[track_caller] - #[rustc_inherit_overflow_checks] - #[allow(arithmetic_overflow)] pub const fn ilog(self, base: Self) -> u32 { - match self.checked_ilog(base) { - Some(n) => n, - None => { - // In debug builds, trigger a panic on None. - // This should optimize completely out in release builds. - let _ = Self::MAX + 1; - - 0 - }, - } + assert!(base >= 2, "base of integer logarithm must be at least 2"); + self.checked_ilog(base).expect("argument of integer logarithm must be positive") } /// Returns the base 2 logarithm of the number, rounded down. /// /// # Panics /// - /// When the number is negative or zero it panics in debug mode and the return value - /// is 0 in release mode. + /// This function will panic if `self` is less than or equal to zero. /// /// # Examples /// @@ -2327,27 +2315,15 @@ macro_rules! int_impl { without modifying the original"] #[inline] #[track_caller] - #[rustc_inherit_overflow_checks] - #[allow(arithmetic_overflow)] pub const fn ilog2(self) -> u32 { - match self.checked_ilog2() { - Some(n) => n, - None => { - // In debug builds, trigger a panic on None. - // This should optimize completely out in release builds. - let _ = Self::MAX + 1; - - 0 - }, - } + self.checked_ilog2().expect("argument of integer logarithm must be positive") } /// Returns the base 10 logarithm of the number, rounded down. /// /// # Panics /// - /// When the number is negative or zero it panics in debug mode and the return value - /// is 0 in release mode. + /// This function will panic if `self` is less than or equal to zero. /// /// # Example /// @@ -2360,19 +2336,8 @@ macro_rules! int_impl { without modifying the original"] #[inline] #[track_caller] - #[rustc_inherit_overflow_checks] - #[allow(arithmetic_overflow)] pub const fn ilog10(self) -> u32 { - match self.checked_ilog10() { - Some(n) => n, - None => { - // In debug builds, trigger a panic on None. - // This should optimize completely out in release builds. - let _ = Self::MAX + 1; - - 0 - }, - } + self.checked_ilog10().expect("argument of integer logarithm must be positive") } /// Returns the logarithm of the number with respect to an arbitrary base, diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 46b0ca23034..d921ff9ba10 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -692,8 +692,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// When the number is zero, or if the base is not at least 2; - /// it panics in debug mode and the return value is 0 in release mode. + /// This function will panic if `self` is zero, or if `base` is less then 2. /// /// # Examples /// @@ -706,27 +705,16 @@ macro_rules! uint_impl { without modifying the original"] #[inline] #[track_caller] - #[rustc_inherit_overflow_checks] - #[allow(arithmetic_overflow)] pub const fn ilog(self, base: Self) -> u32 { - match self.checked_ilog(base) { - Some(n) => n, - None => { - // In debug builds, trigger a panic on None. - // This should optimize completely out in release builds. - let _ = Self::MAX + 1; - - 0 - }, - } + assert!(base >= 2, "base of integer logarithm must be at least 2"); + self.checked_ilog(base).expect("argument of integer logarithm must be positive") } /// Returns the base 2 logarithm of the number, rounded down. /// /// # Panics /// - /// When the number is zero it panics in debug mode and - /// the return value is 0 in release mode. + /// This function will panic if `self` is zero. /// /// # Examples /// @@ -739,27 +727,15 @@ macro_rules! uint_impl { without modifying the original"] #[inline] #[track_caller] - #[rustc_inherit_overflow_checks] - #[allow(arithmetic_overflow)] pub const fn ilog2(self) -> u32 { - match self.checked_ilog2() { - Some(n) => n, - None => { - // In debug builds, trigger a panic on None. - // This should optimize completely out in release builds. - let _ = Self::MAX + 1; - - 0 - }, - } + self.checked_ilog2().expect("argument of integer logarithm must be positive") } /// Returns the base 10 logarithm of the number, rounded down. /// /// # Panics /// - /// When the number is zero it panics in debug mode and the - /// return value is 0 in release mode. + /// This function will panic if `self` is zero. /// /// # Example /// @@ -772,19 +748,8 @@ macro_rules! uint_impl { without modifying the original"] #[inline] #[track_caller] - #[rustc_inherit_overflow_checks] - #[allow(arithmetic_overflow)] pub const fn ilog10(self) -> u32 { - match self.checked_ilog10() { - Some(n) => n, - None => { - // In debug builds, trigger a panic on None. - // This should optimize completely out in release builds. - let _ = Self::MAX + 1; - - 0 - }, - } + self.checked_ilog10().expect("argument of integer logarithm must be positive") } /// Returns the logarithm of the number with respect to an arbitrary base, diff --git a/library/core/tests/num/int_log.rs b/library/core/tests/num/int_log.rs index be203fb5c04..a1edb1a5186 100644 --- a/library/core/tests/num/int_log.rs +++ b/library/core/tests/num/int_log.rs @@ -164,3 +164,33 @@ fn ilog10_u64() { fn ilog10_u128() { ilog10_loop! { u128, 38 } } + +#[test] +#[should_panic(expected = "argument of integer logarithm must be positive")] +fn ilog2_of_0_panic() { + let _ = 0u32.ilog2(); +} + +#[test] +#[should_panic(expected = "argument of integer logarithm must be positive")] +fn ilog10_of_0_panic() { + let _ = 0u32.ilog10(); +} + +#[test] +#[should_panic(expected = "argument of integer logarithm must be positive")] +fn ilog3_of_0_panic() { + let _ = 0u32.ilog(3); +} + +#[test] +#[should_panic(expected = "base of integer logarithm must be at least 2")] +fn ilog0_of_1_panic() { + let _ = 1u32.ilog(0); +} + +#[test] +#[should_panic(expected = "base of integer logarithm must be at least 2")] +fn ilog1_of_1_panic() { + let _ = 1u32.ilog(1); +} diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 4461b21802a..506b2a773cc 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -223,10 +223,10 @@ pub(crate) use super::symbol::Symbol; macro_rules! define_client_side { ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* }),* $(,)?) => { $(impl $name { - $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* { + $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)? { Bridge::with(|bridge| { let mut buf = bridge.cached_buffer.take(); diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs index b1a84095f13..4800eeda022 100644 --- a/library/std/src/io/readbuf.rs +++ b/library/std/src/io/readbuf.rs @@ -3,10 +3,10 @@ #[cfg(test)] mod tests; -use crate::cmp; use crate::fmt::{self, Debug, Formatter}; use crate::io::{Result, Write}; use crate::mem::{self, MaybeUninit}; +use crate::{cmp, ptr}; /// A borrowed byte buffer which is incrementally filled and initialized. /// @@ -250,8 +250,11 @@ impl<'a> BorrowedCursor<'a> { /// Initializes all bytes in the cursor. #[inline] pub fn ensure_init(&mut self) -> &mut Self { - for byte in self.uninit_mut() { - byte.write(0); + let uninit = self.uninit_mut(); + // SAFETY: 0 is a valid value for MaybeUninit<u8> and the length matches the allocation + // since it is comes from a slice reference. + unsafe { + ptr::write_bytes(uninit.as_mut_ptr(), 0, uninit.len()); } self.buf.init = self.buf.capacity(); diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 4b07b393a2f..d4976a469cc 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -308,6 +308,14 @@ pub mod panic_count { // Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG) // records whether panic::always_abort() has been called. This can only be // set, never cleared. + // panic::always_abort() is usually called to prevent memory allocations done by + // the panic handling in the child created by `libc::fork`. + // Memory allocations performed in a child created with `libc::fork` are undefined + // behavior in most operating systems. + // Accessing LOCAL_PANIC_COUNT in a child created by `libc::fork` would lead to a memory + // allocation. Only GLOBAL_PANIC_COUNT can be accessed in this situation. This is + // sufficient because a child process will always have exactly one thread only. + // See also #85261 for details. // // This could be viewed as a struct containing a single bit and an n-1-bit // value, but if we wrote it like that it would be more than a single word, @@ -318,15 +326,26 @@ pub mod panic_count { // panicking thread consumes at least 2 bytes of address space. static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0); + // Return the state of the ALWAYS_ABORT_FLAG and number of panics. + // + // If ALWAYS_ABORT_FLAG is not set, the number is determined on a per-thread + // base (stored in LOCAL_PANIC_COUNT), i.e. it is the amount of recursive calls + // of the calling thread. + // If ALWAYS_ABORT_FLAG is set, the number equals the *global* number of panic + // calls. See above why LOCAL_PANIC_COUNT is not used. pub fn increase() -> (bool, usize) { - ( - GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed) & ALWAYS_ABORT_FLAG != 0, + let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed); + let must_abort = global_count & ALWAYS_ABORT_FLAG != 0; + let panics = if must_abort { + global_count & !ALWAYS_ABORT_FLAG + } else { LOCAL_PANIC_COUNT.with(|c| { let next = c.get() + 1; c.set(next); next - }), - ) + }) + }; + (must_abort, panics) } pub fn decrease() { diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 415774d7255..8b144f14635 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -704,6 +704,7 @@ impl<'a> Builder<'a> { doc::Miri, doc::EmbeddedBook, doc::EditionGuide, + doc::StyleGuide, ), Kind::Dist => describe!( dist::Docs, diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 819af658748..7bdd226cb69 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -82,6 +82,7 @@ book!( Reference, "src/doc/reference", "reference", submodule; RustByExample, "src/doc/rust-by-example", "rust-by-example", submodule; RustdocBook, "src/doc/rustdoc", "rustdoc"; + StyleGuide, "src/doc/style-guide", "style-guide"; ); fn open(builder: &Builder<'_>, path: impl AsRef<Path>) { diff --git a/src/doc/index.md b/src/doc/index.md index 744c7f709a6..bf08960f338 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -113,6 +113,12 @@ resources useful. [The Reference](reference/index.html) is not a formal spec, but is more detailed and comprehensive than the book. +## The Style Guide + +[The Rust Style Guide](style-guide/index.html) describes the standard formatting of Rust +code. Most developers use rustfmt to format their code, and rustfmt's default +formatting matches this style guide. + ## The Rustonomicon [The Rustonomicon](nomicon/index.html) is your guidebook to the dark arts of unsafe diff --git a/src/doc/style-guide/book.toml b/src/doc/style-guide/book.toml new file mode 100644 index 00000000000..056aec8cdd4 --- /dev/null +++ b/src/doc/style-guide/book.toml @@ -0,0 +1,8 @@ +[book] +title = "The Rust Style Guide" +author = "The Rust Style Team" +multilingual = false +src = "src" + +[output.html] +git-repository-url = "https://github.com/rust-lang/rust/tree/HEAD/src/doc/style-guide/" diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md new file mode 100644 index 00000000000..adb73a7eef6 --- /dev/null +++ b/src/doc/style-guide/src/README.md @@ -0,0 +1,190 @@ +# Rust Style Guide + +## Motivation - why use a formatting tool? + +Formatting code is a mostly mechanical task which takes both time and mental +effort. By using an automatic formatting tool, a programmer is relieved of +this task and can concentrate on more important things. + +Furthermore, by sticking to an established style guide (such as this one), +programmers don't need to formulate ad hoc style rules, nor do they need to +debate with other programmers what style rules should be used, saving time, +communication overhead, and mental energy. + +Humans comprehend information through pattern matching. By ensuring that all +Rust code has similar formatting, less mental effort is required to comprehend a +new project, lowering the barrier to entry for new developers. + +Thus, there are productivity benefits to using a formatting tool (such as +rustfmt), and even larger benefits by using a community-consistent formatting, +typically by using a formatting tool's default settings. + + +## Formatting conventions + +### Indentation and line width + +* Use spaces, not tabs. +* Each level of indentation must be four spaces (that is, all indentation + outside of string literals and comments must be a multiple of four). +* The maximum width for a line is 100 characters. +* A tool should be configurable for all three of these variables. + + +### Blank lines + +Separate items and statements by either zero or one blank lines (i.e., one or +two newlines). E.g, + +```rust +fn foo() { + let x = ...; + + let y = ...; + let z = ...; +} + +fn bar() {} +fn baz() {} +``` + +Formatting tools should make the bounds on blank lines configurable: there +should be separate minimum and maximum numbers of newlines between both +statements and (top-level) items (i.e., four options). As described above, the +defaults for both statements and items should be minimum: 1, maximum: 2. + + +### [Module-level items](items.md) +### [Statements](statements.md) +### [Expressions](expressions.md) +### [Types](types.md) + + +### Comments + +The following guidelines for comments are recommendations only, a mechanical +formatter might skip formatting of comments. + +Prefer line comments (`//`) to block comments (`/* ... */`). + +When using line comments there should be a single space after the opening sigil. + +When using single-line block comments there should be a single space after the +opening sigil and before the closing sigil. Multi-line block comments should +have a newline after the opening sigil and before the closing sigil. + +Prefer to put a comment on its own line. Where a comment follows code, there +should be a single space before it. Where a block comment is inline, there +should be surrounding whitespace as if it were an identifier or keyword. There +should be no trailing whitespace after a comment or at the end of any line in a +multi-line comment. Examples: + +```rust +// A comment on an item. +struct Foo { ... } + +fn foo() {} // A comment after an item. + +pub fn foo(/* a comment before an argument */ x: T) {...} +``` + +Comments should usually be complete sentences. Start with a capital letter, end +with a period (`.`). An inline block comment may be treated as a note without +punctuation. + +Source lines which are entirely a comment should be limited to 80 characters +in length (including comment sigils, but excluding indentation) or the maximum +width of the line (including comment sigils and indentation), whichever is +smaller: + +```rust +// This comment goes up to the ................................. 80 char margin. + +{ + // This comment is .............................................. 80 chars wide. +} + +{ + { + { + { + { + { + // This comment is limited by the ......................... 100 char margin. + } + } + } + } + } +} +``` + +#### Doc comments + +Prefer line comments (`///`) to block comments (`/** ... */`). + +Prefer outer doc comments (`///` or `/** ... */`), only use inner doc comments +(`//!` and `/*! ... */`) to write module-level or crate-level documentation. + +Doc comments should come before attributes. + +### Attributes + +Put each attribute on its own line, indented to the level of the item. +In the case of inner attributes (`#!`), indent it to the level of the inside of +the item. Prefer outer attributes, where possible. + +For attributes with argument lists, format like functions. + +```rust +#[repr(C)] +#[foo(foo, bar)] +struct CRepr { + #![repr(C)] + x: f32, + y: f32, +} +``` + +For attributes with an equal sign, there should be a single space before and +after the `=`, e.g., `#[foo = 42]`. + +There must only be a single `derive` attribute. Note for tool authors: if +combining multiple `derive` attributes into a single attribute, the ordering of +the derived names should be preserved. E.g., `#[derive(bar)] #[derive(foo)] +struct Baz;` should be formatted to `#[derive(bar, foo)] struct Baz;`. + +### *small* items + +In many places in this guide we specify that a formatter may format an item +differently if it is *small*, for example struct literals: + +```rust +// Normal formatting +Foo { + f1: an_expression, + f2: another_expression(), +} + +// *small* formatting +Foo { f1, f2 } +``` + +We leave it to individual tools to decide on exactly what *small* means. In +particular, tools are free to use different definitions in different +circumstances. + +Some suitable heuristics are the size of the item (in characters) or the +complexity of an item (for example, that all components must be simple names, +not more complex sub-expressions). For more discussion on suitable heuristics, +see [this issue](https://github.com/rust-lang-nursery/fmt-rfcs/issues/47). + +Tools should give the user an option to ignore such heuristics and always use +the normal formatting. + + +## [Non-formatting conventions](advice.md) + +## [Cargo.toml conventions](cargo.md) + +## [Principles used for deciding these guidelines](principles.md) diff --git a/src/doc/style-guide/src/SUMMARY.md b/src/doc/style-guide/src/SUMMARY.md new file mode 100644 index 00000000000..004692fa6a2 --- /dev/null +++ b/src/doc/style-guide/src/SUMMARY.md @@ -0,0 +1,11 @@ +# Summary + +[Introduction](README.md) + +- [Module-level items](items.md) +- [Statements](statements.md) +- [Expressions](expressions.md) +- [Types](types.md) +- [Non-formatting conventions](advice.md) +- [`Cargo.toml` conventions](cargo.md) +- [Principles used for deciding these guidelines](principles.md) diff --git a/src/doc/style-guide/src/advice.md b/src/doc/style-guide/src/advice.md new file mode 100644 index 00000000000..ab4b92b0a24 --- /dev/null +++ b/src/doc/style-guide/src/advice.md @@ -0,0 +1,34 @@ +# Other style advice + +## Expressions + +Prefer to use Rust's expression oriented nature where possible; + +```rust +// use +let x = if y { 1 } else { 0 }; +// not +let x; +if y { + x = 1; +} else { + x = 0; +} +``` + +## Names + + * Types shall be `UpperCamelCase`, + * Enum variants shall be `UpperCamelCase`, + * Struct fields shall be `snake_case`, + * Function and method names shall be `snake_case`, + * Local variables shall be `snake_case`, + * Macro names shall be `snake_case`, + * Constants (`const`s and immutable `static`s) shall be `SCREAMING_SNAKE_CASE`. + * When a name is forbidden because it is a reserved word (e.g., `crate`), use a + trailing underscore to make the name legal (e.g., `crate_`), or use raw + identifiers if possible. + +### Modules + +Avoid `#[path]` annotations where possible. diff --git a/src/doc/style-guide/src/cargo.md b/src/doc/style-guide/src/cargo.md new file mode 100644 index 00000000000..f4993ba06a8 --- /dev/null +++ b/src/doc/style-guide/src/cargo.md @@ -0,0 +1,78 @@ +# Cargo.toml conventions + +## Formatting conventions + +Use the same line width and indentation as Rust code. + +Put a blank line between the last key-value pair in a section and the header of +the next section. Do not place a blank line between section headers and the +key-value pairs in that section, or between key-value pairs in a section. + +Sort key names alphabetically within each section, with the exception of the +`[package]` section. Put the `[package]` section at the top of the file; put +the `name` and `version` keys in that order at the top of that section, +followed by the remaining keys other than `description` in alphabetical order, +followed by the `description` at the end of that section. + +Don't use quotes around any standard key names; use bare keys. Only use quoted +keys for non-standard keys whose names require them, and avoid introducing such +key names when possible. See the [TOML +specification](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md#table) +for details. + +Put a single space both before and after the `=` between a key and value. Do +not indent any key names; start all key names at the start of a line. + +Use multi-line strings (rather than newline escape sequences) for any string +values that include multiple lines, such as the crate description. + +For array values, such as a list of authors, put the entire list on the same +line as the key, if it fits. Otherwise, use block indentation: put a newline +after the opening square bracket, indent each item by one indentation level, +put a comma after each item (including the last), and put the closing square +bracket at the start of a line by itself after the last item. + +```rust +authors = [ + "A Uthor <a.uthor@example.org>", + "Another Author <author@example.net>", +] +``` + +For table values, such as a crate dependency with a path, write the entire +table using curly braces and commas on the same line as the key if it fits. If +the entire table does not fit on the same line as the key, separate it out into +a separate section with key-value pairs: + +```toml +[dependencies] +crate1 = { path = "crate1", version = "1.2.3" } + +[dependencies.extremely_long_crate_name_goes_here] +path = "extremely_long_path_name_goes_right_here" +version = "4.5.6" +``` + +## Metadata conventions + +The authors list should consist of strings that each contain an author name +followed by an email address in angle brackets: `Full Name <email@address>`. +It should not contain bare email addresses, or names without email addresses. +(The authors list may also include a mailing list address without an associated +name.) + +The license field must contain a valid [SPDX +expression](https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60), +using valid [SPDX license names](https://spdx.org/licenses/). (As an exception, +by widespread convention, the license field may use `/` in place of ` OR `; for +example, `MIT/Apache-2.0`.) + +The homepage field, if present, must consist of a single URL, including the +scheme (e.g. `https://example.org/`, not just `example.org`.) + +Within the description field, wrap text at 80 columns. Don't start the +description field with the name of the crate (e.g. "cratename is a ..."); just +describe the crate itself. If providing a multi-sentence description, the first +sentence should go on a line by itself and summarize the crate, like the +subject of an email or commit message; subsequent sentences can then describe +the crate in more detail. diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md new file mode 100644 index 00000000000..c7d0446dded --- /dev/null +++ b/src/doc/style-guide/src/expressions.md @@ -0,0 +1,850 @@ +## Expressions + +### Blocks + +A block expression should have a newline after the initial `{` and before the +terminal `}`. Any qualifier before the block (e.g., `unsafe`) should always be +on the same line as the opening brace, and separated with a single space. The +contents of the block should be block indented: + +```rust +fn block_as_stmt() { + a_call(); + + { + a_call_inside_a_block(); + + // a comment in a block + the_value + } +} + +fn block_as_expr() { + let foo = { + a_call_inside_a_block(); + + // a comment in a block + the_value + }; +} + +fn unsafe_block_as_stmt() { + a_call(); + + unsafe { + a_call_inside_a_block(); + + // a comment in a block + the_value + } +} +``` + +If a block has an attribute, it should be on its own line: + +```rust +fn block_as_stmt() { + #[an_attribute] + { + #![an_inner_attribute] + + // a comment in a block + the_value + } +} +``` + +Avoid writing comments on the same line as the braces. + +An empty block should be written as `{}`. + +A block may be written on a single line if: + +* it is either used in expression position (not statement position) or is an + unsafe block in statement position +* contains a single-line expression and no statements +* contains no comments + +A single line block should have spaces after the opening brace and before the +closing brace. + +Examples: + +```rust +fn main() { + // Single line + let _ = { a_call() }; + let _ = unsafe { a_call() }; + + // Not allowed on one line + // Statement position. + { + a_call() + } + + // Contains a statement + let _ = { + a_call(); + }; + unsafe { + a_call(); + } + + // Contains a comment + let _ = { + // A comment + }; + let _ = { + // A comment + a_call() + }; + + // Multiple lines + let _ = { + a_call(); + another_call() + }; + let _ = { + a_call( + an_argument, + another_arg, + ) + }; +} +``` + + +### Closures + +Don't put any extra spaces before the first `|` (unless the closure is prefixed +by `move`); put a space between the second `|` and the expression of the +closure. Between the `|`s, you should use function definition syntax, however, +elide types where possible. + +Use closures without the enclosing `{}`, if possible. Add the `{}` when you have +a return type, when there are statements, there are comments in the body, or the +body expression spans multiple lines and is a control-flow expression. If using +braces, follow the rules above for blocks. Examples: + +```rust +|arg1, arg2| expr + +move |arg1: i32, arg2: i32| -> i32 { + expr1; + expr2 +} + +|| Foo { + field1, + field2: 0, +} + +|| { + if true { + blah + } else { + boo + } +} + +|x| unsafe { + expr +} +``` + + +### Struct literals + +If a struct literal is *small* it may be formatted on a single line. If not, +each field should be on it's own, block-indented line. There should be a +trailing comma in the multi-line form only. There should be a space after the +colon only. + +There should be a space before the opening brace. In the single-line form there +should be spaces after the opening brace and before the closing brace. + +```rust +Foo { field1, field2: 0 } +let f = Foo { + field1, + field2: an_expr, +}; +``` + +Functional record update syntax is treated like a field, but it must never have +a trailing comma. There should be no space after `..`. + +let f = Foo { + field1, + ..an_expr +}; + + +### Tuple literals + +Use a single-line form where possible. There should not be spaces around the +parentheses. Where a single-line form is not possible, each element of the tuple +should be on its own block-indented line and there should be a trailing comma. + +```rust +(a, b, c) + +let x = ( + a_long_expr, + another_very_long_expr, +); +``` + + +### Tuple struct literals + +There should be no space between the identifier and the opening parenthesis. +Otherwise, follow the rules for tuple literals, e.g., `Foo(a, b)`. + + +### Enum literals + +Follow the formatting rules for the various struct literals. Prefer using the +name of the enum as a qualifying name, unless the enum is in the prelude. E.g., + +```rust +Foo::Bar(a, b) +Foo::Baz { + field1, + field2: 1001, +} +Ok(an_expr) +``` + + +### Array literals + +For simple array literals, avoid line breaking, no spaces around square +brackets, contents of the array should be separated by commas and spaces. If +using the repeating initialiser, there should be a space after the semicolon +only. Apply the same rules if using the `vec!` or similar macros (always use +square brackets here). Examples: + +```rust +fn main() { + [1, 2, 3]; + vec![a, b, c, d]; + let a = [42; 10]; +} +``` + +If a line must be broken, prefer breaking only after the `;`, if possible. +Otherwise, follow the rules below for function calls. In any case, the contents +of the initialiser should be block indented and there should be line breaks +after the opening bracket and before the closing bracket: + +```rust +fn main() { + [ + a_long_expression(); + 1234567890 + ] + let x = [ + an_expression, + another_expression, + a_third_expression, + ]; +} +``` + + +### Array accesses, indexing, and slicing. + +No spaces around the square brackets, avoid breaking lines if possible, never +break a line between the target expression and the opening bracket. If the +indexing expression covers multiple lines, then it should be block indented and +there should be newlines after the opening brackets and before the closing +bracket. However, this should be avoided where possible. + +Examples: + +```rust +fn main() { + foo[42]; + &foo[..10]; + bar[0..100]; + foo[4 + 5 / bar]; + a_long_target[ + a_long_indexing_expression + ]; +} +``` + +### Unary operations + +Do not include a space between a unary op and its operand (i.e., `!x`, not +`! x`). However, there must be a space after `&mut`. Avoid line-breaking +between a unary operator and its operand. + +### Binary operations + +Do include spaces around binary ops (i.e., `x + 1`, not `x+1`) (including `=` +and other assignment operators such as `+=` or `*=`). + +For comparison operators, because for `T op U`, `&T op &U` is also implemented: +if you have `t: &T`, and `u: U`, prefer `*t op u` to `t op &u`. In general, +within expressions, prefer dereferencing to taking references. + +Use parentheses liberally, do not necessarily elide them due to precedence. +Tools should not automatically insert or remove parentheses. Do not use spaces +to indicate precedence. + +If line-breaking, put the operator on a new line and block indent. Put each +sub-expression on its own line. E.g., + +```rust +foo_bar + + bar + + baz + + qux + + whatever +``` + +Prefer line-breaking at an assignment operator (either `=` or `+=`, etc.) rather +than at other binary operators. + +### Control flow + +Do not include extraneous parentheses for `if` and `while` expressions. + +```rust +if true { +} +``` + +is better than + +```rust +if (true) { +} +``` + +Do include extraneous parentheses if it makes an arithmetic or logic expression +easier to understand (`(x * 15) + (y * 20)` is fine) + +### Function calls + +Do not put a space between the function name, and the opening parenthesis. + +Do not put a space between an argument, and the comma which follows. + +Do put a space between an argument, and the comma which precedes it. + +Prefer not to break a line in the callee expression. + +#### Single-line calls + +Do not put a space between the function name and open paren, between the open +paren and the first argument, or between the last argument and the close paren. + +Do not put a comma after the last argument. + +```rust +foo(x, y, z) +``` + +#### Multi-line calls + +If the function call is not *small*, it would otherwise over-run the max width, +or any argument or the callee is multi-line, then the call should be formatted +across multiple lines. In this case, each argument should be on it's own block- +indented line, there should be a newline after the opening parenthesis and +before the closing parenthesis, and there should be a trailing comma. E.g., + +```rust +a_function_call( + arg1, + a_nested_call(a, b), +) +``` + + +### Method calls + +Follow the function rules for calling. + +Do not put any spaces around the `.`. + +```rust +x.foo().bar().baz(x, y, z); +``` + + +### Macro uses + +Macros which can be parsed like other constructs should be formatted like those +constructs. For example, a macro use `foo!(a, b, c)` can be parsed like a +function call (ignoring the `!`), therefore it should be formatted following the +rules for function calls. + +#### Special case macros + +Macros which take a format string and where all other arguments are *small* may +be formatted with arguments before and after the format string on a single line +and the format string on its own line, rather than putting each argument on its +own line. For example, + +```rust +println!( + "Hello {} and {}", + name1, name2, +); + +assert_eq!( + x, y, + "x and y were not equal, see {}", + reason, +); +``` + + +### Casts (`as`) + +Put spaces before and after `as`: + +```rust +let cstr = "Hi\0" as *const str as *const [u8] as *const std::os::raw::c_char; +``` + + +### Chains of fields and method calls + +A chain is a sequence of field accesses and/or method calls. A chain may also +include the try operator ('?'). E.g., `a.b.c().d` or `foo?.bar().baz?`. + +Prefer formatting on one line if possible, and the chain is *small*. If +formatting on multiple lines, each field access or method call in the chain +should be on its own line with the line-break before the `.` and after any `?`. +Each line should be block-indented. E.g., + +```rust +let foo = bar + .baz? + .qux(); +``` + +If the length of the last line of the first element plus its indentation is +less than or equal to the indentation of the second line (and there is space), +then combine the first and second lines, e.g., + +```rust +x.baz? + .qux() + +let foo = x + .baz? + .qux(); + +foo( + expr1, + expr2, +).baz? + .qux(); +``` + +#### Multi-line elements + +If any element in a chain is formatted across multiple lines, then that element +and any later elements must be on their own line. Earlier elements may be kept +on a single line. E.g., + +```rust +a.b.c()?.d + .foo( + an_expr, + another_expr, + ) + .bar + .baz +``` + +Note there is block indent due to the chain and the function call in the above +example. + +Prefer formatting the whole chain in multi-line style and each element on one +line, rather than putting some elements on multiple lines and some on a single +line, e.g., + +```rust +// Better +self.pre_comment + .as_ref() + .map_or(false, |comment| comment.starts_with("//")) + +// Worse +self.pre_comment.as_ref().map_or( + false, + |comment| comment.starts_with("//"), +) +``` + +### Control flow expressions + +This section covers `if`, `if let`, `loop`, `while`, `while let`, and `for` +expressions. + +The keyword, any initial clauses, and the opening brace of the block should be +on a single line. The usual rules for [block formatting](#blocks) should be +applied to the block. + +If there is an `else` component, then the closing brace, `else`, any following +clause, and the opening brace should all be on the same line. There should be a +single space before and after the `else` keyword. For example: + +```rust +if ... { + ... +} else { + ... +} + +if let ... { + ... +} else if ... { + ... +} else { + ... +} +``` + +If the control line needs to be broken, then prefer to break before the `=` in +`* let` expressions and before `in` in a `for` expression; the following line +should be block indented. If the control line is broken for any reason, then the +opening brace should be on its own line and not indented. Examples: + +```rust +while let Some(foo) + = a_long_expression +{ + ... +} + +for foo + in a_long_expression +{ + ... +} + +if a_long_expression + && another_long_expression + || a_third_long_expression +{ + ... +} +``` + +Where the initial clause is multi-lined and ends with one or more closing +parentheses, square brackets, or braces, and there is nothing else on that line, +and that line is not indented beyond the indent on the first line of the control +flow expression, then the opening brace of the block should be put on the same +line with a preceding space. For example: + +```rust +if !self.config.file_lines().intersects( + &self.codemap.lookup_line_range( + stmt.span, + ), +) { // Opening brace on same line as initial clause. + ... +} +``` + + +#### Single line `if else` + +Formatters may place an `if else` or `if let else` on a single line if it occurs +in expression context (i.e., is not a standalone statement), it contains a +single `else` clause, and is *small*. For example: + +```rust +let y = if x { 0 } else { 1 }; + +// Examples that must be multi-line. +let y = if something_very_long { + not_small +} else { + also_not_small +}; + +if x { + 0 +} else { + 1 +} +``` + + +### Match + +Prefer not to line-break inside the discriminant expression. There must always +be a line break after the opening brace and before the closing brace. The match +arms must be block indented once: + +```rust +match foo { + // arms +} + +let x = match foo.bar.baz() { + // arms +}; +``` + +Use a trailing comma for a match arm if and only if not using a block. + +Never start a match arm pattern with `|`, e.g., + +```rust +match foo { + // Don't do this. + | foo => bar, + // Or this. + | a_very_long_pattern + | another_pattern + | yet_another_pattern + | a_forth_pattern => { + ... + } +} +``` + +Prefer + + +```rust +match foo { + foo => bar, + a_very_long_pattern + | another_pattern + | yet_another_pattern + | a_forth_pattern => { + ... + } +} +``` + +Avoid splitting the left-hand side (before the `=>`) of a match arm where +possible. If the right-hand side of the match arm is kept on the same line, +never use a block (unless the block is empty). + +If the right-hand side consists of multiple statements or has line comments or +the start of the line cannot be fit on the same line as the left-hand side, use +a block. + +The body of a block arm should be block indented once. + +Examples: + +```rust +match foo { + foo => bar, + a_very_long_patten | another_pattern if an_expression() => { + no_room_for_this_expression() + } + foo => { + // A comment. + an_expression() + } + foo => { + let a = statement(); + an_expression() + } + bar => {} + // Trailing comma on last item. + foo => bar, +} +``` + +If the body is a single expression with no line comments and not a control flow +expression, then it may be started on the same line as the right-hand side. If +not, then it must be in a block. Example, + +```rust +match foo { + // A combinable expression. + foo => a_function_call(another_call( + argument1, + argument2, + )), + // A non-combinable expression + bar => { + a_function_call( + another_call( + argument1, + argument2, + ), + another_argument, + ) + } +} +``` + +#### Line-breaking + +Where it is possible to use a block form on the right-hand side and avoid +breaking the left-hand side, do that. E.g. + +```rust + // Assuming the following line does done fit in the max width + a_very_long_pattern | another_pattern => ALongStructName { + ... + }, + // Prefer this + a_very_long_pattern | another_pattern => { + ALongStructName { + ... + } + } + // To splitting the pattern. +``` + +Never break after `=>` without using the block form of the body. + +If the left-hand side must be split and there is an `if` clause, break before +the `if` and block indent. In this case, always use a block body and start the +body on a new line: + +```rust + a_very_long_pattern | another_pattern + if expr => + { + ... + } +``` + +If required to break the pattern, put each clause of the pattern on its own +line with no additional indent, breaking before the `|`. If there is an `if` +clause, then you must use the above form: + +```rust + a_very_long_pattern + | another_pattern + | yet_another_pattern + | a_forth_pattern => { + ... + } + a_very_long_pattern + | another_pattern + | yet_another_pattern + | a_forth_pattern + if expr => + { + ... + } +``` + +If the pattern is multi-line, and the last line is less wide than the indent, do +not put the `if` clause on a newline. E.g., + +```rust + Token::Dimension { + value, + ref unit, + .. + } if num_context.is_ok(context.parsing_mode, value) => { + ... + } +``` + +If every clause in a pattern is *small*, but does not fit on one line, then the +pattern may be formatted across multiple lines with as many clauses per line as +possible. Again break before a `|`: + +```rust + foo | bar | baz + | qux => { + ... + } +``` + +We define a pattern clause to be *small* if it matches the following grammar: + +``` +[small, ntp]: + - single token + - `&[single-line, ntp]` + +[small]: + - `[small, ntp]` + - unary tuple constructor `([small, ntp])` + - `&[small]` +``` + +E.g., `&&Some(foo)` matches, `Foo(4, Bar)` does not. + + +### Combinable expressions + +Where a function call has a single argument, and that argument is formatted +across multiple-lines, the outer call may be formatted as if it were a single- +line call. The same combining behaviour may be applied to any similar +expressions which have multi-line, block-indented lists of sub-expressions +delimited by parentheses (e.g., macros or tuple struct literals). E.g., + +```rust +foo(bar( + an_expr, + another_expr, +)) + +let x = foo(Bar { + field: whatever, +}); + +foo(|param| { + action(); + foo(param) +}) +``` + +Such behaviour should extend recursively, however, tools may choose to limit the +depth of nesting. + +Only where the multi-line sub-expression is a closure with an explicit block, +this combining behaviour may be used where there are other arguments, as long as +all the arguments and the first line of the closure fit on the first line, the +closure is the last argument, and there is only one closure argument: + +```rust +foo(first_arg, x, |param| { + action(); + foo(param) +}) +``` + + +### Ranges + +Do not put spaces in ranges, e.g., `0..10`, `x..=y`, `..x.len()`, `foo..`. + +When writing a range with both upper and lower bounds, if the line must be +broken, break before the range operator and block indent the second line: + +```rust +a_long_expression + ..another_long_expression +``` + +For the sake of indicating precedence, we recommend that if either bound is a +compound expression, then use parentheses around it, e.g., `..(x + 1)`, +`(x.f)..(x.f.len())`, or `0..(x - 10)`. + + +### Hexadecimal literals + +Hexadecimal literals may use upper- or lower-case letters, but they must not be +mixed within the same literal. Projects should use the same case for all +literals, but we do not make a recommendation for either lower- or upper-case. +Tools should have an option to convert mixed case literals to upper-case, and +may have an option to convert all literals to either lower- or upper-case. + + +## Patterns + +Patterns should be formatted like their corresponding expressions. See the +section on `match` for additional formatting for patterns in match arms. diff --git a/src/doc/style-guide/src/items.md b/src/doc/style-guide/src/items.md new file mode 100644 index 00000000000..2835975355f --- /dev/null +++ b/src/doc/style-guide/src/items.md @@ -0,0 +1,565 @@ +## Items + +`extern crate` statements must be first in a file. They must be ordered +alphabetically. + +`use` statements, and module *declarations* (`mod foo;`, not `mod { ... }`) +must come before other items. We recommend that imports come before module +declarations; if imports and modules are separated, then they should be ordered +alphabetically. When sorting, `self` and `super` must come before any other +names. Module declarations should not be moved if they are annotated with +`#[macro_use]`, since that may be semantics changing. + +Tools should make the above ordering optional. + + +### Function definitions + +In Rust, one finds functions by searching for `fn [function-name]`; It's +important that you style your code so that it's very searchable in this way. + +The proper ordering and spacing is: + +```rust +[pub] [unsafe] [extern ["ABI"]] fn foo(arg1: i32, arg2: i32) -> i32 { + ... +} +``` + +Avoid comments within the signature itself. + +If the function signature does not fit on one line, then break after the opening +parenthesis and before the closing parenthesis and put each argument on its own +block-indented line. For example, + +```rust +fn foo( + arg1: i32, + arg2: i32, +) -> i32 { + ... +} +``` + +Note the trailing comma on the last argument. + + +### Tuples and tuple structs + +Write the type list as you would a parameter list to a function. + +Build a tuple or tuple struct as you would call a function. + +#### Single-line + +```rust +struct Bar(Type1, Type2); + +let x = Bar(11, 22); +let y = (11, 22, 33); +``` + +### Enums + +In the declaration, put each variant on its own line, block indented. + +Format each variant accordingly as either a struct, tuple struct, or identifier, +which doesn't require special formatting (but without the `struct` keyword. + +```rust +enum FooBar { + First(u32), + Second, + Error { + err: Box<Error>, + line: u32, + }, +} +``` + +If a struct variant is [*small*](index.html#small-items), it may be formatted on +one line. In this case, do not use a trailing comma for the field list, but do +put spaces around each brace: + +```rust +enum FooBar { + Error { err: Box<Error>, line: u32 }, +} +``` + +In an enum with multiple struct variants, if any struct variant is written on +multiple lines, then the multi-line formatting should be used for all struct +variants. However, such a situation might be an indication that you should +factor out the fields of the variant into their own struct. + + +### Structs and Unions + +Struct names follow on the same line as the `struct` keyword, with the opening +brace on the same line when it fits within the right margin. All struct fields +are indented once and end with a trailing comma. The closing brace is not +indented and appears on its own line. + +```rust +struct Foo { + a: A, + b: B, +} +``` + +If and only if the type of a field does not fit within the right margin, it is +pulled down to its own line and indented again. + +```rust +struct Foo { + a: A, + long_name: + LongType, +} +``` + +Prefer using a unit struct (e.g., `struct Foo;`) to an empty struct (e.g., +`struct Foo();` or `struct Foo {}`, these only exist to simplify code +generation), but if you must use an empty struct, keep it on one line with no +space between the braces: `struct Foo;` or `struct Foo {}`. + +The same guidelines are used for untagged union declarations. + +```rust +union Foo { + a: A, + b: B, + long_name: + LongType, +} +``` + + +### Tuple structs + +Put the whole struct on one line if possible. Types in the parentheses should be +separated by a comma and space with no trailing comma. No spaces around the +parentheses or semi-colon: + +```rust +pub struct Foo(String, u8); +``` + +Prefer unit structs to empty tuple structs (these only exist to simplify code +generation), e.g., `struct Foo;` rather than `struct Foo();`. + +For more than a few fields, prefer a proper struct with named fields. Given +this, a tuple struct should always fit on one line. If it does not, block format +the fields with a field on each line and a trailing comma: + +```rust +pub struct Foo( + String, + u8, +); +``` + + +### Traits + +Trait items should be block-indented. If there are no items, the trait may be +formatted on a single line. Otherwise there should be line-breaks after the +opening brace and before the closing brace: + +```rust +trait Foo {} + +pub trait Bar { + ... +} +``` + +If the trait has bounds, there should be a space after the colon but not before +and before and after each `+`, e.g., + +```rust +trait Foo: Debug + Bar {} +``` + +Prefer not to line-break in the bounds if possible (consider using a `where` +clause). Prefer to break between bounds than to break any individual bound. If +you must break the bounds, put each bound (including the first) on its own +block-indented line, break before the `+` and put the opening brace on its own +line: + +```rust +pub trait IndexRanges: + Index<Range<usize>, Output=Self> + + Index<RangeTo<usize>, Output=Self> + + Index<RangeFrom<usize>, Output=Self> + + Index<RangeFull, Output=Self> +{ + ... +} +``` + + +### Impls + +Impl items should be block indented. If there are no items, the impl may be +formatted on a single line. Otherwise there should be line-breaks after the +opening brace and before the closing brace: + +```rust +impl Foo {} + +impl Bar for Foo { + ... +} +``` + +Avoid line-breaking in the signature if possible. If a line break is required in +a non-inherent impl, break immediately before `for`, block indent the concrete type +and put the opening brace on its own line: + +```rust +impl Bar + for Foo +{ + ... +} +``` + + +### Extern crate + +`extern crate foo;` + +Use spaces around keywords, no spaces around the semi-colon. + + +### Modules + +```rust +mod foo { +} +``` + +```rust +mod foo; +``` + +Use spaces around keywords and before the opening brace, no spaces around the +semi-colon. + +### macro\_rules! + +Use `{}` for the full definition of the macro. + +```rust +macro_rules! foo { +} +``` + + +### Generics + +Prefer to put a generics clause on one line. Break other parts of an item +declaration rather than line-breaking a generics clause. If a generics clause is +large enough to require line-breaking, you should prefer to use a `where` clause +instead. + +Do not put spaces before or after `<` nor before `>`. Only put a space after `>` +if it is followed by a word or opening brace, not an opening parenthesis. There +should be a space after each comma and no trailing comma. + +```rust +fn foo<T: Display, U: Debug>(x: Vec<T>, y: Vec<U>) ... + +impl<T: Display, U: Debug> SomeType<T, U> { ... +``` + +If the generics clause must be formatted across multiple lines, each parameter +should have its own block-indented line, there should be newlines after the +opening bracket and before the closing bracket, and the should be a trailing +comma. + +```rust +fn foo< + T: Display, + U: Debug, +>(x: Vec<T>, y: Vec<U>) ... +``` + +If an associated type is bound in a generic type, then there should be spaces on +either side of the `=`: + +```rust +<T: Example<Item = u32>> +``` + +Prefer to use single-letter names for generic parameters. + + +### `where` clauses + +These rules apply for `where` clauses on any item. + +A `where` clause may immediately follow a closing bracket of any kind. +Otherwise, it must start a new line, with no indent. Each component of a `where` +clause must be on its own line and be block indented. There should be a trailing +comma, unless the clause is terminated with a semicolon. If the `where` clause +is followed by a block (or assignment), the block should be started on a new +line. Examples: + +```rust +fn function<T, U>(args) +where + T: Bound, + U: AnotherBound, +{ + body +} + +fn foo<T>( + args +) -> ReturnType +where + T: Bound, +{ + body +} + +fn foo<T, U>( + args, +) where + T: Bound, + U: AnotherBound, +{ + body +} + +fn foo<T, U>( + args +) -> ReturnType +where + T: Bound, + U: AnotherBound; // Note, no trailing comma. + +// Note that where clauses on `type` aliases are not enforced and should not +// be used. +type Foo<T> +where + T: Bound += Bar<T>; +``` + +If a `where` clause is very short, we recommend using an inline bound on the +type parameter. + + +If a component of a `where` clause is long, it may be broken before `+` and +further block indented. Each bound should go on its own line. E.g., + +```rust +impl<T: ?Sized, Idx> IndexRanges<Idx> for T +where + T: Index<Range<Idx>, Output = Self::Output> + + Index<RangeTo<Idx>, Output = Self::Output> + + Index<RangeFrom<Idx>, Output = Self::Output> + + Index<RangeInclusive<Idx>, Output = Self::Output> + + Index<RangeToInclusive<Idx>, Output = Self::Output> + Index<RangeFull> +``` + +#### Option - `where_single_line` + +`where_single_line` is `false` by default. If `true`, then a where clause with +exactly one component may be formatted on a single line if the rest of the +item's signature is also kept on one line. In this case, there is no need for a +trailing comma and if followed by a block, no need for a newline before the +block. E.g., + +```rust +// May be single-lined. +fn foo<T>(args) -> ReturnType +where T: Bound { + body +} + +// Must be multi-lined. +fn foo<T>( + args +) -> ReturnType +where + T: Bound, +{ + body +} +``` + + +### Type aliases + +Type aliases should generally be kept on one line. If necessary to break the +line, do so after the `=`; the right-hand-side should be block indented: + +```rust +pub type Foo = Bar<T>; + +// If multi-line is required +type VeryLongType<T, U: SomeBound> = + AnEvenLongerType<T, U, Foo<T>>; +``` + +Where possible avoid `where` clauses and keep type constraints inline. Where +that is not possible split the line before and after the `where` clause (and +split the `where` clause as normal), e.g., + +```rust +type VeryLongType<T, U> +where + T: U::AnAssociatedType, + U: SomeBound, += AnEvenLongerType<T, U, Foo<T>>; +``` + + +### Associated types + +Associated types should follow the guidelines above for type aliases. Where an +associated type has a bound, there should be a space after the colon but not +before: + +```rust +pub type Foo: Bar; +``` + + +### extern items + +When writing extern items (such as `extern "C" fn`), always be explicit about +the ABI. For example, write `extern "C" fn foo ...`, not `extern fn foo ...`, or +`extern "C" { ... }`. + + +### Imports (`use` statements) + +If an import can be formatted on one line, do so. There should be no spaces +around braces. + +```rust +use a::b::c; +use a::b::d::*; +use a::b::{foo, bar, baz}; +``` + + +#### Large list imports + +Prefer to use multiple imports rather than a multi-line import. However, tools +should not split imports by default (they may offer this as an option). + +If an import does require multiple lines (either because a list of single names +does not fit within the max width, or because of the rules for nested imports +below), then break after the opening brace and before the closing brace, use a +trailing comma, and block indent the names. + + +```rust +// Prefer +foo::{long, list, of, imports}; +foo::{more, imports}; + +// If necessary +foo::{ + long, list, of, imports, more, + imports, // Note trailing comma +}; +``` + + +#### Ordering of imports + +A *group* of imports is a set of imports on the same or sequential lines. One or +more blank lines or other items (e.g., a function) separate groups of imports. + +Within a group of imports, imports must be sorted ascii-betically. Groups of +imports must not be merged or re-ordered. + + +E.g., input: + +```rust +use d; +use c; + +use b; +use a; +``` + +output: + +```rust +use c; +use d; + +use a; +use b; +``` + +Because of `macro_use`, attributes must also start a new group and prevent +re-ordering. + +Note that tools which only have access to syntax (such as Rustfmt) cannot tell +which imports are from an external crate or the std lib, etc. + + +#### Ordering list import + +Names in a list import must be sorted ascii-betically, but with `self` and +`super` first, and groups and glob imports last. This applies recursively. For +example, `a::*` comes before `b::a` but `a::b` comes before `a::*`. E.g., +`use foo::bar::{a, b::c, b::d, b::d::{x, y, z}, b::{self, r, s}};`. + + +#### Normalisation + +Tools must make the following normalisations: + +* `use a::self;` -> `use a;` +* `use a::{};` -> (nothing) +* `use a::{b};` -> `use a::b;` + +And must apply these recursively. + +Tools must not otherwise merge or un-merge import lists or adjust glob imports +(without an explicit option). + + +#### Nested imports + +If there are any nested imports in a list import, then use the multi-line form, +even if the import fits on one line. Each nested import must be on its own line, +but non-nested imports must be grouped on as few lines as possible. + +For example, + +```rust +use a::b::{ + x, y, z, + u::{...}, + w::{...}, +}; +``` + + +#### Merging/un-merging imports + +An example: + +```rust +// Un-merged +use a::b; +use a::c::d; + +// Merged +use a::{b, c::d}; +``` + +Tools must not merge or un-merge imports by default. They may offer merging or +un-merging as an option. diff --git a/src/doc/style-guide/src/principles.md b/src/doc/style-guide/src/principles.md new file mode 100644 index 00000000000..b02b3c0471f --- /dev/null +++ b/src/doc/style-guide/src/principles.md @@ -0,0 +1,51 @@ +# Guiding principles and rationale + +When deciding on style guidelines, the style team tried to be guided by the +following principles (in rough priority order): + +* readability + - scan-ability + - avoiding misleading formatting + - accessibility - readable and editable by users using the the widest + variety of hardware, including non-visual accessibility interfaces + - readability of code when quoted in rustc error messages + +* aesthetics + - sense of 'beauty' + - consistent with other languages/tools + +* specifics + - compatibility with version control practices - preserving diffs, + merge-friendliness, etc. + - preventing right-ward drift + - minimising vertical space + +* application + - ease of manual application + - ease of implementation (in Rustfmt, and in other tools/editors/code generators) + - internal consistency + - simplicity of formatting rules + + +## Overarching guidelines + +Prefer block indent over visual indent. E.g., + +```rust +// Block indent +a_function_call( + foo, + bar, +); + +// Visual indent +a_function_call(foo, + bar); +``` + +This makes for smaller diffs (e.g., if `a_function_call` is renamed in the above +example) and less rightward drift. + +Lists should have a trailing comma when followed by a newline, see the block +indent example above. This choice makes moving code (e.g., by copy and paste) +easier and makes smaller diffs. diff --git a/src/doc/style-guide/src/statements.md b/src/doc/style-guide/src/statements.md new file mode 100644 index 00000000000..29b48bb1ee0 --- /dev/null +++ b/src/doc/style-guide/src/statements.md @@ -0,0 +1,150 @@ +### Let statements + +There should be spaces after the `:` and on both sides of the `=` (if they are +present). No space before the semi-colon. + +```rust +// A comment. +let pattern: Type = expr; + +let pattern; +let pattern: Type; +let pattern = expr; +``` + +If possible the declaration should be formatted on a single line. If this is not +possible, then try splitting after the `=`, if the declaration can fit on two +lines. The expression should be block indented. + +```rust +let pattern: Type = + expr; +``` + +If the first line does not fit on a single line, then split after the colon, +using block indentation. If the type covers multiple lines, even after line- +breaking after the `:`, then the first line may be placed on the same line as +the `:`, subject to the [combining rules](https://github.com/rust-lang-nursery/fmt-rfcs/issues/61) (WIP). + + +```rust +let pattern: + Type = + expr; +``` + +e.g, + +```rust +let Foo { + f: abcd, + g: qwer, +}: Foo<Bar> = + Foo { f, g }; + +let (abcd, + defg): + Baz = +{ ... } +``` + +If the expression covers multiple lines, if the first line of the expression +fits in the remaining space, it stays on the same line as the `=`, the rest of the +expression is not indented. If the first line does not fit, then it should start +on the next lines, and should be block indented. If the expression is a block +and the type or pattern cover multiple lines, then the opening brace should be +on a new line and not indented (this provides separation for the interior of the +block from the type), otherwise the opening brace follows the `=`. + +Examples: + +```rust +let foo = Foo { + f: abcd, + g: qwer, +}; + +let foo = + ALongName { + f: abcd, + g: qwer, + }; + +let foo: Type = { + an_expression(); + ... +}; + +let foo: + ALongType = +{ + an_expression(); + ... +}; + +let Foo { + f: abcd, + g: qwer, +}: Foo<Bar> = Foo { + f: blimblimblim, + g: blamblamblam, +}; + +let Foo { + f: abcd, + g: qwer, +}: Foo<Bar> = foo( + blimblimblim, + blamblamblam, +); +``` + + +### Macros in statement position + +A macro use in statement position should use parentheses or square brackets as +delimiters and should be terminated with a semi-colon. There should be no spaces +between the name, `!`, the delimiters, or the `;`. + +```rust +// A comment. +a_macro!(...); +``` + + +### Expressions in statement position + +There should be no space between the expression and the semi-colon. + +``` +<expr>; +``` + +All expressions in statement position should be terminated with a semi-colon, +unless they end with a block or are used as the value for a block. + +E.g., + +```rust +{ + an_expression(); + expr_as_value() +} + +return foo(); + +loop { + break; +} +``` + +Use a semi-colon where an expression has void type, even if it could be +propagated. E.g., + +```rust +fn foo() { ... } + +fn bar() { + foo(); +} +``` diff --git a/src/doc/style-guide/src/types.md b/src/doc/style-guide/src/types.md new file mode 100644 index 00000000000..25861ddabb8 --- /dev/null +++ b/src/doc/style-guide/src/types.md @@ -0,0 +1,58 @@ +## Types and Bounds + +### Single line formatting + +* `[T]` no spaces +* `[T; expr]`, e.g., `[u32; 42]`, `[Vec<Foo>; 10 * 2 + foo()]` (space after colon, no spaces around square brackets) +* `*const T`, `*mut T` (no space after `*`, space before type) +* `&'a T`, `&T`, `&'a mut T`, `&mut T` (no space after `&`, single spaces separating other words) +* `unsafe extern "C" fn<'a, 'b, 'c>(T, U, V) -> W` or `fn()` (single spaces around keyowrds and sigils, and after commas, no trailing commas, no spaces around brackets) +* `!` should be treated like any other type name, `Name` +* `(A, B, C, D)` (spaces after commas, no spaces around parens, no trailing comma unless it is a one-tuple) +* `<Baz<T> as SomeTrait>::Foo::Bar` or `Foo::Bar` or `::Foo::Bar` (no spaces around `::` or angle brackets, single spaces around `as`) +* `Foo::Bar<T, U, V>` (spaces after commas, no trailing comma, no spaces around angle brackets) +* `T + T + T` (single spaces between types, and `+`). +* `impl T + T + T` (single spaces between keyword, types, and `+`). + +Parentheses used in types should not be surrounded by whitespace, e.g., `(Foo)` + + +### Line breaks + +Avoid breaking lines in types where possible. Prefer breaking at outermost scope, e.g., prefer + +```rust +Foo< + Bar, + Baz<Type1, Type2>, +> +``` + +to + +```rust +Foo<Bar, Baz< + Type1, + Type2, +>> +``` + +`[T; expr]` may be broken after the `;` if necessary. + +Function types may be broken following the rules for function declarations. + +Generic types may be broken following the rules for generics. + +Types with `+` may be broken after any `+` using block indent and breaking before the `+`. When breaking such a type, all `+`s should be line broken, e.g., + +```rust +impl Clone + + Copy + + Debug + +Box< + Clone + + Copy + + Debug +> +``` diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 661aed71298..894c7328b5f 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -218,55 +218,55 @@ pre.rust a, .sidebar h2 a, .sidebar h3 a, .mobile-topbar h2 a, -h1.fqn a, +h1 a, .search-results a, .module-item .stab, .import-item .stab, .result-name .primitive > i, .result-name .keyword > i, -.content .method .where, -.content .fn .where, -.content .where.fmt-newline { +.method .where, +.fn .where, +.where.fmt-newline { color: var(--main-color); } -.content span.enum, .content a.enum, -.content span.struct, .content a.struct, -.content span.union, .content a.union, -.content span.primitive, .content a.primitive, -.content span.type, .content a.type, -.content span.foreigntype, .content a.foreigntype { +span.enum, a.enum, +span.struct, a.struct, +span.union, a.union, +span.primitive, a.primitive, +span.type, a.type, +span.foreigntype, a.foreigntype { color: var(--type-link-color); } -.content span.trait, .content a.trait, -.content span.traitalias, .content a.traitalias { +span.trait, a.trait, +span.traitalias, a.traitalias { color: var(--trait-link-color); } -.content span.associatedtype, .content a.associatedtype, -.content span.constant, .content a.constant, -.content span.static, .content a.static { +span.associatedtype, a.associatedtype, +span.constant, a.constant, +span.static, a.static { color: var(--assoc-item-link-color); } -.content span.fn, .content a.fn, -.content .fnname, -.content span.method, .content a.method, -.content span.tymethod, .content a.tymethod { +span.fn, a.fn, +.fnname, +span.method, a.method, +span.tymethod, a.tymethod { color: var(--function-link-color); } -.content span.attr, .content a.attr, -.content span.derive, .content a.derive, -.content span.macro, .content a.macro { +span.attr, a.attr, +span.derive, a.derive, +span.macro, a.macro { color: var(--macro-link-color); } -.content span.mod, .content a.mod, .block a.current.mod { +span.mod, a.mod { color: var(--mod-link-color); } -.content span.keyword, .content a.keyword { +span.keyword, a.keyword { color: var(--keyword-link-color); } @@ -563,8 +563,6 @@ h2.location a { .rustdoc .example-wrap { display: flex; position: relative; -} -.rustdoc .example-wrap { margin-bottom: 10px; } /* For the last child of a div, the margin will be taken care of @@ -685,9 +683,9 @@ pre, .rustdoc.source .example-wrap { } /* Shift "where ..." part of method or fn definition down a line */ -.content .method .where, -.content .fn .where, -.content .where.fmt-newline { +.method .where, +.fn .where, +.where.fmt-newline { display: block; font-size: 0.875rem; } @@ -718,7 +716,6 @@ nav.sub { .source nav.sub { margin-left: 32px; } -nav.sum { text-align: right; } nav.sub form { display: inline; } a { diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 0975d497cb2..fc7713b9885 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -56,7 +56,7 @@ input:focus + .slider { h1, h2, h3, h4 { color: white; } -h1.fqn a { +h1 a { color: #fff; } h4 { diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 0180c0ead8d..dc5b8acdf53 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -451,7 +451,6 @@ function loadCss(cssFileName) { const name = item[0]; const desc = item[1]; // can be null - let klass = shortty; let path; if (shortty === "mod") { path = name + "/index.html"; @@ -459,13 +458,12 @@ function loadCss(cssFileName) { path = shortty + "." + name + ".html"; } const current_page = document.location.href.split("/").pop(); - if (path === current_page) { - klass += " current"; - } const link = document.createElement("a"); link.href = path; link.title = desc; - link.className = klass; + if (path === current_page) { + link.className = "current"; + } link.textContent = name; const li = document.createElement("li"); li.appendChild(link); diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 14a38a760d2..2e651b53874 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -1,8 +1,9 @@ //! Validates syntax inside Rust code blocks (\`\`\`rust). use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{ - emitter::Emitter, translation::Translate, Applicability, Diagnostic, Handler, - LazyFallbackBundle, + emitter::Emitter, + translation::{to_fluent_args, Translate}, + Applicability, Diagnostic, Handler, LazyFallbackBundle, }; use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; @@ -193,7 +194,7 @@ impl Emitter for BufferEmitter { fn emit_diagnostic(&mut self, diag: &Diagnostic) { let mut buffer = self.buffer.borrow_mut(); - let fluent_args = self.to_fluent_args(diag.args()); + let fluent_args = to_fluent_args(diag.args()); let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args); buffer.messages.push(format!("error from rustc: {}", translated_main_message)); diff --git a/src/test/rustdoc-gui/sidebar-links-color.goml b/src/test/rustdoc-gui/sidebar-links-color.goml index 3f719c4c4dc..18a1a3fadea 100644 --- a/src/test/rustdoc-gui/sidebar-links-color.goml +++ b/src/test/rustdoc-gui/sidebar-links-color.goml @@ -13,72 +13,72 @@ reload: // Struct assert-css: ( - ".sidebar a.struct:not(.current)", + ".sidebar .block.struct a:not(.current)", {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.struct:not(.current)" +move-cursor-to: ".sidebar .block.struct a:not(.current)" assert-css: ( - ".sidebar a.struct:hover", + ".sidebar .block.struct a:hover", {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"}, ) // Enum assert-css: ( - ".sidebar a.enum", + ".sidebar .block.enum a", {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.enum" +move-cursor-to: ".sidebar .block.enum a" assert-css: ( - ".sidebar a.enum:hover", + ".sidebar .block.enum a:hover", {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"}, ) // Union assert-css: ( - ".sidebar a.union", + ".sidebar .block.union a", {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.union" +move-cursor-to: ".sidebar .block.union a" assert-css: ( - ".sidebar a.union:hover", + ".sidebar .block.union a:hover", {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"}, ) // Trait assert-css: ( - ".sidebar a.trait", + ".sidebar .block.trait a", {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.trait" +move-cursor-to: ".sidebar .block.trait a" assert-css: ( - ".sidebar a.trait:hover", + ".sidebar .block.trait a:hover", {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"}, ) // Function assert-css: ( - ".sidebar a.fn", + ".sidebar .block.fn a", {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.fn" +move-cursor-to: ".sidebar .block.fn a" assert-css: ( - ".sidebar a.fn:hover", + ".sidebar .block.fn a:hover", {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"}, ) // Type definition assert-css: ( - ".sidebar a.type", + ".sidebar .block.type a", {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.type" +move-cursor-to: ".sidebar .block.type a" assert-css: ( - ".sidebar a.type:hover", + ".sidebar .block.type a:hover", {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"}, ) // Keyword assert-css: ( - ".sidebar a.keyword", + ".sidebar .block.keyword a", {"color": "rgb(83, 177, 219)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.keyword" +move-cursor-to: ".sidebar .block.keyword a" assert-css: ( - ".sidebar a.keyword:hover", + ".sidebar .block.keyword a:hover", {"color": "rgb(255, 180, 76)", "background-color": "rgba(0, 0, 0, 0)"}, ) @@ -88,72 +88,72 @@ reload: // Struct assert-css: ( - ".sidebar a.struct:not(.current)", + ".sidebar .block.struct a:not(.current)", {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.struct:not(.current)" +move-cursor-to: ".sidebar .block.struct a:not(.current)" assert-css: ( - ".sidebar a.struct:hover", + ".sidebar .block.struct a:hover", {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"}, ) // Enum assert-css: ( - ".sidebar a.enum", + ".sidebar .block.enum a", {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.enum" +move-cursor-to: ".sidebar .block.enum a" assert-css: ( - ".sidebar a.enum:hover", + ".sidebar .block.enum a:hover", {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"}, ) // Union assert-css: ( - ".sidebar a.union", + ".sidebar .block.union a", {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.union" +move-cursor-to: ".sidebar .block.union a" assert-css: ( - ".sidebar a.union:hover", + ".sidebar .block.union a:hover", {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"}, ) // Trait assert-css: ( - ".sidebar a.trait", + ".sidebar .block.trait a", {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.trait" +move-cursor-to: ".sidebar .block.trait a" assert-css: ( - ".sidebar a.trait:hover", + ".sidebar .block.trait a:hover", {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"}, ) // Function assert-css: ( - ".sidebar a.fn", + ".sidebar .block.fn a", {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.fn" +move-cursor-to: ".sidebar .block.fn a" assert-css: ( - ".sidebar a.fn:hover", + ".sidebar .block.fn a:hover", {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"}, ) // Type definition assert-css: ( - ".sidebar a.type", + ".sidebar .block.type a", {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.type" +move-cursor-to: ".sidebar .block.type a" assert-css: ( - ".sidebar a.type:hover", + ".sidebar .block.type a:hover", {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"}, ) // Keyword assert-css: ( - ".sidebar a.keyword", + ".sidebar .block.keyword a", {"color": "rgb(253, 191, 53)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.keyword" +move-cursor-to: ".sidebar .block.keyword a" assert-css: ( - ".sidebar a.keyword:hover", + ".sidebar .block.keyword a:hover", {"color": "rgb(253, 191, 53)", "background-color": "rgb(68, 68, 68)"}, ) @@ -163,71 +163,71 @@ reload: // Struct assert-css: ( - ".sidebar a.struct:not(.current)", + ".sidebar .block.struct a:not(.current)", {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.struct:not(.current)" +move-cursor-to: ".sidebar .block.struct a:not(.current)" assert-css: ( - ".sidebar a.struct:hover", + ".sidebar .block.struct a:hover", {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"}, ) // Enum assert-css: ( - ".sidebar a.enum", + ".sidebar .block.enum a", {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.enum" +move-cursor-to: ".sidebar .block.enum a" assert-css: ( - ".sidebar a.enum:hover", + ".sidebar .block.enum a:hover", {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"}, ) // Union assert-css: ( - ".sidebar a.union", + ".sidebar .block.union a", {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.union" +move-cursor-to: ".sidebar .block.union a" assert-css: ( - ".sidebar a.union:hover", + ".sidebar .block.union a:hover", {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"}, ) // Trait assert-css: ( - ".sidebar a.trait", + ".sidebar .block.trait a", {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.trait" +move-cursor-to: ".sidebar .block.trait a" assert-css: ( - ".sidebar a.trait:hover", + ".sidebar .block.trait a:hover", {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"}, ) // Function assert-css: ( - ".sidebar a.fn", + ".sidebar .block.fn a", {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.fn" +move-cursor-to: ".sidebar .block.fn a" assert-css: ( - ".sidebar a.fn:hover", + ".sidebar .block.fn a:hover", {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"}, ) // Type definition assert-css: ( - ".sidebar a.type", + ".sidebar .block.type a", {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.type" +move-cursor-to: ".sidebar .block.type a" assert-css: ( - ".sidebar a.type:hover", + ".sidebar .block.type a:hover", {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"}, ) // Keyword assert-css: ( - ".sidebar a.keyword", + ".sidebar .block.keyword a", {"color": "rgb(53, 109, 164)", "background-color": "rgba(0, 0, 0, 0)"}, ) -move-cursor-to: ".sidebar a.keyword" +move-cursor-to: ".sidebar .block.keyword a" assert-css: ( - ".sidebar a.keyword:hover", + ".sidebar .block.keyword a:hover", {"color": "rgb(53, 109, 164)", "background-color": "rgb(255, 255, 255)"}, ) diff --git a/src/test/rustdoc-gui/sidebar-macro-reexport.goml b/src/test/rustdoc-gui/sidebar-macro-reexport.goml index 01282f2ffeb..b5c1b6a4390 100644 --- a/src/test/rustdoc-gui/sidebar-macro-reexport.goml +++ b/src/test/rustdoc-gui/sidebar-macro-reexport.goml @@ -1,5 +1,5 @@ // This test ensures that the reexport of a macro doesn't make the original macro // displayed twice in the sidebar. goto: "file://" + |DOC_PATH| + "/test_docs/macro.repro.html" -wait-for: ".sidebar-elems .macro .macro" +wait-for: ".sidebar-elems .block.macro a" assert-count: ("//*[@class='sidebar-elems']//*[@class='block macro']//a[text()='repro']", 1) diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs index 2319de55683..4b1e04234c8 100644 --- a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs +++ b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.rs @@ -1,11 +1,12 @@ +// check-pass // normalize-stderr-test: "`.*`" -> "`DEF_ID`" // normalize-stdout-test: "`.*`" -> "`DEF_ID`" // edition:2018 pub async fn f() -> impl std::fmt::Debug { + // rustdoc doesn't care that this is infinitely sized #[derive(Debug)] enum E { - //~^ ERROR recursive type `f::{closure#0}::E` has infinite size This(E), Unit, } diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr deleted file mode 100644 index e6ab67d59ce..00000000000 --- a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait-return.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0072]: recursive type `DEF_ID` has infinite size - --> $DIR/infinite-recursive-type-impl-trait-return.rs:7:5 - | -LL | enum E { - | ^^^^^^ -LL | -LL | This(E), - | - recursive without indirection - | -help: insert some indirection (e.g., a `DEF_ID`) to break the cycle - | -LL | This(Box<E>), - | ++++ + - -error: aborting due to previous error - -For more information about this error, try `DEF_ID`. diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs index b3a7ee56313..ac79582fb3f 100644 --- a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs +++ b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.rs @@ -1,6 +1,8 @@ +// check-pass + fn f() -> impl Sized { + // rustdoc doesn't care that this is infinitely sized enum E { - //~^ ERROR recursive type `f::E` has infinite size V(E), } unimplemented!() diff --git a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr b/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr deleted file mode 100644 index 165ff678372..00000000000 --- a/src/test/rustdoc-ui/infinite-recursive-type-impl-trait.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0072]: recursive type `f::E` has infinite size - --> $DIR/infinite-recursive-type-impl-trait.rs:2:5 - | -LL | enum E { - | ^^^^^^ -LL | -LL | V(E), - | - recursive without indirection - | -help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle - | -LL | V(Box<E>), - | ++++ + - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0072`. diff --git a/src/test/rustdoc-ui/z-help.stdout b/src/test/rustdoc-ui/z-help.stdout index 65536cb3aa1..dbf3a8f00ee 100644 --- a/src/test/rustdoc-ui/z-help.stdout +++ b/src/test/rustdoc-ui/z-help.stdout @@ -1,8 +1,8 @@ -Z allow-features=val -- only allow the listed language features to be enabled in code (space separated) -Z always-encode-mir=val -- encode MIR of all functions into the crate metadata (default: no) - -Z assume-incomplete-release=val -- make cfg(version) treat the current version as incomplete (default: no) -Z asm-comments=val -- generate comments into the assembly (may change behavior) (default: no) -Z assert-incr-state=val -- assert that the incremental cache is in given state: either `loaded` or `not-loaded`. + -Z assume-incomplete-release=val -- make cfg(version) treat the current version as incomplete (default: no) -Z binary-dep-depinfo=val -- include artifacts (sysroot, crate dependencies) used during compilation in dep-info (default: no) -Z box-noalias=val -- emit noalias metadata for box (default: yes) -Z branch-protection=val -- set options for branch target identification and pointer authentication on AArch64 @@ -17,6 +17,7 @@ -Z deduplicate-diagnostics=val -- deduplicate identical diagnostics (default: yes) -Z dep-info-omit-d-target=val -- in dep-info output, omit targets for tracking dependencies of the dep-info files themselves (default: no) -Z dep-tasks=val -- print tasks that execute and the color their dep node gets (requires debug build) (default: no) + -Z diagnostic-width=val -- set the current output width for diagnostic truncation -Z dlltool=val -- import library generation tool (windows-gnu only) -Z dont-buffer-diagnostics=val -- emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) (default: no) -Z drop-tracking=val -- enables drop tracking in generators (default: no) @@ -54,11 +55,11 @@ -Z incremental-info=val -- print high-level information about incremental reuse (or the lack thereof) (default: no) -Z incremental-relative-spans=val -- hash spans relative to their parent item for incr. comp. (default: no) -Z incremental-verify-ich=val -- verify incr. comp. hashes of green query instances (default: no) + -Z inline-in-all-cgus=val -- control whether `#[inline]` functions are in all CGUs -Z inline-llvm=val -- enable LLVM inlining (default: yes) -Z inline-mir=val -- enable MIR inlining (default: no) - -Z inline-mir-threshold=val -- a default MIR inlining threshold (default: 50) -Z inline-mir-hint-threshold=val -- inlining threshold for functions with inline hint (default: 100) - -Z inline-in-all-cgus=val -- control whether `#[inline]` functions are in all CGUs + -Z inline-mir-threshold=val -- a default MIR inlining threshold (default: 50) -Z input-stats=val -- gather statistics about the input (default: no) -Z instrument-coverage=val -- instrument the generated code to support LLVM source-based code coverage reports (note, the compiler build config must include `profiler = true`); implies `-C symbol-mangling-version=v0`. Optional values are: `=all` (implicit value) @@ -67,6 +68,7 @@ `=off` (default) -Z instrument-mcount=val -- insert function instrument code for mcount-based tracing (default: no) -Z keep-hygiene-data=val -- keep hygiene data after analysis (default: no) + -Z layout-seed=val -- seed layout randomization -Z link-native-libraries=val -- link native libraries in the linker invocation (default: yes) -Z link-only=val -- link the `.rlink` file generated by `-Z no-link` (default: no) -Z llvm-plugins=val -- a list LLVM plugins to enable (space separated) @@ -78,8 +80,8 @@ -Z meta-stats=val -- gather metadata statistics (default: no) -Z mir-emit-retag=val -- emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 (default: no) -Z mir-enable-passes=val -- use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be enabled, overriding all other checks. Passes that are not specified are enabled or disabled by other flags as usual. - -Z mir-pretty-relative-line-numbers=val -- use line numbers relative to the function in mir pretty printing -Z mir-opt-level=val -- MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds) + -Z mir-pretty-relative-line-numbers=val -- use line numbers relative to the function in mir pretty printing -Z move-size-limit=val -- the size at which the `large_assignments` lint starts to be emitted -Z mutable-noalias=val -- emit noalias metadata for mutable references (default: yes) -Z nll-facts=val -- dump facts from NLL analysis into side files (default: no) @@ -91,12 +93,11 @@ -Z no-leak-check=val -- disable the 'leak check' for subtyping; unsound, but useful for tests -Z no-link=val -- compile without linking -Z no-parallel-llvm=val -- run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO) - -Z no-unique-section-names=val -- do not use unique names for text and data sections when -Z function-sections is used -Z no-profiler-runtime=val -- prevent automatic injection of the profiler_builtins crate + -Z no-unique-section-names=val -- do not use unique names for text and data sections when -Z function-sections is used -Z normalize-docs=val -- normalize associated items in rustdoc when generating documentation -Z oom=val -- panic strategy for out-of-memory handling -Z osx-rpath-install-name=val -- pass `-install_name @rpath/...` to the macOS linker (default: no) - -Z diagnostic-width=val -- set the current output width for diagnostic truncation -Z packed-bundled-libs=val -- change rlib format to store native libraries as archives -Z panic-abort-tests=val -- support compiling tests with panic=abort (default: no) -Z panic-in-drop=val -- panic strategy for panics in drops @@ -120,15 +121,13 @@ -Z profile=val -- insert profiling code (default: no) -Z profile-closures=val -- profile size of closures -Z profile-emit=val -- file path to emit profiling data at runtime when using 'profile' (default based on relative source path) - -Z profiler-runtime=val -- name of the profiler runtime crate to automatically inject (default: `profiler_builtins`) -Z profile-sample-use=val -- use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO) + -Z profiler-runtime=val -- name of the profiler runtime crate to automatically inject (default: `profiler_builtins`) -Z query-dep-graph=val -- enable queries of the dependency graph for regression testing (default: no) -Z randomize-layout=val -- randomize the layout of types (default: no) - -Z layout-seed=val -- seed layout randomization -Z relax-elf-relocations=val -- whether ELF relocations can be relaxed -Z relro-level=val -- choose which RELRO level to use -Z remap-cwd-prefix=val -- remap paths under the current working directory to this path prefix - -Z simulate-remapped-rust-src-base=val -- simulate the effect of remap-debuginfo = true at bootstrapping by remapping path to rust's source base directory. only meant for testing purposes -Z report-delayed-bugs=val -- immediately print bugs registered with `delay_span_bug` (default: no) -Z sanitizer=val -- use a sanitizer -Z sanitizer-memory-track-origins=val -- enable origins tracking in MemorySanitizer @@ -136,22 +135,20 @@ -Z saturating-float-casts=val -- make float->int casts UB-free: numbers outside the integer type's range are clipped to the max/min integer respectively, and NaN is mapped to 0 (default: yes) -Z save-analysis=val -- write syntax and type analysis (in JSON format) information, in addition to normal output (default: no) -Z self-profile=val -- run the self profiler and output the raw event data - -Z self-profile-events=val -- specify the events recorded by the self profiler; - for example: `-Z self-profile-events=default,query-keys` - all options: none, all, default, generic-activity, query-provider, query-cache-hit - query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes -Z self-profile-counter=val -- counter used by the self profiler (default: `wall-time`), one of: `wall-time` (monotonic clock, i.e. `std::time::Instant`) `instructions:u` (retired instructions, userspace-only) `instructions-minus-irqs:u` (subtracting hardware interrupt counts for extra accuracy) + -Z self-profile-events=val -- specify the events recorded by the self profiler; + for example: `-Z self-profile-events=default,query-keys` + all options: none, all, default, generic-activity, query-provider, query-cache-hit + query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes -Z share-generics=val -- make the current crate share its generic instantiations -Z show-span=val -- show spans for compiler debugging (expr|pat|ty) + -Z simulate-remapped-rust-src-base=val -- simulate the effect of remap-debuginfo = true at bootstrapping by remapping path to rust's source base directory. only meant for testing purposes -Z span-debug=val -- forward proc_macro::Span's `Debug` impl to `Span` -Z span-free-formats=val -- exclude spans when debug-printing compiler state (default: no) - -Z src-hash-algorithm=val -- hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`) - -Z stack-protector=val -- control stack smash protection strategy (`rustc --print stack-protector-strategies` for details) - -Z strict-init-checks=val -- control if mem::uninitialized and mem::zeroed panic on more UB - -Z strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`) + -Z split-dwarf-inlining=val -- provide minimal debug info in the object/executable to facilitate online symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF -Z split-dwarf-kind=val -- split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) (default: `split`) @@ -159,14 +156,13 @@ file which is ignored by the linker `single`: sections which do not require relocation are written into object file but ignored by the linker - -Z split-dwarf-inlining=val -- provide minimal debug info in the object/executable to facilitate online symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF + -Z src-hash-algorithm=val -- hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`) + -Z stack-protector=val -- control stack smash protection strategy (`rustc --print stack-protector-strategies` for details) + -Z strict-init-checks=val -- control if mem::uninitialized and mem::zeroed panic on more UB + -Z strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`) -Z symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0') -Z teach=val -- show extended diagnostic help (default: no) -Z temps-dir=val -- the directory the intermediate files are written to - -Z translate-lang=val -- language identifier for diagnostic output - -Z translate-additional-ftl=val -- additional fluent translation to preferentially use (for testing translation) - -Z translate-directionality-markers=val -- emit directionality isolation markers in translated diagnostics - -Z tune-cpu=val -- select processor to schedule for (`rustc --print target-cpus` for details) -Z thinlto=val -- enable ThinLTO when possible -Z thir-unsafeck=val -- use the THIR unsafety checker (default: no) -Z threads=val -- use a thread pool with N threads @@ -174,10 +170,14 @@ -Z time-passes=val -- measure time of each rustc pass (default: no) -Z tls-model=val -- choose the TLS model to use (`rustc --print tls-models` for details) -Z trace-macros=val -- for every macro invocation, print its name and arguments (default: no) + -Z translate-additional-ftl=val -- additional fluent translation to preferentially use (for testing translation) + -Z translate-directionality-markers=val -- emit directionality isolation markers in translated diagnostics + -Z translate-lang=val -- language identifier for diagnostic output -Z translate-remapped-path-to-local-path=val -- translate remapped paths into local paths when possible (default: yes) -Z trap-unreachable=val -- generate trap instructions for unreachable intrinsics (default: use target setting, usually yes) -Z treat-err-as-bug=val -- treat error number `val` that occurs as bug -Z trim-diagnostic-paths=val -- in diagnostics, use heuristics to shorten paths referring to items + -Z tune-cpu=val -- select processor to schedule for (`rustc --print target-cpus` for details) -Z ui-testing=val -- emit compiler diagnostics in a form suitable for UI testing (default: no) -Z uninit-const-chunk-threshold=val -- allow generating const initializers with mixed init/uninit chunks, and set the maximum number of chunks for which this is allowed (default: 16) -Z unleash-the-miri-inside-of-you=val -- take the brakes off const evaluation. NOTE: this is unsound (default: no) diff --git a/src/test/ui-fulldeps/internal-lints/diagnostics.rs b/src/test/ui-fulldeps/internal-lints/diagnostics.rs index 9a5100ce17f..462f5e78498 100644 --- a/src/test/ui-fulldeps/internal-lints/diagnostics.rs +++ b/src/test/ui-fulldeps/internal-lints/diagnostics.rs @@ -13,7 +13,7 @@ extern crate rustc_span; use rustc_errors::{ AddToDiagnostic, IntoDiagnostic, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, Handler, fluent + ErrorGuaranteed, Handler, fluent, SubdiagnosticMessage, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::Span; @@ -52,7 +52,10 @@ impl<'a> IntoDiagnostic<'a, ErrorGuaranteed> for TranslatableInIntoDiagnostic { pub struct UntranslatableInAddToDiagnostic; impl AddToDiagnostic for UntranslatableInAddToDiagnostic { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { diag.note("untranslatable diagnostic"); //~^ ERROR diagnostics should be created using translatable messages } @@ -61,7 +64,10 @@ impl AddToDiagnostic for UntranslatableInAddToDiagnostic { pub struct TranslatableInAddToDiagnostic; impl AddToDiagnostic for TranslatableInAddToDiagnostic { - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { diag.note(fluent::compiletest::note); } } diff --git a/src/test/ui-fulldeps/internal-lints/diagnostics.stderr b/src/test/ui-fulldeps/internal-lints/diagnostics.stderr index f5f92ac1e7f..ac820a79db2 100644 --- a/src/test/ui-fulldeps/internal-lints/diagnostics.stderr +++ b/src/test/ui-fulldeps/internal-lints/diagnostics.stderr @@ -11,13 +11,13 @@ LL | #![deny(rustc::untranslatable_diagnostic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:56:14 + --> $DIR/diagnostics.rs:59:14 | LL | diag.note("untranslatable diagnostic"); | ^^^^ error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls - --> $DIR/diagnostics.rs:70:25 + --> $DIR/diagnostics.rs:76:25 | LL | let _diag = handler.struct_err(fluent::compiletest::example); | ^^^^^^^^^^ @@ -29,13 +29,13 @@ LL | #![deny(rustc::diagnostic_outside_of_impl)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls - --> $DIR/diagnostics.rs:73:25 + --> $DIR/diagnostics.rs:79:25 | LL | let _diag = handler.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:73:25 + --> $DIR/diagnostics.rs:79:25 | LL | let _diag = handler.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 1dc71abc104..e873c36e0b3 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -678,3 +678,74 @@ enum ExampleEnum { struct RawIdentDiagnosticArg { pub r#type: String, } + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBad { + #[subdiagnostic(bad)] +//~^ ERROR `#[subdiagnostic(bad)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBadStr { + #[subdiagnostic = "bad"] +//~^ ERROR `#[subdiagnostic = ...]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBadTwice { + #[subdiagnostic(bad, bad)] +//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticBadLitStr { + #[subdiagnostic("bad")] +//~^ ERROR `#[subdiagnostic("...")]` is not a valid attribute + note: Note, +} + +#[derive(LintDiagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticEagerLint { + #[subdiagnostic(eager)] +//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticEagerCorrect { + #[subdiagnostic(eager)] + note: Note, +} + +// Check that formatting of `correct` in suggestion doesn't move the binding for that field, making +// the `set_arg` call a compile error; and that isn't worked around by moving the `set_arg` call +// after the `span_suggestion` call - which breaks eager translation. + +#[derive(Subdiagnostic)] +#[suggestion_short( + parser::use_instead, + applicability = "machine-applicable", + code = "{correct}" +)] +pub(crate) struct SubdiagnosticWithSuggestion { + #[primary_span] + span: Span, + invalid: String, + correct: String, +} + +#[derive(Diagnostic)] +#[diag(compiletest::example)] +struct SubdiagnosticEagerSuggestion { + #[subdiagnostic(eager)] + sub: SubdiagnosticWithSuggestion, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 167198b47f2..7a42d618707 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -533,6 +533,46 @@ LL | #[label] | = help: `#[label]` and `#[suggestion]` can only be applied to fields +error: `#[subdiagnostic(bad)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:685:21 + | +LL | #[subdiagnostic(bad)] + | ^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic = ...]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:693:5 + | +LL | #[subdiagnostic = "bad"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:701:5 + | +LL | #[subdiagnostic(bad, bad)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic("...")]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:709:21 + | +LL | #[subdiagnostic("bad")] + | ^^^^^ + | + = help: `eager` is the only supported nested attribute for `subdiagnostic` + +error: `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:717:5 + | +LL | #[subdiagnostic(eager)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: eager subdiagnostics are not supported on lints + error: cannot find attribute `nonsense` in this scope --> $DIR/diagnostic-derive.rs:55:3 | @@ -607,7 +647,7 @@ LL | arg: impl IntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg` = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 75 previous errors +error: aborting due to 80 previous errors Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-types/issue-85103.rs b/src/test/ui/associated-types/issue-85103.rs index c5e13856178..9c6a419e9f7 100644 --- a/src/test/ui/associated-types/issue-85103.rs +++ b/src/test/ui/associated-types/issue-85103.rs @@ -4,6 +4,6 @@ use std::borrow::Cow; #[rustc_layout(debug)] type Edges<'a, E> = Cow<'a, [E]>; -//~^ ERROR layout error: NormalizationFailure +//~^ 6:1: 6:18: unable to determine layout for `<[E] as ToOwned>::Owned` because `<[E] as ToOwned>::Owned` cannot be normalized fn main() {} diff --git a/src/test/ui/associated-types/issue-85103.stderr b/src/test/ui/associated-types/issue-85103.stderr index bddd1dce8e6..17f7148074c 100644 --- a/src/test/ui/associated-types/issue-85103.stderr +++ b/src/test/ui/associated-types/issue-85103.stderr @@ -1,4 +1,4 @@ -error: layout error: NormalizationFailure(<[E] as std::borrow::ToOwned>::Owned, Type(<[E] as std::borrow::ToOwned>::Owned)) +error: unable to determine layout for `<[E] as ToOwned>::Owned` because `<[E] as ToOwned>::Owned` cannot be normalized --> $DIR/issue-85103.rs:6:1 | LL | type Edges<'a, E> = Cow<'a, [E]>; diff --git a/src/test/ui/consts/issue-102117.rs b/src/test/ui/consts/issue-102117.rs new file mode 100644 index 00000000000..b77342c4135 --- /dev/null +++ b/src/test/ui/consts/issue-102117.rs @@ -0,0 +1,30 @@ +#![feature(inline_const, const_type_id)] + +use std::alloc::Layout; +use std::any::TypeId; +use std::mem::transmute; +use std::ptr::drop_in_place; + +pub struct VTable { + layout: Layout, + type_id: TypeId, + drop_in_place: unsafe fn(*mut ()), +} + +impl VTable { + pub fn new<T>() -> &'static Self { + const { + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + &VTable { + layout: Layout::new::<T>(), + type_id: TypeId::of::<T>(), + drop_in_place: unsafe { + transmute::<unsafe fn(*mut T), unsafe fn(*mut ())>(drop_in_place::<T>) + }, + } + } + } +} + +fn main() {} diff --git a/src/test/ui/consts/issue-102117.stderr b/src/test/ui/consts/issue-102117.stderr new file mode 100644 index 00000000000..eb4b329bd81 --- /dev/null +++ b/src/test/ui/consts/issue-102117.stderr @@ -0,0 +1,37 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/issue-102117.rs:16:9 + | +LL | / const { +LL | | +LL | | +LL | | &VTable { +... | +LL | | } +LL | | } + | |_________^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | pub fn new<T: 'static>() -> &'static Self { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/issue-102117.rs:16:9 + | +LL | / const { +LL | | +LL | | +LL | | &VTable { +... | +LL | | } +LL | | } + | |_________^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | pub fn new<T: 'static>() -> &'static Self { + | +++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/src/test/ui/issues/issue-72554.rs b/src/test/ui/issues/issue-72554.rs index 7287639c61d..54f7e9ac592 100644 --- a/src/test/ui/issues/issue-72554.rs +++ b/src/test/ui/issues/issue-72554.rs @@ -3,7 +3,6 @@ use std::collections::BTreeSet; #[derive(Hash)] pub enum ElemDerived { //~^ ERROR recursive type `ElemDerived` has infinite size - //~| ERROR cycle detected when computing drop-check constraints for `ElemDerived` A(ElemDerived) } diff --git a/src/test/ui/issues/issue-72554.stderr b/src/test/ui/issues/issue-72554.stderr index bc85cd7b18d..d12be539f7c 100644 --- a/src/test/ui/issues/issue-72554.stderr +++ b/src/test/ui/issues/issue-72554.stderr @@ -3,7 +3,7 @@ error[E0072]: recursive type `ElemDerived` has infinite size | LL | pub enum ElemDerived { | ^^^^^^^^^^^^^^^^^^^^ -... +LL | LL | A(ElemDerived) | ----------- recursive without indirection | @@ -12,20 +12,6 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle LL | A(Box<ElemDerived>) | ++++ + -error[E0391]: cycle detected when computing drop-check constraints for `ElemDerived` - --> $DIR/issue-72554.rs:4:1 - | -LL | pub enum ElemDerived { - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: ...which immediately requires computing drop-check constraints for `ElemDerived` again -note: cycle used when computing drop-check constraints for `Elem` - --> $DIR/issue-72554.rs:11:1 - | -LL | pub enum Elem { - | ^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0072, E0391. -For more information about an error, try `rustc --explain E0072`. +For more information about this error, try `rustc --explain E0072`. diff --git a/src/test/ui/parser/bad-let-as-field.rs b/src/test/ui/parser/bad-let-as-field.rs new file mode 100644 index 00000000000..fec2bc25617 --- /dev/null +++ b/src/test/ui/parser/bad-let-as-field.rs @@ -0,0 +1,6 @@ +struct Foo { + let: i32, + //~^ ERROR expected identifier, found keyword +} + +fn main() {} diff --git a/src/test/ui/parser/bad-let-as-field.stderr b/src/test/ui/parser/bad-let-as-field.stderr new file mode 100644 index 00000000000..57def42b1ee --- /dev/null +++ b/src/test/ui/parser/bad-let-as-field.stderr @@ -0,0 +1,15 @@ +error: expected identifier, found keyword `let` + --> $DIR/bad-let-as-field.rs:2:5 + | +LL | struct Foo { + | --- while parsing this struct +LL | let: i32, + | ^^^ expected identifier, found keyword + | +help: escape `let` to use it as an identifier + | +LL | r#let: i32, + | ++ + +error: aborting due to previous error + diff --git a/src/test/ui/parser/removed-syntax-field-let-2.rs b/src/test/ui/parser/removed-syntax-field-let-2.rs new file mode 100644 index 00000000000..7ff91b476ae --- /dev/null +++ b/src/test/ui/parser/removed-syntax-field-let-2.rs @@ -0,0 +1,12 @@ +struct Foo { + let x: i32, + //~^ ERROR expected identifier, found keyword + let y: i32, + //~^ ERROR expected identifier, found keyword +} + +fn main() { + let _ = Foo { + //~^ ERROR missing fields `x` and `y` in initializer of `Foo` + }; +} diff --git a/src/test/ui/parser/removed-syntax-field-let-2.stderr b/src/test/ui/parser/removed-syntax-field-let-2.stderr new file mode 100644 index 00000000000..fda0919b9b6 --- /dev/null +++ b/src/test/ui/parser/removed-syntax-field-let-2.stderr @@ -0,0 +1,33 @@ +error: expected identifier, found keyword `let` + --> $DIR/removed-syntax-field-let-2.rs:2:5 + | +LL | let x: i32, + | ^^^- + | | + | expected identifier, found keyword + | help: remove this `let` keyword + | + = note: the `let` keyword is not allowed in `struct` fields + = note: see <https://doc.rust-lang.org/book/ch05-01-defining-structs.html> for more information + +error: expected identifier, found keyword `let` + --> $DIR/removed-syntax-field-let-2.rs:4:5 + | +LL | let y: i32, + | ^^^- + | | + | expected identifier, found keyword + | help: remove this `let` keyword + | + = note: the `let` keyword is not allowed in `struct` fields + = note: see <https://doc.rust-lang.org/book/ch05-01-defining-structs.html> for more information + +error[E0063]: missing fields `x` and `y` in initializer of `Foo` + --> $DIR/removed-syntax-field-let-2.rs:9:13 + | +LL | let _ = Foo { + | ^^^ missing `x` and `y` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0063`. diff --git a/src/test/ui/parser/removed-syntax-field-let.stderr b/src/test/ui/parser/removed-syntax-field-let.stderr index bd1b23f7e3b..9bc18dabd6e 100644 --- a/src/test/ui/parser/removed-syntax-field-let.stderr +++ b/src/test/ui/parser/removed-syntax-field-let.stderr @@ -2,15 +2,13 @@ error: expected identifier, found keyword `let` --> $DIR/removed-syntax-field-let.rs:2:5 | LL | let foo: (), - | ^^^ expected identifier, found keyword + | ^^^- + | | + | expected identifier, found keyword + | help: remove this `let` keyword | = note: the `let` keyword is not allowed in `struct` fields = note: see <https://doc.rust-lang.org/book/ch05-01-defining-structs.html> for more information -help: remove the let, the `let` keyword is not allowed in struct field definitions - | -LL - let foo: (), -LL + foo: (), - | error: aborting due to previous error diff --git a/src/test/ui/process/process-panic-after-fork.rs b/src/test/ui/process/process-panic-after-fork.rs index d0a938c03e8..6d4d2492225 100644 --- a/src/test/ui/process/process-panic-after-fork.rs +++ b/src/test/ui/process/process-panic-after-fork.rs @@ -5,7 +5,6 @@ // ignore-sgx no libc // ignore-emscripten no processes // ignore-sgx no processes -// ignore-android: FIXME(#85261) // ignore-fuchsia no fork #![feature(rustc_private)] @@ -79,7 +78,49 @@ unsafe impl<A:GlobalAlloc> GlobalAlloc for PidChecking<A> { fn expect_aborted(status: ExitStatus) { dbg!(status); let signal = status.signal().expect("expected child process to die of signal"); + + #[cfg(not(target_os = "android"))] assert!(signal == libc::SIGABRT || signal == libc::SIGILL || signal == libc::SIGTRAP); + + #[cfg(target_os = "android")] + { + // Android signals an abort() call with SIGSEGV at address 0xdeadbaad + // See e.g. https://groups.google.com/g/android-ndk/c/laW1CJc7Icc + assert!(signal == libc::SIGSEGV); + + // Additional checks performed: + // 1. Find last tombstone (similar to coredump but in text format) from the + // same executable (path) as we are (must be because of usage of fork): + // This ensures that we look into the correct tombstone. + // 2. Cause of crash is a SIGSEGV with address 0xdeadbaad. + // 3. libc::abort call is in one of top two functions on callstack. + // The last two steps distinguish between a normal SIGSEGV and one caused + // by libc::abort. + + let this_exe = std::env::current_exe().unwrap().into_os_string().into_string().unwrap(); + let exe_string = format!(">>> {this_exe} <<<"); + let tombstone = (0..100) + .map(|n| format!("/data/tombstones/tombstone_{n:02}")) + .filter(|f| std::path::Path::new(&f).exists()) + .map(|f| std::fs::read_to_string(&f).expect("Cannot read tombstone file")) + .filter(|f| f.contains(&exe_string)) + .last() + .expect("no tombstone found"); + + println!("Content of tombstone:\n{tombstone}"); + + assert!( + tombstone.contains("signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad") + ); + let abort_on_top = tombstone + .lines() + .skip_while(|l| !l.contains("backtrace:")) + .skip(1) + .take_while(|l| l.starts_with(" #")) + .take(2) + .any(|f| f.contains("/system/lib/libc.so (abort")); + assert!(abort_on_top); + } } fn main() { diff --git a/src/test/ui/variance/variance-regions-unused-indirect.rs b/src/test/ui/variance/variance-regions-unused-indirect.rs index 1514e39563e..6c2c24ddbc7 100644 --- a/src/test/ui/variance/variance-regions-unused-indirect.rs +++ b/src/test/ui/variance/variance-regions-unused-indirect.rs @@ -1,6 +1,7 @@ // Test that disallow lifetime parameters that are unused. enum Foo<'a> { //~ ERROR parameter `'a` is never used + //~^ ERROR recursive types `Foo` and `Bar` have infinite size Foo1(Bar<'a>) } diff --git a/src/test/ui/variance/variance-regions-unused-indirect.stderr b/src/test/ui/variance/variance-regions-unused-indirect.stderr index 93710cc133a..14fdd849294 100644 --- a/src/test/ui/variance/variance-regions-unused-indirect.stderr +++ b/src/test/ui/variance/variance-regions-unused-indirect.stderr @@ -1,3 +1,26 @@ +error[E0072]: recursive types `Foo` and `Bar` have infinite size + --> $DIR/variance-regions-unused-indirect.rs:3:1 + | +LL | enum Foo<'a> { + | ^^^^^^^^^^^^ +LL | +LL | Foo1(Bar<'a>) + | ------- recursive without indirection +... +LL | enum Bar<'a> { + | ^^^^^^^^^^^^ +LL | Bar1(Foo<'a>) + | ------- recursive without indirection + | +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle + | +LL ~ Foo1(Box<Bar<'a>>) +LL | } +LL | +LL | enum Bar<'a> { +LL ~ Bar1(Box<Foo<'a>>) + | + error[E0392]: parameter `'a` is never used --> $DIR/variance-regions-unused-indirect.rs:3:10 | @@ -7,13 +30,14 @@ LL | enum Foo<'a> { = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: parameter `'a` is never used - --> $DIR/variance-regions-unused-indirect.rs:7:10 + --> $DIR/variance-regions-unused-indirect.rs:8:10 | LL | enum Bar<'a> { | ^^ unused parameter | = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0392`. +Some errors have detailed explanations: E0072, E0392. +For more information about an error, try `rustc --explain E0072`. diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 3cdf1ab25dc4fe56f890e8c7330d53a23ad905d +Subproject b8f30cb23c4e5f20854a4f683325782b7cff983 diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs new file mode 100644 index 00000000000..c9f1dfb707f --- /dev/null +++ b/src/tools/tidy/src/alphabetical.rs @@ -0,0 +1,113 @@ +//! Checks that a list of items is in alphabetical order +//! +//! To use, use the following annotation in the code: +//! ```rust +//! // tidy-alphabetical-start +//! fn aaa() {} +//! fn eee() {} +//! fn z() {} +//! // tidy-alphabetical-end +//! ``` +//! +//! The following lines are ignored: +//! - Lines that are indented with more or less spaces than the first line +//! - Lines starting with `//`, `#[`, `)`, `]`, `}` if the comment has the same indentation as +//! the first line +//! +//! If a line ends with an opening bracket, the line is ignored and the next line will have +//! its extra indentation ignored. + +use std::{fmt::Display, path::Path}; + +use crate::walk::{filter_dirs, walk}; + +fn indentation(line: &str) -> usize { + line.find(|c| c != ' ').unwrap_or(0) +} + +fn is_close_bracket(c: char) -> bool { + matches!(c, ')' | ']' | '}') +} + +const START_COMMENT: &str = "// tidy-alphabetical-start"; +const END_COMMENT: &str = "// tidy-alphabetical-end"; + +fn check_section<'a>( + file: impl Display, + lines: impl Iterator<Item = (usize, &'a str)>, + bad: &mut bool, +) { + let content_lines = lines.take_while(|(_, line)| !line.contains(END_COMMENT)); + + let mut prev_line = String::new(); + let mut first_indent = None; + let mut in_split_line = None; + + for (line_idx, line) in content_lines { + if line.contains(START_COMMENT) { + tidy_error!( + bad, + "{file}:{} found `// tidy-alphabetical-start` expecting `// tidy-alphabetical-end`", + line_idx + ) + } + + let indent = first_indent.unwrap_or_else(|| { + let indent = indentation(line); + first_indent = Some(indent); + indent + }); + + let line = if let Some(prev_split_line) = in_split_line { + in_split_line = None; + format!("{prev_split_line}{}", line.trim_start()) + } else { + line.to_string() + }; + + if indentation(&line) != indent { + continue; + } + + let trimmed_line = line.trim_start_matches(' '); + + if trimmed_line.starts_with("//") + || trimmed_line.starts_with("#[") + || trimmed_line.starts_with(is_close_bracket) + { + continue; + } + + if line.trim_end().ends_with('(') { + in_split_line = Some(line); + continue; + } + + let prev_line_trimmed_lowercase = prev_line.trim_start_matches(' ').to_lowercase(); + + if trimmed_line.to_lowercase() < prev_line_trimmed_lowercase { + tidy_error!(bad, "{file}:{}: line not in alphabetical order", line_idx + 1,); + } + + prev_line = line; + } +} + +pub fn check(path: &Path, bad: &mut bool) { + walk(path, &mut filter_dirs, &mut |entry, contents| { + let file = &entry.path().display(); + + let mut lines = contents.lines().enumerate().peekable(); + while let Some((_, line)) = lines.next() { + if line.contains(START_COMMENT) { + check_section(file, &mut lines, bad); + if lines.peek().is_none() { + tidy_error!( + bad, + "{file}: reached end of file expecting `// tidy-alphabetical-end`" + ) + } + } + } + }); +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index e82cca402e2..fc0bce58572 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -38,6 +38,7 @@ macro_rules! tidy_error { }); } +pub mod alphabetical; pub mod bins; pub mod debug_artifacts; pub mod deps; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index c1ce94f4705..8fe361c45a2 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -90,6 +90,8 @@ fn main() { check!(edition, &compiler_path); check!(edition, &library_path); + check!(alphabetical, &compiler_path); + let collected = { while handles.len() >= concurrency.get() { handles.pop_front().unwrap().join().unwrap(); diff --git a/triagebot.toml b/triagebot.toml index d358e59c245..181fb1de930 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -189,6 +189,11 @@ trigger_files = [ "src/tools/bump-stage0", ] +[autolabel."T-style"] +trigger_files = [ + "src/doc/style-guide", +] + [autolabel."A-translation"] trigger_files = [ "compiler/rustc_error_messages", |
