From 837b550238aa671a591ccf282dddeab29cadb206 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 04:49:42 +0200 Subject: Merging upstream version 1.71.1+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_lint/Cargo.toml | 1 + compiler/rustc_lint/messages.ftl | 727 +++++++++++---------- compiler/rustc_lint/src/builtin.rs | 105 ++- compiler/rustc_lint/src/context.rs | 104 ++- compiler/rustc_lint/src/drop_forget_useless.rs | 164 +++++ compiler/rustc_lint/src/early.rs | 2 +- .../rustc_lint/src/enum_intrinsics_non_enums.rs | 2 +- compiler/rustc_lint/src/errors.rs | 2 +- compiler/rustc_lint/src/expect.rs | 2 +- compiler/rustc_lint/src/internal.rs | 87 ++- compiler/rustc_lint/src/late.rs | 10 +- compiler/rustc_lint/src/levels.rs | 14 +- compiler/rustc_lint/src/lib.rs | 8 +- compiler/rustc_lint/src/lints.rs | 53 ++ compiler/rustc_lint/src/non_fmt_panic.rs | 2 +- compiler/rustc_lint/src/nonstandard_style.rs | 8 +- compiler/rustc_lint/src/noop_method_call.rs | 117 +++- .../rustc_lint/src/opaque_hidden_inferred_bound.rs | 4 +- compiler/rustc_lint/src/types.rs | 6 +- compiler/rustc_lint/src/unused.rs | 95 +-- 20 files changed, 968 insertions(+), 545 deletions(-) create mode 100644 compiler/rustc_lint/src/drop_forget_useless.rs (limited to 'compiler/rustc_lint') diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index abe61406c..539eea3d8 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -11,6 +11,7 @@ rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr = { path = "../rustc_attr" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } +rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_target = { path = "../rustc_target" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index db15b176d..0fa67cdb3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -5,508 +5,531 @@ lint_array_into_iter = .use_explicit_into_iter_suggestion = or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value -lint_enum_intrinsics_mem_discriminant = - the return value of `mem::discriminant` is unspecified when called with a non-enum type - .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum. +lint_atomic_ordering_fence = memory fences cannot have `Relaxed` ordering + .help = consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` -lint_enum_intrinsics_mem_variant = - the return value of `mem::variant_count` is unspecified when called with a non-enum type - .note = the type parameter of `variant_count` should be an enum, but it was instantiated with the type `{$ty_param}`, which is not an enum. +lint_atomic_ordering_invalid = `{$method}`'s failure ordering may not be `Release` or `AcqRel`, since a failed `{$method}` does not result in a write + .label = invalid failure ordering + .help = consider using `Acquire` or `Relaxed` failure ordering instead -lint_expectation = this lint expectation is unfulfilled - .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message - .rationale = {$rationale} +lint_atomic_ordering_load = atomic loads cannot have `Release` or `AcqRel` ordering + .help = consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` -lint_for_loops_over_fallibles = - for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement - .suggestion = consider using `if let` to clear intent - .remove_next = to iterate over `{$recv_snip}` remove the call to `next` - .use_while_let = to check pattern in a loop use `while let` - .use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents +lint_atomic_ordering_store = atomic stores cannot have `Acquire` or `AcqRel` ordering + .help = consider using ordering modes `Release`, `SeqCst` or `Relaxed` -lint_map_unit_fn = `Iterator::map` call that discard the iterator's values - .note = `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated - .function_label = this function returns `()`, which is likely not what you wanted - .argument_label = called `Iterator::map` with callable that returns `()` - .map_label = after this call to map, the resulting iterator is `impl Iterator`, which means the only information carried by the iterator is the number of items - .suggestion = you might have meant to use `Iterator::for_each` +lint_bad_attribute_argument = bad attribute argument -lint_non_binding_let_on_sync_lock = - non-binding let on a synchronization lock +lint_bad_opt_access = {$msg} -lint_non_binding_let_on_drop_type = - non-binding let on a type that implements `Drop` +lint_builtin_allow_internal_unsafe = + `allow_internal_unsafe` allows defining macros using unsafe without triggering the `unsafe_code` lint at their call site -lint_non_binding_let_suggestion = - consider binding to an unused variable to avoid immediately dropping the value +lint_builtin_anonymous_params = anonymous parameters are deprecated and will be removed in the next edition + .suggestion = try naming the parameter or explicitly ignoring it -lint_non_binding_let_multi_suggestion = - consider immediately dropping the value +lint_builtin_asm_labels = avoid using named labels in inline assembly -lint_deprecated_lint_name = - lint name `{$name}` is deprecated and may not have an effect in the future. - .suggestion = change it to +lint_builtin_box_pointers = type uses owned (Box type) pointers: {$ty} -lint_renamed_or_removed_lint = {$msg} - .suggestion = use the new name +lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature + .previous_decl_label = `{$orig}` previously declared here + .mismatch_label = this signature doesn't match the previous declaration -lint_unknown_lint = - unknown lint: `{$name}` - .suggestion = did you mean +lint_builtin_clashing_extern_same_name = `{$this}` redeclared with a different signature + .previous_decl_label = `{$orig}` previously declared here + .mismatch_label = this signature doesn't match the previous declaration +lint_builtin_const_no_mangle = const items should never be `#[no_mangle]` + .suggestion = try a static value -lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level +lint_builtin_decl_unsafe_fn = declaration of an `unsafe` function +lint_builtin_decl_unsafe_method = declaration of an `unsafe` method +lint_builtin_deprecated_attr_default_suggestion = remove this attribute -lint_unknown_gated_lint = - unknown lint: `{$name}` - .note = the `{$name}` lint is unstable +lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link} + .msg_suggestion = {$msg} + .default_suggestion = remove this attribute +lint_builtin_deprecated_attr_used = use of deprecated attribute `{$name}`: no longer used. +lint_builtin_deref_nullptr = dereferencing a null pointer + .label = this code causes undefined behavior when executed -lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label} - .label = this {$label} contains {$count -> - [one] an invisible - *[other] invisible - } unicode text flow control {$count -> - [one] codepoint - *[other] codepoints +lint_builtin_ellipsis_inclusive_range_patterns = `...` range patterns are deprecated + .suggestion = use `..=` for an inclusive range + +lint_builtin_explicit_outlives = outlives requirements can be inferred + .suggestion = remove {$count -> + [one] this bound + *[other] these bounds } - .note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen - .suggestion_remove = if their presence wasn't intentional, you can remove them - .suggestion_escape = if you want to keep them but make them visible in your source code, you can escape them - .no_suggestion_note_escape = if you want to keep them but make them visible in your source code, you can escape them: {$escaped} -lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance - .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary +lint_builtin_export_name_fn = declaration of a function with `export_name` +lint_builtin_export_name_method = declaration of a method with `export_name` -lint_query_instability = using `{$query}` can result in unstable query results - .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale +lint_builtin_export_name_static = declaration of a static with `export_name` +lint_builtin_impl_unsafe_method = implementation of an `unsafe` method -lint_tykind_kind = usage of `ty::TyKind::` - .suggestion = try using `ty::` directly +lint_builtin_incomplete_features = the feature `{$name}` is incomplete and may not be safe to use and/or cause compiler crashes + .note = see issue #{$n} for more information + .help = consider using `min_{$name}` instead, which is more stable and complete -lint_tykind = usage of `ty::TyKind` - .help = try using `Ty` instead +lint_builtin_keyword_idents = `{$kw}` is a keyword in the {$next} edition + .suggestion = you can use a raw identifier to stay compatible -lint_ty_qualified = usage of qualified `ty::{$ty}` - .suggestion = try importing it and using it unqualified +lint_builtin_link_section_fn = declaration of a function with `link_section` -lint_lintpass_by_hand = implementing `LintPass` by hand - .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead +lint_builtin_link_section_static = declaration of a static with `link_section` -lint_non_existent_doc_keyword = found non-existing keyword `{$keyword}` used in `#[doc(keyword = "...")]` - .help = only existing keywords are allowed in core/std +lint_builtin_missing_copy_impl = type could implement `Copy`; consider adding `impl Copy` -lint_diag_out_of_impl = - diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls +lint_builtin_missing_debug_impl = + type does not implement `{$debug}`; consider adding `#[derive(Debug)]` or a manual implementation -lint_untranslatable_diag = diagnostics should be created using translatable messages +lint_builtin_missing_doc = missing documentation for {$article} {$desc} -lint_bad_opt_access = {$msg} +lint_builtin_mutable_transmutes = + transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell -lint_cstring_ptr = getting the inner pointer of a temporary `CString` - .as_ptr_label = this pointer will be invalid - .unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime - .note = pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - .help = for more information, see https://doc.rust-lang.org/reference/destructors.html +lint_builtin_no_mangle_fn = declaration of a `no_mangle` function +lint_builtin_no_mangle_generic = functions generic over types or consts must be mangled + .suggestion = remove this attribute -lint_multiple_supertrait_upcastable = `{$ident}` is object-safe and has multiple supertraits +lint_builtin_no_mangle_method = declaration of a `no_mangle` method +lint_builtin_no_mangle_static = declaration of a `no_mangle` static +lint_builtin_non_shorthand_field_patterns = the `{$ident}:` in this pattern is redundant + .suggestion = use shorthand field pattern -lint_identifier_non_ascii_char = identifier contains non-ASCII characters +lint_builtin_overridden_symbol_name = + the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them -lint_identifier_uncommon_codepoints = identifier contains uncommon Unicode codepoints +lint_builtin_overridden_symbol_section = + the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them -lint_confusable_identifier_pair = identifier pair considered confusable between `{$existing_sym}` and `{$sym}` - .label = this is where the previous identifier occurred +lint_builtin_special_module_name_used_lib = found module declaration for lib.rs + .note = lib.rs is the root of this crate's library target + .help = to refer to it from other targets, use the library's name as the path -lint_mixed_script_confusables = - the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables - .includes_note = the usage includes {$includes} - .note = please recheck to make sure their usages are indeed what you want +lint_builtin_special_module_name_used_main = found module declaration for main.rs + .note = a binary crate cannot be used as library -lint_non_fmt_panic = panic message is not a string literal - .note = this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021 - .more_info_note = for more information, see - .supports_fmt_note = the `{$name}!()` macro supports formatting, so there's no need for the `format!()` macro here - .supports_fmt_suggestion = remove the `format!(..)` macro call - .display_suggestion = add a "{"{"}{"}"}" format string to `Display` the message - .debug_suggestion = - add a "{"{"}:?{"}"}" format string to use the `Debug` implementation of `{$ty}` - .panic_suggestion = {$already_suggested -> - [true] or use - *[false] use - } std::panic::panic_any instead +lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters -lint_non_fmt_panic_unused = - panic message contains {$count -> - [one] an unused - *[other] unused - } formatting {$count -> - [one] placeholder - *[other] placeholders - } - .note = this message is not used as a format string when given without arguments, but will be in Rust 2021 - .add_args_suggestion = add the missing {$count -> - [one] argument - *[other] arguments - } - .add_fmt_suggestion = or add a "{"{"}{"}"}" format string to use the message literally +lint_builtin_type_alias_bounds_help = use fully disambiguated paths (i.e., `::Assoc`) to refer to associated types in type aliases -lint_non_fmt_panic_braces = - panic message contains {$count -> - [one] a brace - *[other] braces - } - .note = this message is not used as a format string, but will be in Rust 2021 - .suggestion = add a "{"{"}{"}"}" format string to use the message literally +lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not enforced in type aliases + .suggestion = the bound will not be checked when the type alias is used, and should be removed -lint_non_camel_case_type = {$sort} `{$name}` should have an upper camel case name - .suggestion = convert the identifier to upper camel case - .label = should have an UpperCamelCase name +lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases + .suggestion = the clause will not be checked when the type alias is used, and should be removed -lint_non_snake_case = {$sort} `{$name}` should have a snake case name - .rename_or_convert_suggestion = rename the identifier or convert it to a snake case raw identifier - .cannot_convert_note = `{$sc}` cannot be used as a raw identifier - .rename_suggestion = rename the identifier - .convert_suggestion = convert the identifier to snake case - .help = convert the identifier to snake case: `{$sc}` - .label = should have a snake_case name +lint_builtin_unexpected_cli_config_name = unexpected `{$name}` as condition name + .help = was set with `--cfg` but isn't in the `--check-cfg` expected names -lint_non_upper_case_global = {$sort} `{$name}` should have an upper case name - .suggestion = convert the identifier to upper case - .label = should have an UPPER_CASE name +lint_builtin_unexpected_cli_config_value = unexpected condition value `{$value}` for condition name `{$name}` + .help = was set with `--cfg` but isn't in the `--check-cfg` expected values -lint_noop_method_call = call to `.{$method}()` on a reference in this situation does nothing - .label = unnecessary method call - .note = the type `{$receiver_ty}` which `{$method}` is being called on is the same as the type returned from `{$method}`, so the method call does not do anything and can be removed +lint_builtin_unnameable_test_items = cannot test inner items -lint_pass_by_value = passing `{$ty}` by reference - .suggestion = try passing by value +lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed +lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done -lint_redundant_semicolons = - unnecessary trailing {$multiple -> - [true] semicolons - *[false] semicolon - } - .suggestion = remove {$multiple -> - [true] these semicolons - *[false] this semicolon - } +lint_builtin_unpermitted_type_init_uninit = the type `{$ty}` does not permit being left uninitialized -lint_drop_trait_constraints = - bounds on `{$predicate}` are most likely incorrect, consider instead using `{$needs_drop}` to detect whether a type can be trivially dropped +lint_builtin_unpermitted_type_init_zeroed = the type `{$ty}` does not permit zero-initialization +lint_builtin_unreachable_pub = unreachable `pub` {$what} + .suggestion = consider restricting its visibility + .help = or consider exporting it for use by other crates -lint_drop_glue = - types that do not implement `Drop` can still have drop glue, consider instead using `{$needs_drop}` to detect whether a type is trivially dropped +lint_builtin_unsafe_block = usage of an `unsafe` block -lint_range_endpoint_out_of_range = range endpoint is out of range for `{$ty}` +lint_builtin_unsafe_impl = implementation of an `unsafe` trait -lint_range_use_inclusive_range = use an inclusive range instead +lint_builtin_unsafe_trait = declaration of an `unsafe` trait +lint_builtin_unstable_features = unstable feature -lint_overflowing_bin_hex = literal out of range for `{$ty}` - .negative_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` - .negative_becomes_note = and the value `-{$lit}` will become `{$actually}{$ty}` - .positive_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}` - .suggestion = consider using the type `{$suggestion_ty}` instead - .help = consider using the type `{$suggestion_ty}` instead +lint_builtin_unused_doc_comment = unused doc comment + .label = rustdoc does not generate documentation for {$kind} + .plain_help = use `//` for a plain comment + .block_help = use `/* */` for a plain comment -lint_overflowing_int = literal out of range for `{$ty}` - .note = the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}` - .help = consider using the type `{$suggestion_ty}` instead +lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` + .suggestion = use `loop` -lint_only_cast_u8_to_char = only `u8` can be cast into `char` - .suggestion = use a `char` literal instead +lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name} -lint_overflowing_uint = literal out of range for `{$ty}` - .note = the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}` +lint_check_name_unknown = unknown lint: `{$lint_name}` + .help = did you mean: `{$suggestion}` -lint_overflowing_literal = literal out of range for `{$ty}` - .note = the literal `{$lit}` does not fit into the type `{$ty}` and will be converted to `{$ty}::INFINITY` +lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` -lint_unused_comparisons = comparison is useless due to type limits +lint_check_name_warning = {$msg} -lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe - .label = not FFI-safe - .note = the type is defined here +lint_command_line_source = `forbid` lint level was set on command line -lint_improper_ctypes_opaque = opaque types have no C equivalent +lint_confusable_identifier_pair = identifier pair considered confusable between `{$existing_sym}` and `{$sym}` + .label = this is where the previous identifier occurred -lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention -lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead +lint_cstring_ptr = getting the inner pointer of a temporary `CString` + .as_ptr_label = this pointer will be invalid + .unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + .note = pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + .help = for more information, see https://doc.rust-lang.org/reference/destructors.html -lint_improper_ctypes_tuple_reason = tuples have unspecified layout -lint_improper_ctypes_tuple_help = consider using a struct instead +lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance + .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary -lint_improper_ctypes_str_reason = string slices have no C equivalent -lint_improper_ctypes_str_help = consider using `*const u8` and a length instead +lint_default_source = `forbid` lint level is the default for {$id} -lint_improper_ctypes_dyn = trait objects have no C equivalent +lint_deprecated_lint_name = + lint name `{$name}` is deprecated and may not have an effect in the future. + .suggestion = change it to -lint_improper_ctypes_slice_reason = slices have no C equivalent -lint_improper_ctypes_slice_help = consider using a raw pointer instead +lint_diag_out_of_impl = + diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls -lint_improper_ctypes_128bit = 128-bit integers don't currently have a known stable ABI +lint_drop_glue = + types that do not implement `Drop` can still have drop glue, consider instead using `{$needs_drop}` to detect whether a type is trivially dropped -lint_improper_ctypes_char_reason = the `char` type has no C equivalent -lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead +lint_drop_trait_constraints = + bounds on `{$predicate}` are most likely incorrect, consider instead using `{$needs_drop}` to detect whether a type can be trivially dropped -lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive -lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants +lint_dropping_copy_types = calls to `std::mem::drop` with a value that implements `Copy` does nothing + .label = argument has type `{$arg_ty}` + .note = use `let _ = ...` to ignore the expression or result -lint_improper_ctypes_enum_repr_reason = enum has no representation hint -lint_improper_ctypes_enum_repr_help = - consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum +lint_dropping_references = calls to `std::mem::drop` with a reference instead of an owned value does nothing + .label = argument has type `{$arg_ty}` + .note = use `let _ = ...` to ignore the expression or result -lint_improper_ctypes_struct_fieldless_reason = this struct has no fields -lint_improper_ctypes_struct_fieldless_help = consider adding a member to this struct +lint_enum_intrinsics_mem_discriminant = + the return value of `mem::discriminant` is unspecified when called with a non-enum type + .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum. -lint_improper_ctypes_union_fieldless_reason = this union has no fields -lint_improper_ctypes_union_fieldless_help = consider adding a member to this union +lint_enum_intrinsics_mem_variant = + the return value of `mem::variant_count` is unspecified when called with a non-enum type + .note = the type parameter of `variant_count` should be an enum, but it was instantiated with the type `{$ty_param}`, which is not an enum. -lint_improper_ctypes_struct_non_exhaustive = this struct is non-exhaustive -lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive +lint_expectation = this lint expectation is unfulfilled + .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message + .rationale = {$rationale} -lint_improper_ctypes_struct_layout_reason = this struct has unspecified layout -lint_improper_ctypes_struct_layout_help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct +lint_for_loops_over_fallibles = + for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement + .suggestion = consider using `if let` to clear intent + .remove_next = to iterate over `{$recv_snip}` remove the call to `next` + .use_while_let = to check pattern in a loop use `while let` + .use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents -lint_improper_ctypes_union_layout_reason = this union has unspecified layout -lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union +lint_forgetting_copy_types = calls to `std::mem::forget` with a value that implements `Copy` does nothing + .label = argument has type `{$arg_ty}` + .note = use `let _ = ...` to ignore the expression or result +lint_forgetting_references = calls to `std::mem::forget` with a reference instead of an owned value does nothing + .label = argument has type `{$arg_ty}` + .note = use `let _ = ...` to ignore the expression or result -lint_improper_ctypes_box = box cannot be represented as a single pointer +lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label} + .label = this {$label} contains {$count -> + [one] an invisible + *[other] invisible + } unicode text flow control {$count -> + [one] codepoint + *[other] codepoints + } + .note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen + .suggestion_remove = if their presence wasn't intentional, you can remove them + .suggestion_escape = if you want to keep them but make them visible in your source code, you can escape them + .no_suggestion_note_escape = if you want to keep them but make them visible in your source code, you can escape them: {$escaped} -lint_improper_ctypes_enum_phantomdata = this enum contains a PhantomData field +lint_identifier_non_ascii_char = identifier contains non-ASCII characters -lint_improper_ctypes_struct_zst = this struct contains only zero-sized fields +lint_identifier_uncommon_codepoints = identifier contains uncommon Unicode codepoints -lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe -lint_improper_ctypes_array_help = consider passing a pointer to the array +lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level -lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` +lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe + .label = not FFI-safe + .note = the type is defined here -lint_variant_size_differences = - enum variant is more than three times larger ({$largest} bytes) than the next largest +lint_improper_ctypes_128bit = 128-bit integers don't currently have a known stable ABI -lint_atomic_ordering_load = atomic loads cannot have `Release` or `AcqRel` ordering - .help = consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` +lint_improper_ctypes_array_help = consider passing a pointer to the array -lint_atomic_ordering_store = atomic stores cannot have `Acquire` or `AcqRel` ordering - .help = consider using ordering modes `Release`, `SeqCst` or `Relaxed` +lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe +lint_improper_ctypes_box = box cannot be represented as a single pointer -lint_atomic_ordering_fence = memory fences cannot have `Relaxed` ordering - .help = consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` +lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead -lint_atomic_ordering_invalid = `{$method}`'s failure ordering may not be `Release` or `AcqRel`, since a failed `{$method}` does not result in a write - .label = invalid failure ordering - .help = consider using `Acquire` or `Relaxed` failure ordering instead +lint_improper_ctypes_char_reason = the `char` type has no C equivalent +lint_improper_ctypes_dyn = trait objects have no C equivalent -lint_unused_op = unused {$op} that must be used - .label = the {$op} produces a value - .suggestion = use `let _ = ...` to ignore the resulting value +lint_improper_ctypes_enum_phantomdata = this enum contains a PhantomData field -lint_unused_result = unused result of type `{$ty}` +lint_improper_ctypes_enum_repr_help = + consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum -lint_unused_closure = - unused {$pre}{$count -> - [one] closure - *[other] closures - }{$post} that must be used - .note = closures are lazy and do nothing unless called +lint_improper_ctypes_enum_repr_reason = enum has no representation hint +lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead -lint_unused_generator = - unused {$pre}{$count -> - [one] generator - *[other] generator - }{$post} that must be used - .note = generators are lazy and do nothing unless resumed +lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention +lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive +lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants -lint_unused_def = unused {$pre}`{$def}`{$post} that must be used - .suggestion = use `let _ = ...` to ignore the resulting value +lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` -lint_path_statement_drop = path statement drops value - .suggestion = use `drop` to clarify the intent +lint_improper_ctypes_opaque = opaque types have no C equivalent -lint_path_statement_no_effect = path statement with no effect +lint_improper_ctypes_slice_help = consider using a raw pointer instead -lint_unused_delim = unnecessary {$delim} around {$item} - .suggestion = remove these {$delim} +lint_improper_ctypes_slice_reason = slices have no C equivalent +lint_improper_ctypes_str_help = consider using `*const u8` and a length instead -lint_unused_import_braces = braces around {$node} is unnecessary +lint_improper_ctypes_str_reason = string slices have no C equivalent +lint_improper_ctypes_struct_fieldless_help = consider adding a member to this struct -lint_unused_allocation = unnecessary allocation, use `&` instead -lint_unused_allocation_mut = unnecessary allocation, use `&mut` instead +lint_improper_ctypes_struct_fieldless_reason = this struct has no fields +lint_improper_ctypes_struct_layout_help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct -lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` - .suggestion = use `loop` +lint_improper_ctypes_struct_layout_reason = this struct has unspecified layout +lint_improper_ctypes_struct_non_exhaustive = this struct is non-exhaustive +lint_improper_ctypes_struct_zst = this struct contains only zero-sized fields -lint_builtin_box_pointers = type uses owned (Box type) pointers: {$ty} +lint_improper_ctypes_tuple_help = consider using a struct instead -lint_builtin_non_shorthand_field_patterns = the `{$ident}:` in this pattern is redundant - .suggestion = use shorthand field pattern +lint_improper_ctypes_tuple_reason = tuples have unspecified layout +lint_improper_ctypes_union_fieldless_help = consider adding a member to this union -lint_builtin_overridden_symbol_name = - the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them +lint_improper_ctypes_union_fieldless_reason = this union has no fields +lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union -lint_builtin_overridden_symbol_section = - the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them +lint_improper_ctypes_union_layout_reason = this union has unspecified layout +lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive -lint_builtin_allow_internal_unsafe = - `allow_internal_unsafe` allows defining macros using unsafe without triggering the `unsafe_code` lint at their call site +lint_lintpass_by_hand = implementing `LintPass` by hand + .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead -lint_builtin_unsafe_block = usage of an `unsafe` block +lint_malformed_attribute = malformed lint attribute input -lint_builtin_unsafe_trait = declaration of an `unsafe` trait +lint_map_unit_fn = `Iterator::map` call that discard the iterator's values + .note = `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated + .function_label = this function returns `()`, which is likely not what you wanted + .argument_label = called `Iterator::map` with callable that returns `()` + .map_label = after this call to map, the resulting iterator is `impl Iterator`, which means the only information carried by the iterator is the number of items + .suggestion = you might have meant to use `Iterator::for_each` -lint_builtin_unsafe_impl = implementation of an `unsafe` trait +lint_mixed_script_confusables = + the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables + .includes_note = the usage includes {$includes} + .note = please recheck to make sure their usages are indeed what you want -lint_builtin_no_mangle_fn = declaration of a `no_mangle` function -lint_builtin_export_name_fn = declaration of a function with `export_name` -lint_builtin_link_section_fn = declaration of a function with `link_section` +lint_multiple_supertrait_upcastable = `{$ident}` is object-safe and has multiple supertraits -lint_builtin_no_mangle_static = declaration of a `no_mangle` static -lint_builtin_export_name_static = declaration of a static with `export_name` -lint_builtin_link_section_static = declaration of a static with `link_section` +lint_node_source = `forbid` level set here + .note = {$reason} -lint_builtin_no_mangle_method = declaration of a `no_mangle` method -lint_builtin_export_name_method = declaration of a method with `export_name` +lint_non_binding_let_multi_suggestion = + consider immediately dropping the value -lint_builtin_decl_unsafe_fn = declaration of an `unsafe` function -lint_builtin_decl_unsafe_method = declaration of an `unsafe` method -lint_builtin_impl_unsafe_method = implementation of an `unsafe` method +lint_non_binding_let_on_drop_type = + non-binding let on a type that implements `Drop` -lint_builtin_missing_doc = missing documentation for {$article} {$desc} +lint_non_binding_let_on_sync_lock = + non-binding let on a synchronization lock -lint_builtin_missing_copy_impl = type could implement `Copy`; consider adding `impl Copy` +lint_non_binding_let_suggestion = + consider binding to an unused variable to avoid immediately dropping the value -lint_builtin_missing_debug_impl = - type does not implement `{$debug}`; consider adding `#[derive(Debug)]` or a manual implementation +lint_non_camel_case_type = {$sort} `{$name}` should have an upper camel case name + .suggestion = convert the identifier to upper camel case + .label = should have an UpperCamelCase name -lint_builtin_anonymous_params = anonymous parameters are deprecated and will be removed in the next edition - .suggestion = try naming the parameter or explicitly ignoring it +lint_non_existent_doc_keyword = found non-existing keyword `{$keyword}` used in `#[doc(keyword = "...")]` + .help = only existing keywords are allowed in core/std -lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link} - .msg_suggestion = {$msg} - .default_suggestion = remove this attribute -lint_builtin_deprecated_attr_used = use of deprecated attribute `{$name}`: no longer used. -lint_builtin_deprecated_attr_default_suggestion = remove this attribute +lint_non_fmt_panic = panic message is not a string literal + .note = this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021 + .more_info_note = for more information, see + .supports_fmt_note = the `{$name}!()` macro supports formatting, so there's no need for the `format!()` macro here + .supports_fmt_suggestion = remove the `format!(..)` macro call + .display_suggestion = add a "{"{"}{"}"}" format string to `Display` the message + .debug_suggestion = + add a "{"{"}:?{"}"}" format string to use the `Debug` implementation of `{$ty}` + .panic_suggestion = {$already_suggested -> + [true] or use + *[false] use + } std::panic::panic_any instead -lint_builtin_unused_doc_comment = unused doc comment - .label = rustdoc does not generate documentation for {$kind} - .plain_help = use `//` for a plain comment - .block_help = use `/* */` for a plain comment +lint_non_fmt_panic_braces = + panic message contains {$count -> + [one] a brace + *[other] braces + } + .note = this message is not used as a format string, but will be in Rust 2021 + .suggestion = add a "{"{"}{"}"}" format string to use the message literally -lint_builtin_no_mangle_generic = functions generic over types or consts must be mangled - .suggestion = remove this attribute +lint_non_fmt_panic_unused = + panic message contains {$count -> + [one] an unused + *[other] unused + } formatting {$count -> + [one] placeholder + *[other] placeholders + } + .note = this message is not used as a format string when given without arguments, but will be in Rust 2021 + .add_args_suggestion = add the missing {$count -> + [one] argument + *[other] arguments + } + .add_fmt_suggestion = or add a "{"{"}{"}"}" format string to use the message literally -lint_builtin_const_no_mangle = const items should never be `#[no_mangle]` - .suggestion = try a static value +lint_non_snake_case = {$sort} `{$name}` should have a snake case name + .rename_or_convert_suggestion = rename the identifier or convert it to a snake case raw identifier + .cannot_convert_note = `{$sc}` cannot be used as a raw identifier + .rename_suggestion = rename the identifier + .convert_suggestion = convert the identifier to snake case + .help = convert the identifier to snake case: `{$sc}` + .label = should have a snake_case name -lint_builtin_mutable_transmutes = - transmuting &T to &mut T is undefined behavior, even if the reference is unused, consider instead using an UnsafeCell +lint_non_upper_case_global = {$sort} `{$name}` should have an upper case name + .suggestion = convert the identifier to upper case + .label = should have an UPPER_CASE name -lint_builtin_unstable_features = unstable feature +lint_noop_method_call = call to `.{$method}()` on a reference in this situation does nothing + .label = unnecessary method call + .note = the type `{$receiver_ty}` which `{$method}` is being called on is the same as the type returned from `{$method}`, so the method call does not do anything and can be removed -lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op - .label = this function will not propagate the caller location +lint_only_cast_u8_to_char = only `u8` can be cast into `char` + .suggestion = use a `char` literal instead -lint_builtin_unreachable_pub = unreachable `pub` {$what} - .suggestion = consider restricting its visibility - .help = or consider exporting it for use by other crates +lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its associated type bounds + .specifically = this associated type bound is unsatisfied for `{$proj_ty}` -lint_builtin_unexpected_cli_config_name = unexpected `{$name}` as condition name - .help = was set with `--cfg` but isn't in the `--check-cfg` expected names +lint_opaque_hidden_inferred_bound_sugg = add this bound -lint_builtin_unexpected_cli_config_value = unexpected condition value `{$value}` for condition name `{$name}` - .help = was set with `--cfg` but isn't in the `--check-cfg` expected values +lint_overflowing_bin_hex = literal out of range for `{$ty}` + .negative_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` + .negative_becomes_note = and the value `-{$lit}` will become `{$actually}{$ty}` + .positive_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}` + .suggestion = consider using the type `{$suggestion_ty}` instead + .help = consider using the type `{$suggestion_ty}` instead -lint_builtin_type_alias_bounds_help = use fully disambiguated paths (i.e., `::Assoc`) to refer to associated types in type aliases +lint_overflowing_int = literal out of range for `{$ty}` + .note = the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}` + .help = consider using the type `{$suggestion_ty}` instead -lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases - .suggestion = the clause will not be checked when the type alias is used, and should be removed +lint_overflowing_literal = literal out of range for `{$ty}` + .note = the literal `{$lit}` does not fit into the type `{$ty}` and will be converted to `{$ty}::INFINITY` -lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not enforced in type aliases - .suggestion = the bound will not be checked when the type alias is used, and should be removed +lint_overflowing_uint = literal out of range for `{$ty}` + .note = the literal `{$lit}` does not fit into the type `{$ty}` whose range is `{$min}..={$max}` -lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters +lint_overruled_attribute = {$lint_level}({$lint_source}) incompatible with previous forbid + .label = overruled by previous forbid -lint_builtin_ellipsis_inclusive_range_patterns = `...` range patterns are deprecated - .suggestion = use `..=` for an inclusive range +lint_pass_by_value = passing `{$ty}` by reference + .suggestion = try passing by value -lint_builtin_unnameable_test_items = cannot test inner items +lint_path_statement_drop = path statement drops value + .suggestion = use `drop` to clarify the intent -lint_builtin_keyword_idents = `{$kw}` is a keyword in the {$next} edition - .suggestion = you can use a raw identifier to stay compatible +lint_path_statement_no_effect = path statement with no effect -lint_builtin_explicit_outlives = outlives requirements can be inferred - .suggestion = remove {$count -> - [one] this bound - *[other] these bounds - } +lint_query_instability = using `{$query}` can result in unstable query results + .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale -lint_builtin_incomplete_features = the feature `{$name}` is incomplete and may not be safe to use and/or cause compiler crashes - .note = see issue #{$n} for more information - .help = consider using `min_{$name}` instead, which is more stable and complete +lint_range_endpoint_out_of_range = range endpoint is out of range for `{$ty}` -lint_builtin_unpermitted_type_init_zeroed = the type `{$ty}` does not permit zero-initialization -lint_builtin_unpermitted_type_init_unint = the type `{$ty}` does not permit being left uninitialized +lint_range_use_inclusive_range = use an inclusive range instead -lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed -lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done -lint_builtin_clashing_extern_same_name = `{$this}` redeclared with a different signature - .previous_decl_label = `{$orig}` previously declared here - .mismatch_label = this signature doesn't match the previous declaration -lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature - .previous_decl_label = `{$orig}` previously declared here - .mismatch_label = this signature doesn't match the previous declaration +lint_reason_must_be_string_literal = reason must be a string literal -lint_builtin_deref_nullptr = dereferencing a null pointer - .label = this code causes undefined behavior when executed +lint_reason_must_come_last = reason in lint attribute must come last -lint_builtin_asm_labels = avoid using named labels in inline assembly +lint_redundant_semicolons = + unnecessary trailing {$multiple -> + [true] semicolons + *[false] semicolon + } + .suggestion = remove {$multiple -> + [true] these semicolons + *[false] this semicolon + } -lint_builtin_special_module_name_used_lib = found module declaration for lib.rs - .note = lib.rs is the root of this crate's library target - .help = to refer to it from other targets, use the library's name as the path +lint_renamed_or_removed_lint = {$msg} + .suggestion = use the new name -lint_builtin_special_module_name_used_main = found module declaration for main.rs - .note = a binary crate cannot be used as library +lint_requested_level = requested on the command line with `{$level} {$lint_name}` lint_supertrait_as_deref_target = `{$t}` implements `Deref` with supertrait `{$target_principal}` as target .label = target type is set here -lint_overruled_attribute = {$lint_level}({$lint_source}) incompatible with previous forbid - .label = overruled by previous forbid +lint_suspicious_double_ref_clone = + using `.clone()` on a double reference, which returns `{$ty}` instead of cloning the inner type -lint_default_source = `forbid` lint level is the default for {$id} +lint_suspicious_double_ref_deref = + using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type -lint_node_source = `forbid` level set here - .note = {$reason} +lint_trivial_untranslatable_diag = diagnostic with static strings only -lint_command_line_source = `forbid` lint level was set on command line +lint_ty_qualified = usage of qualified `ty::{$ty}` + .suggestion = try importing it and using it unqualified -lint_malformed_attribute = malformed lint attribute input +lint_tykind = usage of `ty::TyKind` + .help = try using `Ty` instead -lint_bad_attribute_argument = bad attribute argument +lint_tykind_kind = usage of `ty::TyKind::` + .suggestion = try using `ty::` directly -lint_reason_must_be_string_literal = reason must be a string literal +lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op + .label = this function will not propagate the caller location -lint_reason_must_come_last = reason in lint attribute must come last +lint_unknown_gated_lint = + unknown lint: `{$name}` + .note = the `{$name}` lint is unstable + +lint_unknown_lint = + unknown lint: `{$name}` + .suggestion = did you mean lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}` .help = add `#![register_tool({$tool_name})]` to the crate root lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´ -lint_requested_level = requested on the command line with `{$level} {$lint_name}` +lint_untranslatable_diag = diagnostics should be created using translatable messages -lint_check_name_unknown = unknown lint: `{$lint_name}` - .help = did you mean: `{$suggestion}` +lint_unused_allocation = unnecessary allocation, use `&` instead +lint_unused_allocation_mut = unnecessary allocation, use `&mut` instead -lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` +lint_unused_closure = + unused {$pre}{$count -> + [one] closure + *[other] closures + }{$post} that must be used + .note = closures are lazy and do nothing unless called -lint_check_name_warning = {$msg} +lint_unused_comparisons = comparison is useless due to type limits -lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name} +lint_unused_def = unused {$pre}`{$def}`{$post} that must be used + .suggestion = use `let _ = ...` to ignore the resulting value -lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its associated type bounds - .specifically = this associated type bound is unsatisfied for `{$proj_ty}` +lint_unused_delim = unnecessary {$delim} around {$item} + .suggestion = remove these {$delim} -lint_opaque_hidden_inferred_bound_sugg = add this bound +lint_unused_generator = + unused {$pre}{$count -> + [one] generator + *[other] generator + }{$post} that must be used + .note = generators are lazy and do nothing unless resumed + +lint_unused_import_braces = braces around {$node} is unnecessary + +lint_unused_op = unused {$op} that must be used + .label = the {$op} produces a value + .suggestion = use `let _ = ...` to ignore the resulting value + +lint_unused_result = unused result of type `{$ty}` + +lint_variant_size_differences = + enum variant is more than three times larger ({$largest} bytes) than the next largest diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 6b387df78..85141836e 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -22,7 +22,7 @@ use crate::fluent_generated as fluent; use crate::{ - errors::BuiltinEllpisisInclusiveRangePatterns, + errors::BuiltinEllipsisInclusiveRangePatterns, lints::{ BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinClashingExtern, BuiltinClashingExternSub, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink, @@ -62,7 +62,9 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::{LayoutError, LayoutOf}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef}; +use rustc_session::config::ExpectedValues; use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason}; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; @@ -116,8 +118,7 @@ impl EarlyLintPass for WhileTrue { #[inline] fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { if let ast::ExprKind::While(cond, _, label) = &e.kind - && let cond = pierce_parens(cond) - && let ast::ExprKind::Lit(token_lit) = cond.kind + && let ast::ExprKind::Lit(token_lit) = pierce_parens(cond).kind && let token::Lit { kind: token::Bool, symbol: kw::True, .. } = token_lit && !cond.span.from_expansion() { @@ -166,10 +167,8 @@ declare_lint_pass!(BoxPointers => [BOX_POINTERS]); impl BoxPointers { fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) { for leaf in ty.walk() { - if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { - if leaf_ty.is_box() { - cx.emit_spanned_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty }); - } + if let GenericArgKind::Type(leaf_ty) = leaf.unpack() && leaf_ty.is_box() { + cx.emit_spanned_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty }); } } } @@ -548,32 +547,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - match it.kind { - hir::ItemKind::Trait(..) => { - // Issue #11592: traits are always considered exported, even when private. - if cx.tcx.visibility(it.owner_id) - == ty::Visibility::Restricted( - cx.tcx.parent_module_from_def_id(it.owner_id.def_id).to_def_id(), - ) - { - return; - } - } - hir::ItemKind::TyAlias(..) - | hir::ItemKind::Fn(..) - | hir::ItemKind::Macro(..) - | hir::ItemKind::Mod(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::Const(..) - | hir::ItemKind::Static(..) => {} - - _ => return, - }; + // Previously the Impl and Use types have been excluded from missing docs, + // so we will continue to exclude them for compatibility. + // + // The documentation on `ExternCrate` is not used at the moment so no need to warn for it. + if let hir::ItemKind::Impl(..) | hir::ItemKind::Use(..) | hir::ItemKind::ExternCrate(_) = + it.kind + { + return; + } let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id()); - self.check_missing_docs_attrs(cx, it.owner_id.def_id, article, desc); } @@ -631,7 +615,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { declare_lint! { /// The `missing_copy_implementations` lint detects potentially-forgotten - /// implementations of [`Copy`]. + /// implementations of [`Copy`] for public types. /// /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html /// @@ -667,7 +651,9 @@ declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS]) impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) { + if !(cx.effective_visibilities.is_reachable(item.owner_id.def_id) + && cx.tcx.local_visibility(item.owner_id.def_id).is_public()) + { return; } let (def, ty) = match item.kind { @@ -747,7 +733,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { declare_lint! { /// The `missing_debug_implementations` lint detects missing - /// implementations of [`fmt::Debug`]. + /// implementations of [`fmt::Debug`] for public types. /// /// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html /// @@ -786,7 +772,9 @@ impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]); impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) { + if !(cx.effective_visibilities.is_reachable(item.owner_id.def_id) + && cx.tcx.local_visibility(item.owner_id.def_id).is_public()) + { return; } @@ -977,7 +965,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi()))); } - if attrs.peek().map_or(false, |next_attr| next_attr.is_doc_comment()) { + if attrs.peek().is_some_and(|next_attr| next_attr.is_doc_comment()) { continue; } @@ -1463,6 +1451,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { // Bounds are respected for `type X = impl Trait` return; } + if cx.tcx.type_of(item.owner_id).skip_binder().has_inherent_projections() { + // Bounds are respected for `type X = … Type::Inherent …` + return; + } // There must not be a where clause if type_alias_generics.predicates.is_empty() { return; @@ -1582,7 +1574,6 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::Clause; use rustc_middle::ty::PredicateKind::*; @@ -1711,13 +1702,13 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } } - let (parenthesise, endpoints) = match &pat.kind { + let (parentheses, endpoints) = match &pat.kind { PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(&subpat)), _ => (false, matches_ellipsis_pat(pat)), }; if let Some((start, end, join)) = endpoints { - if parenthesise { + if parentheses { self.node_id = Some(pat.id); let end = expr_to_string(&end); let replace = match start { @@ -1725,7 +1716,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { None => format!("&(..={})", end), }; if join.edition() >= Edition::Edition2021 { - cx.sess().emit_err(BuiltinEllpisisInclusiveRangePatterns { + cx.sess().emit_err(BuiltinEllipsisInclusiveRangePatterns { span: pat.span, suggestion: pat.span, replace, @@ -1743,7 +1734,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } else { let replace = "..="; if join.edition() >= Edition::Edition2021 { - cx.sess().emit_err(BuiltinEllpisisInclusiveRangePatterns { + cx.sess().emit_err(BuiltinEllipsisInclusiveRangePatterns { span: pat.span, suggestion: join, replace: replace.to_string(), @@ -1899,8 +1890,8 @@ declare_lint_pass!( struct UnderMacro(bool); impl KeywordIdents { - fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: TokenStream) { - for tt in tokens.into_trees() { + fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: &TokenStream) { + for tt in tokens.trees() { match tt { // Only report non-raw idents. TokenTree::Token(token, _) => { @@ -1961,10 +1952,10 @@ impl KeywordIdents { impl EarlyLintPass for KeywordIdents { fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef) { - self.check_tokens(cx, mac_def.body.tokens.clone()); + self.check_tokens(cx, &mac_def.body.tokens); } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { - self.check_tokens(cx, mac.args.tokens.clone()); + self.check_tokens(cx, &mac.args.tokens); } fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { self.check_ident_token(cx, UnderMacro(false), ident); @@ -2560,7 +2551,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { .subst(cx.tcx, substs) .apply_any_module(cx.tcx, cx.param_env) { - // Entirely skip uninhbaited variants. + // Entirely skip uninhabited variants. Some(false) => return None, // Forward the others, but remember which ones are definitely inhabited. Some(true) => true, @@ -2628,7 +2619,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { if let Some(err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) { let msg = match init { InitKind::Zeroed => fluent::lint_builtin_unpermitted_type_init_zeroed, - InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_unint, + InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_uninit, }; let sub = BuiltinUnpermittedTypeInitSub { err }; cx.emit_spanned_lint( @@ -2919,6 +2910,7 @@ impl ClashingExternDeclarations { | (Generator(..), Generator(..)) | (GeneratorWitness(..), GeneratorWitness(..)) | (Alias(ty::Projection, ..), Alias(ty::Projection, ..)) + | (Alias(ty::Inherent, ..), Alias(ty::Inherent, ..)) | (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false, // These definitely should have been caught above. @@ -3308,16 +3300,15 @@ impl EarlyLintPass for UnexpectedCfgs { let cfg = &cx.sess().parse_sess.config; let check_cfg = &cx.sess().parse_sess.check_config; for &(name, value) in cfg { - if let Some(names_valid) = &check_cfg.names_valid && !names_valid.contains(&name){ - cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName { - name, - }); - } - if let Some(value) = value && let Some(values) = check_cfg.values_valid.get(&name) && !values.contains(&value) { - cx.emit_lint( - UNEXPECTED_CFGS, - BuiltinUnexpectedCliConfigValue { name, value }, - ); + match check_cfg.expecteds.get(&name) { + Some(ExpectedValues::Some(values)) if !values.contains(&value) => { + let value = value.unwrap_or(kw::Empty); + cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigValue { name, value }); + } + None if check_cfg.exhaustive_names => { + cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName { name }); + } + _ => { /* expected */ } } } } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 626c09fea..1d0c43e95 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -36,6 +36,7 @@ use rustc_middle::middle::stability; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt}; +use rustc_session::config::ExpectedValues; use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId}; use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; use rustc_session::Session; @@ -48,9 +49,9 @@ use std::cell::Cell; use std::iter; use std::slice; -type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync; +type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync; type LateLintPassFactory = - dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::Send + sync::Sync; + dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync; /// Information about the registered lints. /// @@ -168,7 +169,7 @@ impl LintStore { pub fn register_early_pass( &mut self, - pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, + pass: impl Fn() -> EarlyLintPassObject + 'static + sync::DynSend + sync::DynSync, ) { self.early_passes.push(Box::new(pass)); } @@ -181,7 +182,7 @@ impl LintStore { /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518) pub fn register_pre_expansion_pass( &mut self, - pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync, + pass: impl Fn() -> EarlyLintPassObject + 'static + sync::DynSend + sync::DynSync, ) { self.pre_expansion_passes.push(Box::new(pass)); } @@ -190,8 +191,8 @@ impl LintStore { &mut self, pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + 'static - + sync::Send - + sync::Sync, + + sync::DynSend + + sync::DynSync, ) { self.late_passes.push(Box::new(pass)); } @@ -200,8 +201,8 @@ impl LintStore { &mut self, pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + 'static - + sync::Send - + sync::Sync, + + sync::DynSend + + sync::DynSync, ) { self.late_module_passes.push(Box::new(pass)); } @@ -616,7 +617,7 @@ pub trait LintContext: Sized { 1 => ("an ", ""), _ => ("", "s"), }; - db.span_label(span, &format!( + db.span_label(span, format!( "this comment contains {}invisible unicode text flow control codepoint{}", an, s, @@ -680,12 +681,12 @@ pub trait LintContext: Sized { ); } BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => { - db.span_suggestion(span, ¬e, sugg, Applicability::MaybeIncorrect); + db.span_suggestion(span, note, sugg, Applicability::MaybeIncorrect); } BuiltinLintDiagnostics::UnusedImports(message, replaces, in_test_module) => { if !replaces.is_empty() { db.tool_only_multipart_suggestion( - &message, + message, replaces, Applicability::MachineApplicable, ); @@ -720,13 +721,13 @@ pub trait LintContext: Sized { } BuiltinLintDiagnostics::MissingAbi(span, default_abi) => { db.span_label(span, "ABI should be specified here"); - db.help(&format!("the default ABI is {}", default_abi.name())); + db.help(format!("the default ABI is {}", default_abi.name())); } BuiltinLintDiagnostics::LegacyDeriveHelpers(span) => { db.span_label(span, "the attribute is introduced here"); } BuiltinLintDiagnostics::ProcMacroBackCompat(note) => { - db.note(¬e); + db.note(note); } BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => { db.span_suggestion(span, "use pat_param to preserve semantics", suggestion, Applicability::MachineApplicable); @@ -747,13 +748,13 @@ pub trait LintContext: Sized { } => { db.span_note( invoc_span, - &format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`") + format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`") ); } BuiltinLintDiagnostics::TrailingMacro(is_trailing, name) => { if is_trailing { db.note("macro invocations at the end of a block are treated as expressions"); - db.note(&format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`")); + db.note(format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`")); } } BuiltinLintDiagnostics::BreakWithLabelAndLoop(span) => { @@ -765,25 +766,55 @@ pub trait LintContext: Sized { ); } BuiltinLintDiagnostics::NamedAsmLabel(help) => { - db.help(&help); + db.help(help); db.note("see the asm section of Rust By Example for more information"); }, - BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), None) => { - let Some(names_valid) = &sess.parse_sess.check_config.names_valid else { - bug!("it shouldn't be possible to have a diagnostic on a name if name checking is not enabled"); - }; - let possibilities: Vec = names_valid.iter().map(|s| *s).collect(); + BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => { + let possibilities: Vec = sess.parse_sess.check_config.expecteds.keys().map(|s| *s).collect(); // Suggest the most probable if we found one if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { - db.span_suggestion(name_span, "did you mean", best_match, Applicability::MaybeIncorrect); + if let Some(ExpectedValues::Some(best_match_values)) = + sess.parse_sess.check_config.expecteds.get(&best_match) { + let mut possibilities = best_match_values.iter() + .flatten() + .map(Symbol::as_str) + .collect::>(); + possibilities.sort(); + + if let Some((value, value_span)) = value { + if best_match_values.contains(&Some(value)) { + db.span_suggestion(name_span, "there is a config with a similar name and value", best_match, Applicability::MaybeIncorrect); + } else if best_match_values.contains(&None) { + db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and no value", best_match, Applicability::MaybeIncorrect); + } else if let Some(first_value) = possibilities.first() { + db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", format!("{best_match} = \"{first_value}\""), Applicability::MaybeIncorrect); + } else { + db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", best_match, Applicability::MaybeIncorrect); + }; + } else { + db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect); + } + + if !possibilities.is_empty() { + let possibilities = possibilities.join("`, `"); + db.help(format!("expected values for `{best_match}` are: `{possibilities}`")); + } + } else { + db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect); + } } }, - BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), Some((value, value_span))) => { - let Some(values) = &sess.parse_sess.check_config.values_valid.get(&name) else { + BuiltinLintDiagnostics::UnexpectedCfgValue((name, name_span), value) => { + let Some(ExpectedValues::Some(values)) = &sess.parse_sess.check_config.expecteds.get(&name) else { bug!("it shouldn't be possible to have a diagnostic on a value whose name is not in values"); }; - let possibilities: Vec = values.iter().map(|&s| s).collect(); + let mut have_none_possibility = false; + let possibilities: Vec = values.iter() + .inspect(|a| have_none_possibility |= a.is_none()) + .copied() + .flatten() + .collect(); // Show the full list if all possible values for a given name, but don't do it // for names as the possibilities could be very long @@ -792,17 +823,24 @@ pub trait LintContext: Sized { let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::>(); possibilities.sort(); - let possibilities = possibilities.join(", "); - db.note(&format!("expected values for `{name}` are: {possibilities}")); + let possibilities = possibilities.join("`, `"); + let none = if have_none_possibility { "(none), " } else { "" }; + + db.note(format!("expected values for `{name}` are: {none}`{possibilities}`")); } - // Suggest the most probable if we found one - if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) { - db.span_suggestion(value_span, "did you mean", format!("\"{best_match}\""), Applicability::MaybeIncorrect); + if let Some((value, value_span)) = value { + // Suggest the most probable if we found one + if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) { + db.span_suggestion(value_span, "there is a expected value with a similar name", format!("\"{best_match}\""), Applicability::MaybeIncorrect); + + } + } else if let &[first_possibility] = &possibilities[..] { + db.span_suggestion(name_span.shrink_to_hi(), "specify a config value", format!(" = \"{first_possibility}\""), Applicability::MaybeIncorrect); } - } else { - db.note(&format!("no expected value for `{name}`")); - if name != sym::feature { + } else if have_none_possibility { + db.note(format!("no expected value for `{name}`")); + if let Some((_value, value_span)) = value { db.span_suggestion(name_span.shrink_to_hi().to(value_span), "remove the value", "", Applicability::MaybeIncorrect); } } diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs new file mode 100644 index 000000000..77e4a7669 --- /dev/null +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -0,0 +1,164 @@ +use rustc_hir::{Arm, Expr, ExprKind, Node}; +use rustc_span::sym; + +use crate::{ + lints::{DropCopyDiag, DropRefDiag, ForgetCopyDiag, ForgetRefDiag}, + LateContext, LateLintPass, LintContext, +}; + +declare_lint! { + /// The `dropping_references` lint checks for calls to `std::mem::drop` with a reference + /// instead of an owned value. + /// + /// ### Example + /// + /// ```rust + /// # fn operation_that_requires_mutex_to_be_unlocked() {} // just to make it compile + /// # let mutex = std::sync::Mutex::new(1); // just to make it compile + /// let mut lock_guard = mutex.lock(); + /// std::mem::drop(&lock_guard); // Should have been drop(lock_guard), mutex + /// // still locked + /// operation_that_requires_mutex_to_be_unlocked(); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Calling `drop` on a reference will only drop the + /// reference itself, which is a no-op. It will not call the `drop` method (from + /// the `Drop` trait implementation) on the underlying referenced value, which + /// is likely what was intended. + pub DROPPING_REFERENCES, + Warn, + "calls to `std::mem::drop` with a reference instead of an owned value" +} + +declare_lint! { + /// The `forgetting_references` lint checks for calls to `std::mem::forget` with a reference + /// instead of an owned value. + /// + /// ### Example + /// + /// ```rust + /// let x = Box::new(1); + /// std::mem::forget(&x); // Should have been forget(x), x will still be dropped + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Calling `forget` on a reference will only forget the + /// reference itself, which is a no-op. It will not forget the underlying + /// referenced value, which is likely what was intended. + pub FORGETTING_REFERENCES, + Warn, + "calls to `std::mem::forget` with a reference instead of an owned value" +} + +declare_lint! { + /// The `dropping_copy_types` lint checks for calls to `std::mem::drop` with a value + /// that derives the Copy trait. + /// + /// ### Example + /// + /// ```rust + /// let x: i32 = 42; // i32 implements Copy + /// std::mem::drop(x); // A copy of x is passed to the function, leaving the + /// // original unaffected + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Calling `std::mem::drop` [does nothing for types that + /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the + /// value will be copied and moved into the function on invocation. + pub DROPPING_COPY_TYPES, + Warn, + "calls to `std::mem::drop` with a value that implements Copy" +} + +declare_lint! { + /// The `forgetting_copy_types` lint checks for calls to `std::mem::forget` with a value + /// that derives the Copy trait. + /// + /// ### Example + /// + /// ```rust + /// let x: i32 = 42; // i32 implements Copy + /// std::mem::forget(x); // A copy of x is passed to the function, leaving the + /// // original unaffected + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Calling `std::mem::forget` [does nothing for types that + /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the + /// value will be copied and moved into the function on invocation. + /// + /// An alternative, but also valid, explanation is that Copy types do not + /// implement the Drop trait, which means they have no destructors. Without a + /// destructor, there is nothing for `std::mem::forget` to ignore. + pub FORGETTING_COPY_TYPES, + Warn, + "calls to `std::mem::forget` with a value that implements Copy" +} + +declare_lint_pass!(DropForgetUseless => [DROPPING_REFERENCES, FORGETTING_REFERENCES, DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES]); + +impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Call(path, [arg]) = expr.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id) + { + let arg_ty = cx.typeck_results().expr_ty(arg); + let is_copy = arg_ty.is_copy_modulo_regions(cx.tcx, cx.param_env); + let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); + match fn_name { + sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => { + cx.emit_spanned_lint(DROPPING_REFERENCES, expr.span, DropRefDiag { arg_ty, label: arg.span }); + }, + sym::mem_forget if arg_ty.is_ref() => { + cx.emit_spanned_lint(FORGETTING_REFERENCES, expr.span, ForgetRefDiag { arg_ty, label: arg.span }); + }, + sym::mem_drop if is_copy && !drop_is_single_call_in_arm => { + cx.emit_spanned_lint(DROPPING_COPY_TYPES, expr.span, DropCopyDiag { arg_ty, label: arg.span }); + } + sym::mem_forget if is_copy => { + cx.emit_spanned_lint(FORGETTING_COPY_TYPES, expr.span, ForgetCopyDiag { arg_ty, label: arg.span }); + } + _ => return, + }; + } + } +} + +// Dropping returned value of a function, as in the following snippet is considered idiomatic, see +// rust-lang/rust-clippy#9482 for examples. +// +// ``` +// match { +// => drop(fn_with_side_effect_and_returning_some_value()), +// .. +// } +// ``` +fn is_single_call_in_arm<'tcx>( + cx: &LateContext<'tcx>, + arg: &'tcx Expr<'_>, + drop_expr: &'tcx Expr<'_>, +) -> bool { + if arg.can_have_side_effects() { + let parent_node = cx.tcx.hir().find_parent(drop_expr.hir_id); + if let Some(Node::Arm(Arm { body, .. })) = &parent_node { + return body.hir_id == drop_expr.hir_id; + } + } + false +} diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 65607d718..9f1f5a26e 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -428,7 +428,7 @@ pub fn check_ast_node_inner<'a, T: EarlyLintPass>( for early_lint in lints { sess.delay_span_bug( early_lint.span, - &format!( + format!( "failed to process buffered lint here (dummy = {})", id == ast::DUMMY_NODE_ID ), diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs index f1ba192f2..2ce28f3a0 100644 --- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs +++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs @@ -42,7 +42,7 @@ declare_lint_pass!(EnumIntrinsicsNonEnums => [ENUM_INTRINSICS_NON_ENUMS]); /// Returns `true` if we know for sure that the given type is not an enum. Note that for cases where /// the type is generic, we can't be certain if it will be an enum so we have to assume that it is. fn is_non_enum(t: Ty<'_>) -> bool { - !t.is_enum() && !t.needs_subst() + !t.is_enum() && !t.has_param() } fn enforce_mem_discriminant( diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index 9af5284df..bbae3d368 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -81,7 +81,7 @@ pub struct UnknownToolInScopedLint { #[derive(Diagnostic)] #[diag(lint_builtin_ellipsis_inclusive_range_patterns, code = "E0783")] -pub struct BuiltinEllpisisInclusiveRangePatterns { +pub struct BuiltinEllipsisInclusiveRangePatterns { #[primary_span] pub span: Span, #[suggestion(style = "short", code = "{replace}", applicability = "machine-applicable")] diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index e9eb14ea1..b1266b58a 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -1,5 +1,5 @@ use crate::lints::{Expectation, ExpectationNote}; -use rustc_middle::ty::query::Providers; +use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS; use rustc_session::lint::LintExpectationId; diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 4ac589c2e..6f773e04a 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -4,6 +4,7 @@ use crate::lints::{ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword, QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag, + UntranslatableDiagnosticTrivial, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_ast as ast; @@ -366,7 +367,15 @@ declare_tool_lint! { report_in_external_macro: true } -declare_lint_pass!(Diagnostics => [ UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL ]); +declare_tool_lint! { + /// The `untranslatable_diagnostic_trivial` lint detects diagnostics created using only static strings. + pub rustc::UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL, + Deny, + "prevent creation of diagnostics which cannot be translated, which use only static strings", + report_in_external_macro: true +} + +declare_lint_pass!(Diagnostics => [ UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL, UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL ]); impl LateLintPass<'_> for Diagnostics { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -374,9 +383,8 @@ impl LateLintPass<'_> for Diagnostics { debug!(?span, ?def_id, ?substs); let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) .ok() - .and_then(|inst| inst) - .map(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics)) - .unwrap_or(false); + .flatten() + .is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics)); if !has_attr { return; } @@ -423,6 +431,77 @@ impl LateLintPass<'_> for Diagnostics { } } +impl EarlyLintPass for Diagnostics { + #[allow(unused_must_use)] + fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { + // Looking for a straight chain of method calls from 'struct_span_err' to 'emit'. + let ast::StmtKind::Semi(expr) = &stmt.kind else { + return; + }; + let ast::ExprKind::MethodCall(meth) = &expr.kind else { + return; + }; + if meth.seg.ident.name != sym::emit || !meth.args.is_empty() { + return; + } + let mut segments = vec![]; + let mut cur = &meth.receiver; + let fake = &[].into(); + loop { + match &cur.kind { + ast::ExprKind::Call(func, args) => { + if let ast::ExprKind::Path(_, path) = &func.kind { + segments.push((path.segments.last().unwrap().ident.name, args)) + } + break; + } + ast::ExprKind::MethodCall(method) => { + segments.push((method.seg.ident.name, &method.args)); + cur = &method.receiver; + } + ast::ExprKind::MacCall(mac) => { + segments.push((mac.path.segments.last().unwrap().ident.name, fake)); + break; + } + _ => { + break; + } + } + } + segments.reverse(); + if segments.is_empty() { + return; + } + if segments[0].0.as_str() != "struct_span_err" { + return; + } + if !segments.iter().all(|(name, args)| { + let arg = match name.as_str() { + "struct_span_err" | "span_note" | "span_label" | "span_help" if args.len() == 2 => { + &args[1] + } + "note" | "help" if args.len() == 1 => &args[0], + _ => { + return false; + } + }; + if let ast::ExprKind::Lit(lit) = arg.kind + && let ast::token::LitKind::Str = lit.kind { + true + } else { + false + } + }) { + return; + } + cx.emit_spanned_lint( + UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL, + stmt.span, + UntranslatableDiagnosticTrivial, + ); + } +} + declare_tool_lint! { /// The `bad_opt_access` lint detects accessing options by field instead of /// the wrapper function. diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index b42878a02..8a4a451f8 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -16,7 +16,7 @@ use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore}; use rustc_ast as ast; -use rustc_data_structures::sync::join; +use rustc_data_structures::sync::{join, DynSend}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit as hir_visit; @@ -240,8 +240,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) { - lint_callback!(self, check_arm, a); - hir_visit::walk_arm(self, a); + self.with_lint_attrs(a.hir_id, |cx| { + lint_callback!(cx, check_arm, a); + hir_visit::walk_arm(cx, a); + }) } fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { @@ -429,7 +431,7 @@ fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>( /// Performs lint checking on a crate. pub fn check_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>( tcx: TyCtxt<'tcx>, - builtin_lints: impl FnOnce() -> T + Send, + builtin_lints: impl FnOnce() -> T + Send + DynSend, ) { join( || { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index bb863f095..8376835f5 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -14,13 +14,13 @@ use rustc_errors::{DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirId; -use rustc_index::vec::IndexVec; +use rustc_index::IndexVec; use rustc_middle::hir::nested_filter; use rustc_middle::lint::{ reveal_actual_level, struct_lint_level, LevelAndSource, LintExpectation, LintLevelSource, ShallowLintLevelMap, }; -use rustc_middle::ty::query::Providers; +use rustc_middle::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES}; use rustc_session::lint::{ @@ -242,7 +242,9 @@ impl LintLevelsProvider for LintLevelQueryMap<'_> { struct QueryMapExpectationsWrapper<'tcx> { tcx: TyCtxt<'tcx>, + /// HirId of the currently investigated element. cur: HirId, + /// Level map for `cur`. specs: ShallowLintLevelMap, expectations: Vec<(LintExpectationId, LintExpectation)>, unstable_to_stable_ids: FxHashMap, @@ -255,11 +257,11 @@ impl LintLevelsProvider for QueryMapExpectationsWrapper<'_> { self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty) } fn insert(&mut self, id: LintId, lvl: LevelAndSource) { - let specs = self.specs.specs.get_mut_or_insert_default(self.cur.local_id); - specs.clear(); - specs.insert(id, lvl); + self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl); } fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource { + // We cannot use `tcx.lint_level_at_node` because we want to know in which order the + // attributes have been inserted, in particular whether an `expect` follows a `forbid`. self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur) } fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) { @@ -355,7 +357,9 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { impl<'tcx> LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> { fn add_id(&mut self, hir_id: HirId) { + // Change both the `HirId` and the associated specs. self.provider.cur = hir_id; + self.provider.specs.specs.clear(); self.add(self.provider.tcx.hir().attrs(hir_id), hir_id == hir::CRATE_HIR_ID, Some(hir_id)); } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index b35785405..dfddfe09a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -52,6 +52,7 @@ mod array_into_iter; pub mod builtin; mod context; mod deref_into_dyn_supertrait; +mod drop_forget_useless; mod early; mod enum_intrinsics_non_enums; mod errors; @@ -82,10 +83,10 @@ pub use array_into_iter::ARRAY_INTO_ITER; use rustc_ast as ast; use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; +use rustc_fluent_macro::fluent_messages; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_macros::fluent_messages; -use rustc_middle::ty::query::Providers; +use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{ BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS, @@ -96,6 +97,7 @@ use rustc_span::Span; use array_into_iter::ArrayIntoIter; use builtin::*; use deref_into_dyn_supertrait::*; +use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; use hidden_unicode_codepoints::*; @@ -201,6 +203,7 @@ late_lint_methods!( [ ForLoopsOverFallibles: ForLoopsOverFallibles, DerefIntoDynSupertrait: DerefIntoDynSupertrait, + DropForgetUseless: DropForgetUseless, HardwiredLints: HardwiredLints, ImproperCTypesDeclarations: ImproperCTypesDeclarations, ImproperCTypesDefinitions: ImproperCTypesDefinitions, @@ -518,6 +521,7 @@ fn register_internals(store: &mut LintStore) { store.register_lints(&TyTyKind::get_lints()); store.register_late_pass(|_| Box::new(TyTyKind)); store.register_lints(&Diagnostics::get_lints()); + store.register_early_pass(|| Box::new(Diagnostics)); store.register_late_pass(|_| Box::new(Diagnostics)); store.register_lints(&BadOptAccess::get_lints()); store.register_late_pass(|_| Box::new(BadOptAccess)); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1d5e02369..d96723a68 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -662,6 +662,43 @@ pub struct ForLoopsOverFalliblesSuggestion<'a> { pub end_span: Span, } +// drop_forget_useless.rs +#[derive(LintDiagnostic)] +#[diag(lint_dropping_references)] +#[note] +pub struct DropRefDiag<'a> { + pub arg_ty: Ty<'a>, + #[label] + pub label: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_dropping_copy_types)] +#[note] +pub struct DropCopyDiag<'a> { + pub arg_ty: Ty<'a>, + #[label] + pub label: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_forgetting_references)] +#[note] +pub struct ForgetRefDiag<'a> { + pub arg_ty: Ty<'a>, + #[label] + pub label: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_forgetting_copy_types)] +#[note] +pub struct ForgetCopyDiag<'a> { + pub arg_ty: Ty<'a>, + #[label] + pub label: Span, +} + // hidden_unicode_codepoints.rs #[derive(LintDiagnostic)] #[diag(lint_hidden_unicode_codepoints)] @@ -820,6 +857,10 @@ pub struct DiagOutOfImpl; #[diag(lint_untranslatable_diag)] pub struct UntranslatableDiag; +#[derive(LintDiagnostic)] +#[diag(lint_trivial_untranslatable_diag)] +pub struct UntranslatableDiagnosticTrivial; + #[derive(LintDiagnostic)] #[diag(lint_bad_opt_access)] pub struct BadOptAccessDiag<'a> { @@ -1146,6 +1187,18 @@ pub struct NoopMethodCallDiag<'a> { pub label: Span, } +#[derive(LintDiagnostic)] +#[diag(lint_suspicious_double_ref_deref)] +pub struct SuspiciousDoubleRefDerefDiag<'a> { + pub ty: Ty<'a>, +} + +#[derive(LintDiagnostic)] +#[diag(lint_suspicious_double_ref_clone)] +pub struct SuspiciousDoubleRefCloneDiag<'a> { + pub ty: Ty<'a>, +} + // pass_by_value.rs #[derive(LintDiagnostic)] #[diag(lint_pass_by_value)] diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 5bb1abfd2..b218cc578 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -128,7 +128,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc // No clue where this argument is coming from. return lint; } - if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { + if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { // A case of `panic!(format!(..))`. lint.note(fluent::lint_supports_fmt_note); if let Some((open, close, _)) = find_delimiters(cx, arg_span) { diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 9efc14849..79253cbc8 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -33,6 +33,11 @@ pub fn method_context(cx: &LateContext<'_>, id: LocalDefId) -> MethodLateContext } } +fn assoc_item_in_trait_impl(cx: &LateContext<'_>, ii: &hir::ImplItem<'_>) -> bool { + let item = cx.tcx.associated_item(ii.owner_id); + item.trait_item_def_id.is_some() +} + declare_lint! { /// The `non_camel_case_types` lint detects types, variants, traits and /// type parameters that don't have camel case names. @@ -177,6 +182,7 @@ impl EarlyLintPass for NonCamelCaseTypes { // trait impls where we should have warned for the trait definition already. ast::ItemKind::Impl(box ast::Impl { of_trait: None, items, .. }) => { for it in items { + // FIXME: this doesn't respect `#[allow(..)]` on the item itself. if let ast::AssocItemKind::Type(..) = it.kind { self.check_case(cx, "associated type", &it.ident); } @@ -505,7 +511,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { } fn check_impl_item(&mut self, cx: &LateContext<'_>, ii: &hir::ImplItem<'_>) { - if let hir::ImplItemKind::Const(..) = ii.kind { + if let hir::ImplItemKind::Const(..) = ii.kind && !assoc_item_in_trait_impl(cx, ii) { NonUpperCaseGlobals::check_upper_case(cx, "associated constant", &ii.ident); } } diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index d67a00619..d56c35bb6 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -1,10 +1,13 @@ use crate::context::LintContext; -use crate::lints::NoopMethodCallDiag; +use crate::lints::{ + NoopMethodCallDiag, SuspiciousDoubleRefCloneDiag, SuspiciousDoubleRefDerefDiag, +}; use crate::LateContext; use crate::LateLintPass; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind}; use rustc_middle::ty; +use rustc_middle::ty::adjustment::Adjust; use rustc_span::symbol::sym; declare_lint! { @@ -35,32 +38,62 @@ declare_lint! { "detects the use of well-known noop methods" } -declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]); +declare_lint! { + /// The `suspicious_double_ref_op` lint checks for usage of `.clone()`/`.borrow()`/`.deref()` + /// on an `&&T` when `T: !Deref/Borrow/Clone`, which means the call will return the inner `&T`, + /// instead of performing the operation on the underlying `T` and can be confusing. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// struct Foo; + /// let foo = &&Foo; + /// let clone: &Foo = foo.clone(); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Since `Foo` doesn't implement `Clone`, running `.clone()` only dereferences the double + /// reference, instead of cloning the inner type which should be what was intended. + pub SUSPICIOUS_DOUBLE_REF_OP, + Warn, + "suspicious call of trait method on `&&T`" +} + +declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL, SUSPICIOUS_DOUBLE_REF_OP]); impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // We only care about method calls. - let ExprKind::MethodCall(call, receiver, ..) = &expr.kind else { - return + let ExprKind::MethodCall(call, receiver, _, call_span) = &expr.kind else { + return; }; + + if call_span.from_expansion() { + return; + } + // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow` // traits and ignore any other method call. - let did = match cx.typeck_results().type_dependent_def(expr.hir_id) { - // Verify we are dealing with a method/associated function. - Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) { - // Check that we're dealing with a trait method for one of the traits we care about. - Some(trait_id) - if matches!( - cx.tcx.get_diagnostic_name(trait_id), - Some(sym::Borrow | sym::Clone | sym::Deref) - ) => - { - did - } - _ => return, - }, - _ => return, + + let Some((DefKind::AssocFn, did)) = + cx.typeck_results().type_dependent_def(expr.hir_id) + else { + return; }; + + let Some(trait_id) = cx.tcx.trait_of_item(did) else { return }; + + if !matches!( + cx.tcx.get_diagnostic_name(trait_id), + Some(sym::Borrow | sym::Clone | sym::Deref) + ) { + return; + }; + let substs = cx .tcx .normalize_erasing_regions(cx.param_env, cx.typeck_results().node_substs(expr.hir_id)); @@ -70,25 +103,43 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { }; // (Re)check that it implements the noop diagnostic. let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return }; - if !matches!( - name, - sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref - ) { - return; - } + let receiver_ty = cx.typeck_results().expr_ty(receiver); let expr_ty = cx.typeck_results().expr_ty_adjusted(expr); - if receiver_ty != expr_ty { - // This lint will only trigger if the receiver type and resulting expression \ - // type are the same, implying that the method call is unnecessary. + let arg_adjustments = cx.typeck_results().expr_adjustments(receiver); + + // If there is any user defined auto-deref step, then we don't want to warn. + // https://github.com/rust-lang/rust-clippy/issues/9272 + if arg_adjustments.iter().any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) { return; } + let expr_span = expr.span; let span = expr_span.with_lo(receiver.span.hi()); - cx.emit_spanned_lint( - NOOP_METHOD_CALL, - span, - NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span }, - ); + + if receiver_ty == expr_ty { + cx.emit_spanned_lint( + NOOP_METHOD_CALL, + span, + NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span }, + ); + } else { + match name { + // If `type_of(x) == T` and `x.borrow()` is used to get `&T`, + // then that should be allowed + sym::noop_method_borrow => return, + sym::noop_method_clone => cx.emit_spanned_lint( + SUSPICIOUS_DOUBLE_REF_OP, + span, + SuspiciousDoubleRefCloneDiag { ty: expr_ty }, + ), + sym::noop_method_deref => cx.emit_spanned_lint( + SUSPICIOUS_DOUBLE_REF_OP, + span, + SuspiciousDoubleRefDerefDiag { ty: expr_ty }, + ), + _ => return, + } + } } } diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index f9d43fe22..15715c8fc 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // For every projection predicate in the opaque type's explicit bounds, // check that the type that we're assigning actually satisfies the bounds // of the associated type. - for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) { + for (pred, pred_span) in cx.tcx.explicit_item_bounds(def_id).subst_identity_iter_copied() { // Liberate bound regions in the predicate since we // don't actually care about lifetimes in this check. let predicate = cx.tcx.liberate_late_bound_regions(def_id, pred.kind()); @@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // with `impl Send: OtherTrait`. for (assoc_pred, assoc_pred_span) in cx .tcx - .bound_explicit_item_bounds(proj.projection_ty.def_id) + .explicit_item_bounds(proj.projection_ty.def_id) .subst_iter_copied(cx.tcx, &proj.projection_ty.substs) { let assoc_pred = assoc_pred.fold_with(proj_replacer); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index a6ba74220..4bf4fda82 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -677,7 +677,7 @@ pub fn transparent_newtype_field<'a, 'tcx>( let param_env = tcx.param_env(variant.def_id); variant.fields.iter().find(|field| { let field_ty = tcx.type_of(field.did).subst_identity(); - let is_zst = tcx.layout_of(param_env.and(field_ty)).map_or(false, |layout| layout.is_zst()); + let is_zst = tcx.layout_of(param_env.and(field_ty)).is_ok_and(|layout| layout.is_zst()); !is_zst }) } @@ -1119,14 +1119,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, // so they are currently ignored for the purposes of this lint. - ty::Param(..) | ty::Alias(ty::Projection, ..) + ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..) if matches!(self.mode, CItemKind::Definition) => { FfiSafe } ty::Param(..) - | ty::Alias(ty::Projection, ..) + | ty::Alias(ty::Projection | ty::Inherent, ..) | ty::Infer(..) | ty::Bound(..) | ty::Error(_) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 28cc63198..8f75fa11d 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -256,7 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { - elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).iter().cloned()) + elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).subst_identity_iter_copied()) // We only care about self bounds for the impl-trait .filter_only_self() .find_map(|(pred, _span)| { @@ -571,36 +571,50 @@ trait UnusedDelimLint { } } - // Prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }` - let lhs_needs_parens = { + // Check if LHS needs parens to prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`. + { let mut innermost = inner; loop { innermost = match &innermost.kind { - ExprKind::Binary(_, lhs, _rhs) => lhs, + ExprKind::Binary(_op, lhs, _rhs) => lhs, ExprKind::Call(fn_, _params) => fn_, ExprKind::Cast(expr, _ty) => expr, ExprKind::Type(expr, _ty) => expr, ExprKind::Index(base, _subscript) => base, - _ => break false, + _ => break, }; if !classify::expr_requires_semi_to_be_stmt(innermost) { - break true; + return true; } } - }; + } - lhs_needs_parens - || (followed_by_block - && match &inner.kind { - ExprKind::Ret(_) - | ExprKind::Break(..) - | ExprKind::Yield(..) - | ExprKind::Yeet(..) => true, - ExprKind::Range(_lhs, Some(rhs), _limits) => { - matches!(rhs.kind, ExprKind::Block(..)) - } - _ => parser::contains_exterior_struct_lit(&inner), - }) + // Check if RHS needs parens to prevent false-positives in cases like `if (() == return) {}`. + if !followed_by_block { + return false; + } + let mut innermost = inner; + loop { + innermost = match &innermost.kind { + ExprKind::Unary(_op, expr) => expr, + ExprKind::Binary(_op, _lhs, rhs) => rhs, + ExprKind::AssignOp(_op, _lhs, rhs) => rhs, + ExprKind::Assign(_lhs, rhs, _span) => rhs, + + ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true, + + ExprKind::Break(_label, None) => return false, + ExprKind::Break(_label, Some(break_expr)) => { + return matches!(break_expr.kind, ExprKind::Block(..)); + } + + ExprKind::Range(_lhs, Some(rhs), _limits) => { + return matches!(rhs.kind, ExprKind::Block(..)); + } + + _ => return parser::contains_exterior_struct_lit(&inner), + } + } } fn emit_unused_delims_expr( @@ -638,26 +652,20 @@ trait UnusedDelimLint { return; } let spans = match value.kind { - ast::ExprKind::Block(ref block, None) if block.stmts.len() == 1 => { - if let Some(span) = block.stmts[0].span.find_ancestor_inside(value.span) { - Some((value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))) - } else { - None - } - } + ast::ExprKind::Block(ref block, None) if block.stmts.len() == 1 => block.stmts[0] + .span + .find_ancestor_inside(value.span) + .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))), ast::ExprKind::Paren(ref expr) => { - let expr_span = expr.span.find_ancestor_inside(value.span); - if let Some(expr_span) = expr_span { - Some((value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi()))) - } else { - None - } + expr.span.find_ancestor_inside(value.span).map(|expr_span| { + (value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi())) + }) } _ => return, }; let keep_space = ( - left_pos.map_or(false, |s| s >= value.span.lo()), - right_pos.map_or(false, |s| s <= value.span.hi()), + left_pos.is_some_and(|s| s >= value.span.lo()), + right_pos.is_some_and(|s| s <= value.span.hi()), ); self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space); } @@ -930,11 +938,10 @@ impl UnusedParens { // Otherwise proceed with linting. _ => {} } - let spans = if let Some(inner) = inner.span.find_ancestor_inside(value.span) { - Some((value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi()))) - } else { - None - }; + let spans = inner + .span + .find_ancestor_inside(value.span) + .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi()))); self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space); } } @@ -1045,11 +1052,11 @@ impl EarlyLintPass for UnusedParens { if self.with_self_ty_parens && b.generic_params.len() > 0 => {} ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} _ => { - let spans = if let Some(r) = r.span.find_ancestor_inside(ty.span) { - Some((ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi()))) - } else { - None - }; + let spans = r + .span + .find_ancestor_inside(ty.span) + .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi()))); + self.emit_unused_delims(cx, ty.span, spans, "type", (false, false)); } } -- cgit v1.2.3