diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6c4b2b3724e34..dc0f0e7cd3c4c 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_error_messages/locales/en-US/middle.ftl b/compiler/rustc_error_messages/locales/en-US/middle.ftl index ca3c91ce24a1d..b9e4499d47f36 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 995ad4fe25859..1f1c9c29d665a 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_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index bbe6435be59f4..9b41234dcfb66 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/lib.rs b/compiler/rustc_errors/src/lib.rs index 2f6686f81965b..c8ccdc539af5a 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. diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 77ab27266b286..f6f25603581b2 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 0bd45bb1c9153..0a8a1bec9b8a3 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_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a175b1eb46733..06eb10c9137a1 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/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 26b6be7a1b891..b0a2412ab153f 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 5f8729a8ddf3c..6045c1acdd032 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/query.rs b/compiler/rustc_middle/src/ty/query.rs index b6cda34c51f61..ce1b69935f27a 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 bb89959b29ded..f4b4c3fb05a7c 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_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5455d063c13a9..73fb89bbc385e 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 e502b9b54e302..aa726d6cd92aa 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 08f704da62c2f..6a97ad3fe86e2 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 e08683fe23b20..253b0a88e48aa 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 e428d9293db17..3f991cf657241 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 eec37d19a8831..38a259ca8846f 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 cc231af71a274..1cc81a9ab9884 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)] @@ -526,6 +536,15 @@ pub struct DebugVisualizerInvalid { pub span: Span, } +#[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 { @@ -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 79900a90aed63..71b0735192ac4 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 46c4a702fde95..c1085094962a7 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 6e621b7eb5eb0..15f60f626c89a 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 04173c792a979..b5843c0ae488b 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 cdda0e388ddf0..077194ec687bf 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 2690be66c21e5..acc54e7e11006 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 34fa80228df89..cfd6acd8d7cd0 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 c48b4ecf87a3a..92024989a75e2 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_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 83aae28640299..c4644d4f076e2 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/parse.rs b/compiler/rustc_session/src/parse.rs index 2c3d8d5283b57..a199947ebed05 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 5926cdc9dad9a..beb22ab3eb951 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_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 30efbf6617598..cd9d229640571 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. diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index d6aeee299e30d..ff3b7bc2c9047 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 46b0ca2303406..d921ff9ba1026 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 be203fb5c04ff..a1edb1a518632 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/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs index b1a84095f13fa..4800eeda022bb 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/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 415774d7255d7..8b144f1463579 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 819af6587484d..7bdd226cb692e 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 744c7f709a6db..bf08960f338a3 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 0000000000000..056aec8cdd4f6 --- /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 0000000000000..adb73a7eef6e0 --- /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 0000000000000..004692fa6a22b --- /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 0000000000000..ab4b92b0a2478 --- /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 0000000000000..f4993ba06a888 --- /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 0000000000000..c7d0446dded19 --- /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 0000000000000..2835975355fca --- /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 0000000000000..b02b3c0471f28 --- /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 0000000000000..29b48bb1ee0b7 --- /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 0000000000000..25861ddabb8d0 --- /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 661aed71298d9..f5b0d15d733aa 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); } @@ -685,9 +685,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; } diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 0975d497cb23c..fc7713b98857b 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 0180c0ead8d39..dc5b8acdf53a8 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/test/rustdoc-gui/sidebar-links-color.goml b/src/test/rustdoc-gui/sidebar-links-color.goml index 3f719c4c4dc34..18a1a3fadea55 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 01282f2ffeb7a..b5c1b6a4390b6 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 2319de5568366..4b1e04234c870 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 e6ab67d59ce58..0000000000000 --- 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 b3a7ee563130e..ac79582fb3f0d 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 165ff67837244..0000000000000 --- 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/ui/associated-types/issue-85103.rs b/src/test/ui/associated-types/issue-85103.rs index c5e13856178de..9c6a419e9f72d 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 bddd1dce8e647..17f7148074cf3 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 0000000000000..b77342c4135e1 --- /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 0000000000000..eb4b329bd8134 --- /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 7287639c61dde..54f7e9ac592eb 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 bc85cd7b18d55..d12be539f7c17 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/variance/variance-regions-unused-indirect.rs b/src/test/ui/variance/variance-regions-unused-indirect.rs index 1514e39563e1f..6c2c24ddbc728 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 93710cc133aa8..14fdd849294b1 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/triagebot.toml b/triagebot.toml index d358e59c24525..181fb1de93055 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",